USING: kernel sequences quotations assocs math math.parser
       combinators.lib vars lsys.strings combinators.short-circuit ;

IN: lsys.strings.interpret

! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

VAR: command-table

! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

: exec-command ( string -- ) command-table> at >quotation call ;

! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

: command ( string -- command ) 1 head ;

: parameter ( string -- parameter )
  [ drop 2 ] [ length 1- ] [ ] tri subseq string>number ;

: exec-command* ( string -- )
  [ parameter ] [ command ] bi
  command-table> at dup
  [ 1 tail* call ] [ 3drop ] if ;

! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

: (interpret) ( slice -- )
  { { [ empty? ]     [ drop ] }
    { [ has-param? ] [ next+rest* [ exec-command* ] [ (interpret) ] bi* ] }
    { [ t ]          [ next+rest  [ exec-command  ] [ (interpret) ] bi* ] } }
  switch ;

: interpret ( string -- ) <flat-slice> (interpret) ;

Fix stack underflow in lsys

USING: kernel sequences quotations assocs math math.parser
       combinators.lib vars lsys.strings combinators.short-circuit ;

IN: lsys.strings.interpret

! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

VAR: command-table

! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

: exec-command ( string -- ) command-table> at >quotation call ;

! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

: command ( string -- command ) 1 head ;

: parameter ( string -- parameter )
  [ drop 2 ] [ length 1- ] [ ] tri subseq string>number ;

: exec-command* ( string -- )
  [ parameter ] [ command ] bi
  command-table> at dup
  [ 1 tail* call ] [ 2drop ] if ;

! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

: (interpret) ( slice -- )
  { { [ empty? ]     [ drop ] }
    { [ has-param? ] [ next+rest* [ exec-command* ] [ (interpret) ] bi* ] }
    { [ t ]          [ next+rest  [ exec-command  ] [ (interpret) ] bi* ] } }
  switch ;

: interpret ( string -- ) <flat-slice> (interpret) ;


! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: parser kernel words generic namespaces ;
IN: generic.parser

ERROR: not-in-a-method-error ;

: CREATE-GENERIC ( -- word ) CREATE dup reset-word ;

: create-method-in ( class generic -- method )
    create-method dup set-word dup save-location ;

: CREATE-METHOD ( -- method )
    scan-word bootstrap-word scan-word create-method-in ;

SYMBOL: current-method

: with-method-definition ( method quot -- )
    [ dup current-method ] dip with-variable ; inline

: (M:) ( method def -- )
    CREATE-METHOD [ parse-definition ] with-method-definition ;

Fix corner case where auto-use didn't print using list

! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: parser kernel words generic namespaces ;
IN: generic.parser

ERROR: not-in-a-method-error ;

: CREATE-GENERIC ( -- word ) CREATE dup reset-word ;

: create-method-in ( class generic -- method )
    create-method dup set-word dup save-location ;

: CREATE-METHOD ( -- method )
    scan-word bootstrap-word scan-word create-method-in ;

SYMBOL: current-method

: with-method-definition ( method quot -- )
    over current-method set call current-method off ; inline

: (M:) ( method def -- )
    CREATE-METHOD [ parse-definition ] with-method-definition ;


IN: ui.tests
USING: ui tools.test ;

\ event-loop must-infer

Add unit test now that open-window infers

IN: ui.tests
USING: ui tools.test ;

\ event-loop must-infer
\ open-window must-infer


USING: alarms alarms.private kernel calendar sequences
tools.test threads concurrency.count-downs concurrency.promises
fry tools.time math ;
IN: alarms.tests

[ ] [
    1 <count-down>
    { f } clone 2dup
    [ first cancel-alarm count-down ] 2curry 1 seconds later
    swap set-first
    await
] unit-test

[ ] [
    self [ resume ] curry instant later drop
    "test" suspend drop
] unit-test

[ t ] [
    [
        <promise>
        '[ t _ fulfill ] 5 seconds later drop
    ] benchmark 4,000,000,000 >
] unit-test

[ { 3 } ] [
    { 3 } dup
    '[ 4 _ set-first ] 2 seconds later
    1/2 seconds sleep
    cancel-alarm
] unit-test

Make the alarm test wait for the promise to be fulfilled instead of returning immediately and failing

USING: alarms alarms.private calendar concurrency.count-downs
concurrency.promises fry kernel math math.order sequences
threads tools.test tools.time ;
IN: alarms.tests

[ ] [
    1 <count-down>
    { f } clone 2dup
    [ first stop-alarm count-down ] 2curry 1 seconds later
    swap set-first
    await
] unit-test

[ ] [
    self [ resume ] curry instant later drop
    "test" suspend drop
] unit-test

[ t ] [
    [
        <promise>
        [ '[ t _ fulfill ] 2 seconds later drop ]
        [ 5 seconds ?promise-timeout drop ] bi
    ] benchmark 1,500,000,000 2,500,000,000 between?
] unit-test

[ { 3 } ] [
    { 3 } dup
    '[ 4 _ set-first ] 2 seconds later
    1/2 seconds sleep
    stop-alarm
] unit-test


USING: io lint kernel math tools.test ;
IN: lint.tests

! Don't write code like this
: lint1 ( obj -- ) [ "hi" print ] [ ] if ; ! when

[ { { lint1 { [ [ ] if ] } } } ] [ \ lint1 lint-word ] unit-test

: lint2 ( n -- n' ) 1 + ; ! 1+
[ { [ 1 + ] } ] [ \ lint2 lint ] unit-test

: lint3 ( a b -- b a b ) dup -rot ; ! tuck

[ { { lint3 { [ dup -rot ] } } } ] [ \ lint3 lint-word ] unit-test

Remove bogus test in lint

USING: io lint kernel math tools.test ;
IN: lint.tests

! Don't write code like this
: lint1 ( obj -- ) [ "hi" print ] [ ] if ; ! when

[ { { lint1 { [ [ ] if ] } } } ] [ \ lint1 lint-word ] unit-test

: lint3 ( a b -- b a b ) dup -rot ; ! tuck

[ { { lint3 { [ dup -rot ] } } } ] [ \ lint3 lint-word ] unit-test


USING: kernel math tools.test namespaces random
random.blum-blum-shub ;
IN: blum-blum-shub.tests

[ 887708070 ] [
    T{ blum-blum-shub f 590695557939 811977232793 } random-32*
] unit-test

[ 887708070 ] [
    T{ blum-blum-shub f 590695557939 811977232793 } [
        32 random-bits
    ] with-random
] unit-test

[ 5726770047455156646 ] [
    T{ blum-blum-shub f 590695557939 811977232793 } [
        64 random-bits
    ] with-random
] unit-test

[ 3716213681 ]
[
    100 T{ blum-blum-shub f 200352954495 846054538649 } tuck [
        random-32* drop
    ] curry times
    random-32*
] unit-test

Fix blum blum shub unit tests on big endian

USING: kernel math tools.test namespaces random
random.blum-blum-shub alien.c-types sequences splitting ;
IN: blum-blum-shub.tests

[ 887708070 ] [
    T{ blum-blum-shub f 590695557939 811977232793 } clone random-32*
] unit-test

[ 887708070 ] [
    T{ blum-blum-shub f 590695557939 811977232793 } clone [
        32 random-bits
        little-endian? [ <uint> reverse *uint ] unless
    ] with-random
] unit-test

[ 5726770047455156646 ] [
    T{ blum-blum-shub f 590695557939 811977232793 } clone [
        64 random-bits
        little-endian? [ <ulonglong> 4 group [ reverse ] map concat *ulonglong ] unless
    ] with-random
] unit-test

[ 3716213681 ]
[
    100 T{ blum-blum-shub f 200352954495 846054538649 } clone tuck [
        random-32* drop
    ] curry times
    random-32*
] unit-test


! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: accessors kernel sequences sets arrays
compiler.cfg.linear-scan.live-intervals
compiler.cfg.linear-scan.allocation ;
IN: compiler.cfg.linear-scan.debugger

: check-assigned ( live-intervals -- )
    [
        reg>>
        [ "Not all intervals have registers" throw ] unless
    ] each ;

: split-children ( live-interval -- seq )
    dup split-before>> [
        [ split-before>> ] [ split-after>> ] bi
        [ split-children ] bi@
        append
    ] [ 1array ] if ;

: check-linear-scan ( live-intervals machine-registers -- )
    [ [ clone ] map ] dip allocate-registers
    [ split-children ] map concat check-assigned ;

Add tool to dump live intervals

! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: accessors kernel sequences sets arrays math strings fry
prettyprint compiler.cfg.linear-scan.live-intervals
compiler.cfg.linear-scan.allocation ;
IN: compiler.cfg.linear-scan.debugger

: check-assigned ( live-intervals -- )
    [
        reg>>
        [ "Not all intervals have registers" throw ] unless
    ] each ;

: split-children ( live-interval -- seq )
    dup split-before>> [
        [ split-before>> ] [ split-after>> ] bi
        [ split-children ] bi@
        append
    ] [ 1array ] if ;

: check-linear-scan ( live-intervals machine-registers -- )
    [ [ clone ] map ] dip allocate-registers
    [ split-children ] map concat check-assigned ;

: picture ( uses -- str )
    dup peek 1 + CHAR: space <string>
    [ '[ CHAR: * swap _ set-nth ] each ] keep ;

: interval-picture ( interval -- str )
    [ uses>> picture ]
    [ copy-from>> unparse ]
    [ vreg>> unparse ]
    tri 3array ;

: live-intervals. ( seq -- )
    [ interval-picture ] map simple-table. ;


USING: accessors fry ftp.server io.backend io.encodings.ascii
io.files io.pathnames io.servers kernel tools.test urls ;
FROM: ftp.client => ftp-get ;
IN: ftp.server.tests

CONSTANT: test-file-contents "Files are so boring anymore."

: create-test-file ( -- path )
    test-file-contents "ftp.server" normalize-path
    [ ascii set-file-contents ] keep ;

: test-ftp-server ( quot: ( server path -- ) -- )
    '[
        "." 0 <ftp-server> [
            "ftp://localhost" >url insecure-addr set-url-addr
                "ftp" >>protocol
                create-test-file >>path
                @
        ] with-threaded-server
    ] with-test-directory ; inline

{ t } [
    [
        [ ftp-get ]
        [ path>> file-name ascii file-contents ] bi
    ] test-ftp-server test-file-contents =
] unit-test

[

    [
        "/" >>path
        [ ftp-get ]
        [ path>> file-name ascii file-contents ] bi
    ] test-ftp-server test-file-contents =
] must-fail

Revert "ftp.server: make sure the test file returns a normalized path."

USING: accessors fry ftp.server io.encodings.ascii io.files
io.pathnames io.servers kernel tools.test urls ;
FROM: ftp.client => ftp-get ;
IN: ftp.server.tests

CONSTANT: test-file-contents "Files are so boring anymore."

: create-test-file ( -- path )
    test-file-contents "ftp.server" [ ascii set-file-contents ] keep ;

: test-ftp-server ( quot: ( server path -- ) -- )
    '[
        "." 0 <ftp-server> [
            "ftp://localhost" >url insecure-addr set-url-addr
                "ftp" >>protocol
                create-test-file >>path
                @
        ] with-threaded-server
    ] with-test-directory ; inline

{ t } [
    [
        [ ftp-get ]
        [ path>> file-name ascii file-contents ] bi
    ] test-ftp-server test-file-contents =
] unit-test

[

    [
        "/" >>path
        [ ftp-get ]
        [ path>> file-name ascii file-contents ] bi
    ] test-ftp-server test-file-contents =
] must-fail


! Copyright (C) 2009 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license.
USING: constructors kernel splitting unicode.case combinators
accessors images.bitmap images.tiff images io.pathnames
images.jpeg images.png ;
IN: images.loader

ERROR: unknown-image-extension extension ;

: image-class ( path -- class )
    file-extension >lower {
        { "bmp" [ bitmap-image ] }
        { "tif" [ tiff-image ] }
        { "tiff" [ tiff-image ] }
        { "jpg" [ jpeg-image ] }
        { "jpeg" [ jpeg-image ] }
        { "png" [ png-image ] }
        [ unknown-image-extension ]
    } case ;

: load-image ( path -- image )
    dup image-class new load-image* ;

Comment out JPEG code until we figure out if we want default bootstrap to depend on BLAS or not

! Copyright (C) 2009 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license.
USING: constructors kernel splitting unicode.case combinators
accessors images.bitmap images.tiff images io.pathnames
images.png ;
IN: images.loader

ERROR: unknown-image-extension extension ;

: image-class ( path -- class )
    file-extension >lower {
        { "bmp" [ bitmap-image ] }
        { "tif" [ tiff-image ] }
        { "tiff" [ tiff-image ] }
        ! { "jpg" [ jpeg-image ] }
        ! { "jpeg" [ jpeg-image ] }
        { "png" [ png-image ] }
        [ unknown-image-extension ]
    } case ;

: load-image ( path -- image )
    dup image-class new load-image* ;


USING: kernel alien combinators alien.syntax
       alien.c-types
       alien.libraries ;
IN: tokyo.alient.tcutil

C-ENUM:
    TCDBTHASH
    TCDBTBTREE
    TCDBTFIXED
    TCDBTTABLE ;

! long seems safe enough
TYPEDEF: long time_t

TYPEDEF: void* TCLIST*

FUNCTION: TCLIST* tclistnew ( ) ;
FUNCTION: TCLIST* tclistnew2 ( int anum ) ;
FUNCTION: void tclistdel ( TCLIST* list ) ;
FUNCTION: int tclistnum ( TCLIST* list ) ;
FUNCTION: void* tclistval ( TCLIST* list, int index, int* sp ) ;
FUNCTION: char* tclistval2 ( TCLIST* list, int index ) ;
FUNCTION: void tclistpush ( TCLIST* list, void* ptr, int size ) ;
FUNCTION: void tclistpush2 ( TCLIST* list, char* str ) ;

Make time_t a long, needs fix for 64bits windows

USING: kernel alien combinators alien.syntax
       alien.c-types
       alien.libraries ;
IN: tokyo.alient.tcutil

C-ENUM:
    TCDBTHASH
    TCDBTBTREE
    TCDBTFIXED
    TCDBTTABLE ;

! FIXME: on windows 64bits this isn't correct, because long is 32bits there, and time_t is int64
TYPEDEF: long time_t

TYPEDEF: void* TCLIST*

FUNCTION: TCLIST* tclistnew ( ) ;
FUNCTION: TCLIST* tclistnew2 ( int anum ) ;
FUNCTION: void tclistdel ( TCLIST* list ) ;
FUNCTION: int tclistnum ( TCLIST* list ) ;
FUNCTION: void* tclistval ( TCLIST* list, int index, int* sp ) ;
FUNCTION: char* tclistval2 ( TCLIST* list, int index ) ;
FUNCTION: void tclistpush ( TCLIST* list, void* ptr, int size ) ;
FUNCTION: void tclistpush2 ( TCLIST* list, char* str ) ;


! Copyright (C) 2008 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license.
USING: calendar kernel http.server.dispatchers prettyprint
sequences printf furnace.actions html.forms accessors
furnace.redirection ;
IN: webapps.irc-log

TUPLE: irclog-app < dispatcher ;

: irc-link ( -- string )
    gmt -7 hours convert-timezone >date<
    [ unparse 2 tail ] 2dip
    "http://bespin.org/~nef/logs/concatenative/%02s.%02d.%02d"
    sprintf ;

: <display-irclog-action> ( -- action )
    <action>
        [ irc-link <redirect> ] >>display ;

: <irclog-app> ( -- dispatcher )
    irclog-app new-dispatcher
        <display-irclog-action> "" add-responder ;

Update use of old printf vocabulary.

! Copyright (C) 2008 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license.
USING: calendar kernel http.server.dispatchers prettyprint
sequences formatting furnace.actions html.forms accessors
furnace.redirection ;
IN: webapps.irc-log

TUPLE: irclog-app < dispatcher ;

: irc-link ( -- string )
    gmt -7 hours convert-timezone >date<
    [ unparse 2 tail ] 2dip
    "http://bespin.org/~nef/logs/concatenative/%02s.%02d.%02d"
    sprintf ;

: <display-irclog-action> ( -- action )
    <action>
        [ irc-link <redirect> ] >>display ;

: <irclog-app> ( -- dispatcher )
    irclog-app new-dispatcher
        <display-irclog-action> "" add-responder ;


USING: kernel io.backend io.monitors io.monitors.recursive
io.files io.pathnames io.buffers io.ports io.timeouts
io.backend.unix io.encodings.utf8 assocs namespaces make
sequences locals system destructors accessors o;
IN: io.monitors.freebsd

TUPLE: freebsd-monitor < monitor handle ;

M:: freebsd (monitor) ( path recursive? mailbox -- monitor )
    path normalize-path :> path
    path mailbox freebsd-monitor new-monitor ;

M: freebsd-monitor dispose*
    [ handle>> dispose ] [ call-next-method ] bi ;

freebsd set-io-backend

Fix typo in monitor file

USING: kernel io.backend io.monitors io.monitors.recursive
io.files io.pathnames io.buffers io.ports io.timeouts
io.backend.unix io.encodings.utf8 assocs namespaces make
sequences locals system destructors accessors ;
IN: io.monitors.freebsd

TUPLE: freebsd-monitor < monitor handle ;

M:: freebsd (monitor) ( path recursive? mailbox -- monitor )
    path normalize-path :> path
    path mailbox freebsd-monitor new-monitor ;

M: freebsd-monitor dispose*
    [ handle>> dispose ] [ call-next-method ] bi ;

freebsd set-io-backend


USING: io.backend io.windows io.windows.ce.backend
io.windows.ce.files io.windows.ce.sockets io.windows.ce.launcher
namespaces ;
IN: io.windows.ce

T{ windows-ce-io } io-backend set-global

Load mmap on CE too

USING: io.backend io.windows io.windows.ce.backend
io.windows.ce.files io.windows.ce.sockets io.windows.ce.launcher
namespaces io.windows.mmap ;
IN: io.windows.ce

T{ windows-ce-io } io-backend set-global


! Copyright (C) 2007 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.

USING: parser kernel sequences words effects inference.transforms
       combinators assocs definitions quotations namespaces ;

IN: macros

: (:)
    CREATE dup reset-generic parse-definition
    over "declared-effect" word-prop effect-in length ;

: (MACRO:)
    >r
    2dup "macro" set-word-prop
    2dup [ call ] append define-compound
    r> define-transform ;

: MACRO:
    (:) (MACRO:) ; parsing

PREDICATE: word macro
    "macro" word-prop >boolean ;

M: macro definer drop \ MACRO: \ ; ;

M: macro definition "macro" word-prop ;

: macro-expand ( ... word -- quot ) "macro" word-prop call ;

: n*quot ( n seq -- seq' ) <repetition> concat >quotation ;

: saver \ >r <repetition> >quotation ;

: restorer \ r> <repetition> >quotation ;

Fix definition of macro predicate class

! Copyright (C) 2007 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.

USING: parser kernel sequences words effects inference.transforms
       combinators assocs definitions quotations namespaces ;

IN: macros

: (:)
    CREATE dup reset-generic parse-definition
    over "declared-effect" word-prop effect-in length ;

: (MACRO:)
    >r
    2dup "macro" set-word-prop
    2dup [ call ] append define-compound
    r> define-transform ;

: MACRO:
    (:) (MACRO:) ; parsing

PREDICATE: compound macro
    "macro" word-prop >boolean ;

M: macro definer drop \ MACRO: \ ; ;

M: macro definition "macro" word-prop ;

: macro-expand ( ... word -- quot ) "macro" word-prop call ;

: n*quot ( n seq -- seq' ) <repetition> concat >quotation ;

: saver \ >r <repetition> >quotation ;

: restorer \ r> <repetition> >quotation ;


USING: generic kernel kernel.private math memory prettyprint io
sequences tools.test words namespaces layouts classes
classes.builtin arrays quotations ;
IN: memory.tests

[ [ ] instances ] must-infer

! Code GC wasn't kicking in when needed
: leak-step 800000 f <array> 1quotation call drop ;

: leak-loop 100 [ leak-step ] times ;

[ ] [ leak-loop ] unit-test

TUPLE: testing x y z ;

[ save-image-and-exit ] must-fail

[ ] [
    num-types get [
        type>class [
            dup . flush
            "predicate" word-prop instances [
                class drop
            ] each
        ] when*
    ] each
] unit-test

! Erg's bug
2 [ [ [ 3 throw ] instances ] must-fail ] times

Add unit test for GC problem

USING: generic kernel kernel.private math memory prettyprint io
sequences tools.test words namespaces layouts classes
classes.builtin arrays quotations io.launcher system ;
IN: memory.tests

! LOL
[ ] [
    vm
    "-generations=2"
    "-e=USING: memory io prettyprint system ; input-stream gc . 0 exit"
    3array try-process
] unit-test

[ [ ] instances ] must-infer

! Code GC wasn't kicking in when needed
: leak-step 800000 f <array> 1quotation call drop ;

: leak-loop 100 [ leak-step ] times ;

[ ] [ leak-loop ] unit-test

TUPLE: testing x y z ;

[ save-image-and-exit ] must-fail

[ ] [
    num-types get [
        type>class [
            dup . flush
            "predicate" word-prop instances [
                class drop
            ] each
        ] when*
    ] each
] unit-test

! Erg's bug
2 [ [ [ 3 throw ] instances ] must-fail ] times


USING: destructors io io.encodings.binary io.files io.directories
io.files.temp io.ports kernel sequences math
specialized-arrays.instances.alien.c-types.int tools.test ;
IN: io.ports.tests

! Make sure that writing malloced storage to a file works, and
! also make sure that writes larger than the buffer size work

[ ] [
    "test.txt" temp-file binary [
        100,000 iota
        0
        100,000 malloc-int-array &dispose [ copy ] keep write
    ] with-file-writer
] unit-test

[ t ] [
    "test.txt" temp-file binary [
        100,000 4 * read byte-array>int-array 100,000 iota sequence=
    ] with-file-reader
] unit-test

[ ] [ "test.txt" temp-file delete-file ] unit-test

Add some commented out unit tests to io.ports.tests that seem like they should be supported

USING: destructors io io.encodings.binary io.files io.directories
io.files.temp io.ports kernel sequences math
specialized-arrays.instances.alien.c-types.int tools.test
specialized-arrays alien.c-types classes.struct alien ;
IN: io.ports.tests

! Make sure that writing malloced storage to a file works, and
! also make sure that writes larger than the buffer size work

[ ] [
    "test.txt" temp-file binary [
        100,000 iota
        0
        100,000 malloc-int-array &dispose [ copy ] keep write
    ] with-file-writer
] unit-test

[ t ] [
    "test.txt" temp-file binary [
        100,000 4 * read byte-array>int-array 100,000 iota sequence=
    ] with-file-reader
] unit-test

USE: multiline
/*
[ ] [
    BV{ 0 1 2 } "test.txt" temp-file binary set-file-contents
] unit-test

[ t ] [
    "test.txt" temp-file binary file-contents
    B{ 0 1 2 } =
] unit-test

STRUCT: pt { x uint } { y uint } ;
SPECIALIZED-ARRAY: pt

CONSTANT: pt-array-1
    pt-array{ S{ pt f 1 1 } S{ pt f 2 2 } S{ pt f 3 3 } }

[ ] [
    pt-array-1
    "test.txt" temp-file binary set-file-contents
] unit-test

[ t ] [
    "test.txt" temp-file binary file-contents
    pt-array-1 >c-ptr sequence=
] unit-test

[ ] [
    pt-array-1 rest-slice
    "test.txt" temp-file binary set-file-contents
] unit-test

[ t ] [
    "test.txt" temp-file binary file-contents
    pt-array-1 rest-slice >c-ptr sequence=
] unit-test

*/

[ ] [ "test.txt" temp-file delete-file ] unit-test


IN: io.monitors.tests
USING: io.monitors tools.test io.files system sequences
continuations namespaces concurrency.count-downs kernel io
threads calendar ;

os { winnt macosx linux } member? [
    [ "monitor-test" temp-file delete-tree ] ignore-errors

    [ ] [ "monitor-test" temp-file make-directory ] unit-test

    [ ] [ "monitor-test" temp-file t <monitor> "m" set ] unit-test

    [ ] [ 1 <count-down> "c" set ] unit-test

    [ ] [
        [
           [
               "m" get next-change drop
               dup print flush
               "test.txt" tail? not
           ] [ ] [ ] while
           "c" get count-down
        ] "Monitor test thread" spawn drop
    ] unit-test

    [ ] [ "monitor-test/test.txt" touch-file ] unit-test

    [ ] [ "c" get 30 seconds await-timeout ] unit-test
] when

Add unit tests for monitors

IN: io.monitors.tests
USING: io.monitors tools.test io.files system sequences
continuations namespaces concurrency.count-downs kernel io
threads calendar prettyprint ;

os { winnt macosx linux } member? [
    [ "monitor-test" temp-file delete-tree ] ignore-errors

    [ ] [ "monitor-test/xyz" temp-file make-directories ] unit-test

    [ ] [ "monitor-test" temp-file t <monitor> "m" set ] unit-test

    [ ] [ 1 <count-down> "b" set ] unit-test

    [ ] [ 1 <count-down> "c" set ] unit-test

    [ ] [
        [
            "b" get count-down
           [
               "m" get next-change drop
               dup print flush right-trim-separators
               "xyz" tail? not
           ] [ ] [ ] while
           "c" get count-down
        ] "Monitor test thread" spawn drop
    ] unit-test

    [ ] [ "b" get await ] unit-test

    [ ] [ "monitor-test/xyz/test.txt" temp-file touch-file ] unit-test

    [ ] [ "c" get 30 seconds await-timeout ] unit-test
] when


! Copyright (C) 2007 Daniel Ehrenberg
! See http://factorcode.org/license.txt for BSD license.
USING: delegate sequences.private sequences assocs prettyprint.sections
io definitions kernel continuations ;
IN: delegate.protocols

PROTOCOL: sequence-protocol
    clone clone-like like new new-resizable nth nth-unsafe
    set-nth set-nth-unsafe length set-length lengthen ;

PROTOCOL: assoc-protocol
    at* assoc-size >alist set-at assoc-clone-like
    delete-at clear-assoc new-assoc assoc-like ;
    ! assoc-find excluded because GENERIC# 1
    ! everything should work, just slower (with >alist)

PROTOCOL: stream-protocol
    stream-read1 stream-read stream-read-until dispose
    stream-flush stream-write1 stream-write stream-format
    stream-nl make-span-stream make-block-stream stream-readln
    make-cell-stream stream-write-table ;

PROTOCOL: definition-protocol
    where set-where forget uses redefined*
    synopsis* definer definition ;

PROTOCOL: prettyprint-section-protocol
    section-fits? indent-section? unindent-first-line?
    newline-after?  short-section? short-section long-section
    <section> delegate>block add-section ;

Update extra/delegate; removing section protocol since it makes little sense


USING: interpolate multiline
io io.directories io.encodings.ascii io.files
io.files.temp io.launcher io.streams.string kernel locals system
tools.test sequences ;
IN: alien.remote-control.tests

: compile-file ( contents -- )
    "test.c" ascii set-file-contents
    { "gcc" "-I../" "-L.." "-lfactor" "test.c" }
    os macosx? cpu x86.64? and [ "-m64" suffix ] when
    try-process ;

: run-test ( -- line )
    os windows? "temp/a.exe" "temp/a.out" ?
    ascii [ readln ] with-process-reader ;

:: test-embedding ( code -- line )
    image :> image

    [
        I[
#include <vm/master.h>
#include <stdio.h>
#include <stdbool.h>

int main(int argc, char **argv)
{
    F_PARAMETERS p;
    default_parameters(&p);
    p.image_path = STRING_LITERAL("${image}");
    init_factor(&p);
    start_embedded_factor(&p);
    ${code}
    printf("Done.\n");
    return 0;
}
        ]I
    ] with-string-writer
    "resource:temp" [ compile-file ] with-directory
    "resource:" [ run-test ] with-directory ;

[ "Done." ] [ "" test-embedding ] unit-test

[ "Done." ] [ "factor_yield();" test-embedding ] unit-test


USING: interpolate multiline
io io.directories io.encodings.ascii io.files
io.files.temp io.launcher io.streams.string kernel locals system
tools.test sequences ;
IN: alien.remote-control.tests

: compile-file ( contents -- )
    "test.c" ascii set-file-contents
    { "gcc" "-I../" "-L.." "-lfactor" "test.c" }
    os macosx? cpu x86.64? and [ "-m64" suffix ] when
    try-process ;

: run-test ( -- line )
    os windows? "temp/a.exe" "temp/a.out" ?
    ascii [ readln ] with-process-reader ;

:: test-embedding ( code -- line )
    image :> image

    [
        I[
#include <vm/master.h>
#include <stdio.h>
#include <stdbool.h>

int main(int argc, char **argv)
{
    F_PARAMETERS p;
    default_parameters(&p);
    p.image_path = STRING_LITERAL("${image}");
    init_factor(&p);
    start_embedded_factor(&p);
    ${code}
    printf("Done.\n");
    return 0;
}
        ]I
    ] with-string-writer
    "resource:temp" [ compile-file ] with-directory
    "resource:" [ run-test ] with-directory ;

[ "Done." ] [ "" test-embedding ] unit-test

[ "Done." ] [ "factor_yield();" test-embedding ] unit-test

Comment out alien.remote-control tests for now

USING: interpolate multiline
io io.directories io.encodings.ascii io.files
io.files.temp io.launcher io.streams.string kernel locals system
tools.test sequences ;
IN: alien.remote-control.tests

: compile-file ( contents -- )
    "test.c" ascii set-file-contents
    { "gcc" "-I../" "-L.." "-lfactor" "test.c" }
    os macosx? cpu x86.64? and [ "-m64" suffix ] when
    try-process ;

: run-test ( -- line )
    os windows? "temp/a.exe" "temp/a.out" ?
    ascii [ readln ] with-process-reader ;

:: test-embedding ( code -- line )
    image :> image

    [
        I[
#include <vm/master.h>
#include <stdio.h>
#include <stdbool.h>

int main(int argc, char **argv)
{
    F_PARAMETERS p;
    default_parameters(&p);
    p.image_path = STRING_LITERAL("${image}");
    init_factor(&p);
    start_embedded_factor(&p);
    ${code}
    printf("Done.\n");
    return 0;
}
        ]I
    ] with-string-writer
    "resource:temp" [ compile-file ] with-directory
    "resource:" [ run-test ] with-directory ;

! [ "Done." ] [ "" test-embedding ] unit-test

! [ "Done." ] [ "factor_yield();" test-embedding ] unit-test


! Copyright (c) 2008 Eric Mertens
! See http://factorcode.org/license.txt for BSD license.
USING: arrays assocs combinators kernel math sequences math.ranges locals ;
IN: project-euler.076

! http://projecteuler.net/index.php?section=problems&id=76

! DESCRIPTION
! -----------

! How many different ways can one hundred be written as a
! sum of at least two positive integers?

! SOLUTION
! --------

! This solution uses dynamic programming and the following
! recurence relation:

! ways(0,_) = 1
! ways(_,0) = 0
! ways(n,i) = ways(n-i,i) + ways(n,i-1)

<PRIVATE

: init ( n -- table )
    [1,b] [ 0 2array 0 ] H{ } map>assoc
    1 { 0 0 } pick set-at ;

: use ( n i -- n i )
    [ - dup ] keep min ; inline

: ways ( n i table -- )
    over zero? [
        3drop
    ] [
        [ [ 1-  2array ] dip at     ]
        [ [ use 2array ] dip at +   ]
        [ [     2array ] dip set-at ] 3tri
    ] if ;

:: each-subproblem ( n quot -- )
    n [1,b] [ dup [1,b] quot with each ] each ; inline

PRIVATE>

: (euler076) ( n -- m )
    dup init
    [ [ ways ] curry each-subproblem ]
    [ [ dup 2array ] dip at 1- ] 2bi ;

: euler076 ( -- m )
    100 (euler076) ;


Add test file that should have been added.

! Copyright (C) 2020 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel multiline rocksdb.ffi tools.test ;
IN: rocksdb.ffi.tests

![[
{ } [
    rocksdb_restore_options_create
    rocksdb_restore_options_destroy
] unit-test

! Added recently, not in homebrew yet
{ 123 } [
    rocksdb_writeoptions_create
    [ 123 rocksdb_writeoptions_set_sync ]
    [ rocksdb_writeoptions_get_sync ]
    [ rocksdb_writeoptions_destroy ] tri
] unit-test
]]


USING: kernel combinators quotations arrays sequences assocs
       locals shuffle macros fry ;

IN: combinators.short-circuit

! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

:: n&&-rewrite ( quots N -- quot )
   quots
     [ '[ drop N ndup @ dup not ] [ drop N ndrop f ] 2array ]
   map
   [ t ] [ N nnip ] 2array suffix
   '[ f , cond ] ;

MACRO: 0&& ( quots -- quot ) 0 n&&-rewrite ;
MACRO: 1&& ( quots -- quot ) 1 n&&-rewrite ;
MACRO: 2&& ( quots -- quot ) 2 n&&-rewrite ;

! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

:: n||-rewrite ( quots N -- quot )
   quots
     [ '[ drop N ndup @ dup ] [ N nnip ] 2array ]
   map
   [ drop N ndrop t ] [ f ] 2array suffix
   '[ f , cond ] ;

MACRO: 0|| ( quots -- quot ) 0 n||-rewrite ;
MACRO: 1|| ( quots -- quot ) 1 n||-rewrite ;
MACRO: 2|| ( quots -- quot ) 2 n||-rewrite ;

! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Add 3&& and 3|| words

USING: kernel combinators quotations arrays sequences assocs
       locals shuffle macros fry ;

IN: combinators.short-circuit

! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

:: n&&-rewrite ( quots N -- quot )
   quots
     [ '[ drop N ndup @ dup not ] [ drop N ndrop f ] 2array ]
   map
   [ t ] [ N nnip ] 2array suffix
   '[ f , cond ] ;

MACRO: 0&& ( quots -- quot ) 0 n&&-rewrite ;
MACRO: 1&& ( quots -- quot ) 1 n&&-rewrite ;
MACRO: 2&& ( quots -- quot ) 2 n&&-rewrite ;
MACRO: 3&& ( quots -- quot ) 3 n&&-rewrite ;

! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

:: n||-rewrite ( quots N -- quot )
   quots
     [ '[ drop N ndup @ dup ] [ N nnip ] 2array ]
   map
   [ drop N ndrop t ] [ f ] 2array suffix
   '[ f , cond ] ;

MACRO: 0|| ( quots -- quot ) 0 n||-rewrite ;
MACRO: 1|| ( quots -- quot ) 1 n||-rewrite ;
MACRO: 2|| ( quots -- quot ) 2 n||-rewrite ;
MACRO: 3|| ( quots -- quot ) 3 n||-rewrite ;

! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


! Copyright (C) 2006, 2009 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel sequences namespaces assocs math ;
IN: definitions

MIXIN: definition

ERROR: no-compilation-unit definition ;

SYMBOLS: inlined-dependency flushed-dependency called-dependency ;

: set-in-unit ( value key assoc -- )
    [ set-at ] [ no-compilation-unit ] if* ;

SYMBOL: changed-definitions

: changed-definition ( defspec -- )
    inlined-dependency swap changed-definitions get set-in-unit ;

SYMBOL: changed-effects

SYMBOL: changed-generics

SYMBOL: outdated-generics

SYMBOL: new-classes

: new-class ( word -- )
    dup new-classes get set-in-unit ;

: new-class? ( word -- ? )
    new-classes get key? ;

GENERIC: where ( defspec -- loc )

M: object where drop f ;

GENERIC: set-where ( loc defspec -- )

GENERIC: forget* ( defspec -- )

M: f forget* drop ;

SYMBOL: forgotten-definitions

: forgotten-definition ( defspec -- )
    dup forgotten-definitions get set-in-unit ;

: forget ( defspec -- ) [ forgotten-definition ] [ forget* ] bi ;

: forget-all ( definitions -- ) [ forget ] each ;

GENERIC: definer ( defspec -- start end )

GENERIC: definition ( defspec -- seq )

Make FORGET: M\ ... work

! Copyright (C) 2006, 2009 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel sequences namespaces assocs math accessors ;
IN: definitions

MIXIN: definition

ERROR: no-compilation-unit definition ;

SYMBOLS: inlined-dependency flushed-dependency called-dependency ;

: set-in-unit ( value key assoc -- )
    [ set-at ] [ no-compilation-unit ] if* ;

SYMBOL: changed-definitions

: changed-definition ( defspec -- )
    inlined-dependency swap changed-definitions get set-in-unit ;

SYMBOL: changed-effects

SYMBOL: changed-generics

SYMBOL: outdated-generics

SYMBOL: new-classes

: new-class ( word -- )
    dup new-classes get set-in-unit ;

: new-class? ( word -- ? )
    new-classes get key? ;

GENERIC: where ( defspec -- loc )

M: object where drop f ;

GENERIC: set-where ( loc defspec -- )

GENERIC: forget* ( defspec -- )

M: f forget* drop ;

M: wrapper forget* wrapped>> forget* ;

SYMBOL: forgotten-definitions

: forgotten-definition ( defspec -- )
    dup forgotten-definitions get set-in-unit ;

: forget ( defspec -- ) [ forgotten-definition ] [ forget* ] bi ;

: forget-all ( definitions -- ) [ forget ] each ;

GENERIC: definer ( defspec -- start end )

GENERIC: definition ( defspec -- seq )


USING: kernel math tools.test namespaces random
random.blum-blum-shub alien.c-types sequences splitting
grouping ;
IN: blum-blum-shub.tests

[ 887708070 ] [
    T{ blum-blum-shub f 590695557939 811977232793 } clone random-32*
] unit-test

[ 70576473 ] [
    T{ blum-blum-shub f 590695557939 811977232793 } clone [
        32 random-bits
    ] with-random
] unit-test

[ 5570804936418322777 ] [
    T{ blum-blum-shub f 590695557939 811977232793 } clone [
        64 random-bits
    ] with-random
] unit-test

[ 3716213681 ]
[
    T{ blum-blum-shub f 200352954495 846054538649 } clone 100 over [
        random-32* drop
    ] curry times
    random-32*
] unit-test

Fix test failure for random number speedup from using be>.

USING: kernel math tools.test namespaces random
random.blum-blum-shub alien.c-types sequences splitting
grouping ;
IN: blum-blum-shub.tests

[ 887708070 ] [
    T{ blum-blum-shub f 590695557939 811977232793 } clone random-32*
] unit-test

[ 2790910260 ] [
    T{ blum-blum-shub f 590695557939 811977232793 } clone [
        32 random-bits
    ] with-random
] unit-test

[ 11986868292847630671 ] [
    T{ blum-blum-shub f 590695557939 811977232793 } clone [
        64 random-bits
    ] with-random
] unit-test

[ 3716213681 ]
[
    T{ blum-blum-shub f 200352954495 846054538649 } clone 100 over [
        random-32* drop
    ] curry times
    random-32*
] unit-test


! Copyright (C) 2008 Chris Double.
! See http://factorcode.org/license.txt for BSD license.
!
USING: kernel tools.test peg peg.javascript.ast peg.javascript.tokenizer
       peg.javascript.parser accessors multiline sequences math ;
IN: peg.javascript.parser.tests

\ javascript must-infer

{
  T{
      ast-begin
      f
      V{
          T{ ast-number f 123 }
          T{ ast-string f "hello" }
          T{
              ast-call
              f
              T{ ast-get f "foo" }
              V{ T{ ast-get f "x" } }
          }
      }
  }
} [
  "123; 'hello'; foo(x);" tokenizer ast>> javascript ast>>
] unit-test

{ t } [
<"
function foldl(f, initial, seq) {
   for(var i=0; i< seq.length; ++i)
     initial = f(initial, seq[i]);
   return initial;
}
"> tokenizer ast>> javascript remaining>> length zero?
] unit-test

{ t } [
<"
ParseState.prototype.from = function(index) {
    var r = new ParseState(this.input, this.index + index);
    r.cache = this.cache;
    r.length = this.length - index;
    return r;
}
"> tokenizer ast>> javascript remaining>> length zero?
] unit-test

Add a javascript sc test

! Copyright (C) 2008 Chris Double.
! See http://factorcode.org/license.txt for BSD license.
!
USING: kernel tools.test peg peg.javascript.ast peg.javascript.tokenizer
       peg.javascript.parser accessors multiline sequences math ;
IN: peg.javascript.parser.tests

\ javascript must-infer

{
  T{
      ast-begin
      f
      V{
          T{ ast-number f 123 }
          T{ ast-string f "hello" }
          T{
              ast-call
              f
              T{ ast-get f "foo" }
              V{ T{ ast-get f "x" } }
          }
      }
  }
} [
  "123; 'hello'; foo(x);" tokenizer ast>> javascript ast>>
] unit-test

{ t } [
<"
var x=5
var y=10
"> tokenizer ast>> javascript remaining>> length zero?
] unit-test

{ t } [
<"
function foldl(f, initial, seq) {
   for(var i=0; i< seq.length; ++i)
     initial = f(initial, seq[i]);
   return initial;
}
"> tokenizer ast>> javascript remaining>> length zero?
] unit-test

{ t } [
<"
ParseState.prototype.from = function(index) {
    var r = new ParseState(this.input, this.index + index);
    r.cache = this.cache;
    r.length = this.length - index;
    return r;
}
"> tokenizer ast>> javascript remaining>> length zero?
] unit-test


USING: continuations destructors io.sockets kernel ping
tools.test ;
IN: ping.tests

[ t ] [ "localhost" alive? ] unit-test
[ t ] [ "127.0.0.1" alive? ] unit-test
[ f ] [ "0.0.0.0" alive? ] unit-test

Add another unit test so we can see why ping fails, if it does.

USING: continuations destructors io.sockets kernel ping
tools.test ;
IN: ping.tests

[ ] [ "localhost" ping drop ] unit-test
[ t ] [ "localhost" alive? ] unit-test
[ t ] [ "127.0.0.1" alive? ] unit-test
[ f ] [ "0.0.0.0" alive? ] unit-test


USING: io lint kernel math tools.test ;
IN: lint.tests

! Don't write code like this
: lint1 ( obj -- ) [ "hi" print ] [ ] if ; ! when

[ { { lint1 { [ [ ] if ] } } } ] [ \ lint1 lint-word ] unit-test

: lint2 ( n -- n' ) 1 + ; ! 1+
[ { [ 1 + ] } ] [ \ lint2 lint ] unit-test

: lint3 ( a b -- b a b ) dup -rot ; ! tuck

[ { { lint3 { [ dup -rot ] } } } ] [ \ lint3 lint-word ] unit-test

Remove bogus test in lint

USING: io lint kernel math tools.test ;
IN: lint.tests

! Don't write code like this
: lint1 ( obj -- ) [ "hi" print ] [ ] if ; ! when

[ { { lint1 { [ [ ] if ] } } } ] [ \ lint1 lint-word ] unit-test

: lint3 ( a b -- b a b ) dup -rot ; ! tuck

[ { { lint3 { [ dup -rot ] } } } ] [ \ lint3 lint-word ] unit-test


USING: help.syntax help.markup trees assocs ;

HELP: TREE{
{ $syntax "TREE{ { key value }... }" }
{ $values { "key" "a key" } { "value" "a value" } }
{ $description "Literal syntax for an unbalanced tree." } ;

HELP: <tree>
{ $values { "tree" tree } }
{ $description "Creates an empty unbalanced binary tree" } ;

HELP: >tree
{ $values { "assoc" assoc } { "tree" tree } }
{ $description "Converts any " { $link assoc } " into an unbalanced binary tree." } ;

HELP: tree
{ $class-description "This is the class for unbalanced binary search trees. It is not usually intended to be used directly but rather as a basis for other trees." } ;

ARTICLE: { "trees" "intro" } "Binary search trees"
"This is a library for unbalanced binary search trees. It is not intended to be used directly in most situations but rather as a base class for new trees, because performance can degrade to linear time storage/retrieval by the number of keys. These binary search trees conform to the assoc protocol."
{ $subsection tree }
{ $subsection <tree> }
{ $subsection >tree }
{ $subsection POSTPONE: TREE{ } ;

IN: trees
ABOUT: { "trees" "intro" }

Update trees for factor changes

USING: help.syntax help.markup assocs ;
IN: trees

HELP: TREE{
{ $syntax "TREE{ { key value }... }" }
{ $values { "key" "a key" } { "value" "a value" } }
{ $description "Literal syntax for an unbalanced tree." } ;

HELP: <tree>
{ $values { "tree" tree } }
{ $description "Creates an empty unbalanced binary tree" } ;

HELP: >tree
{ $values { "assoc" assoc } { "tree" tree } }
{ $description "Converts any " { $link assoc } " into an unbalanced binary tree." } ;

HELP: tree
{ $class-description "This is the class for unbalanced binary search trees. It is not usually intended to be used directly but rather as a basis for other trees." } ;

ARTICLE: { "trees" "intro" } "Binary search trees"
"This is a library for unbalanced binary search trees. It is not intended to be used directly in most situations but rather as a base class for new trees, because performance can degrade to linear time storage/retrieval by the number of keys. These binary search trees conform to the assoc protocol."
{ $subsection tree }
{ $subsection <tree> }
{ $subsection >tree }
{ $subsection POSTPONE: TREE{ } ;

IN: trees
ABOUT: { "trees" "intro" }


USING: alarms alarms.private kernel calendar sequences
tools.test threads concurrency.count-downs concurrency.promises
fry tools.time math ;
IN: alarms.tests

[ ] [
    1 <count-down>
    { f } clone 2dup
    [ first cancel-alarm count-down ] 2curry 1 seconds later
    swap set-first
    await
] unit-test

[ ] [
    self [ resume ] curry instant later drop
    "test" suspend drop
] unit-test

[ t ] [
    [
        <promise>
        '[ t _ fulfill ] 5 seconds later drop
    ] benchmark 4,000,000,000 >
] unit-test

[ { 3 } ] [
    { 3 } dup
    '[ 4 _ set-first ] 2 seconds later
    1/2 seconds sleep
    cancel-alarm
] unit-test

Make the alarm test wait for the promise to be fulfilled instead of returning immediately and failing

USING: alarms alarms.private calendar concurrency.count-downs
concurrency.promises fry kernel math math.order sequences
threads tools.test tools.time ;
IN: alarms.tests

[ ] [
    1 <count-down>
    { f } clone 2dup
    [ first stop-alarm count-down ] 2curry 1 seconds later
    swap set-first
    await
] unit-test

[ ] [
    self [ resume ] curry instant later drop
    "test" suspend drop
] unit-test

[ t ] [
    [
        <promise>
        [ '[ t _ fulfill ] 2 seconds later drop ]
        [ 5 seconds ?promise-timeout drop ] bi
    ] benchmark 1,500,000,000 2,500,000,000 between?
] unit-test

[ { 3 } ] [
    { 3 } dup
    '[ 4 _ set-first ] 2 seconds later
    1/2 seconds sleep
    stop-alarm
] unit-test


! Copyright (C) 2011 Erik Charlebois.
! See http://factorcode.org/license.txt for BSD license.
USING: arrays help.markup help.syntax math
sequences.private vectors strings kernel math.order layouts
quotations generic.single ;
IN: readline

HELP: readline
{ $values
    { "prompt" string }
    { "str" string }
}
{ $description "Read a line from using readline." } ;

HELP: set-completion
{ $values
    { "quot" "a quotation with stack effect ( str n -- str )"}
}
{ $description "Set the given quotation as the completion hook for readline. The quotation is called with the string to complete and the index in the completion list to return. When all completions have been returned, returning " { $snippet "f" } " terminates the loop." }
{ $examples
    { $example "USING: readline sequences combinators kernel ;"
               "[ nip [ \"keep\" \"dip\" ] ?nth ] set-completion"
               ""
    }
} ;

ARTICLE: "readline" "Readline"
{ $vocab-link "readline" }
;

ABOUT: "readline"

Write some more docs for readline and link readline main article to words.

! Copyright (C) 2011 Erik Charlebois.
! See http://factorcode.org/license.txt for BSD license.
USING: arrays help.markup help.syntax math
sequences.private vectors strings kernel math.order layouts
quotations generic.single ;
IN: readline

HELP: readline
{ $values
    { "prompt" string }
    { "str" string }
}
{ $description "Read a line from using readline." } ;

HELP: set-completion
{ $values
    { "quot" "a quotation with stack effect ( str n -- str )"}
}
{ $description "Set the given quotation as the completion hook for readline. The quotation is called with the string to complete and the index in the completion list to return. When all completions have been returned, returning " { $snippet "f" } " terminates the loop." }
{ $examples
    { $example "USING: readline sequences combinators kernel ;"
               "[ nip [ \"keep\" \"dip\" ] ?nth ] set-completion"
               ""
    }
} ;

ARTICLE: "readline" "Readline"
"The " { $vocab-link "readline" } " vocabulary binds to the C readline library and provides Emacs-style key bindings for editing text. Currently, it only works from the non-graphical UI." $nl

"To read a line:"
{ $subsections readline }
"To set a completion hook:"
{ $subsections set-completion } ;

ABOUT: "readline"


USING: kernel stack-checker.transforms struct-arrays.private ;
IN: struct-arrays

: struct-element-constructor ( c-type -- word )
    "Struct array usages must be compiled" throw ;

<<

\ struct-element-constructor [
    (struct-element-constructor) [ ] curry
] 1 define-transform

>>

Fix tree shaker again, gah

USING: kernel stack-checker.transforms ;
IN: struct-arrays.private

: struct-element-constructor ( c-type -- word )
    "Struct array usages must be compiled" throw ;

<<

\ struct-element-constructor [
    (struct-element-constructor) [ ] curry
] 1 define-transform

>>


! Copyright (C) 2015 Jordan Lewis.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel hashtables accessors assocs locals math sequences ;
IN: malenv

TUPLE: malenv
{ outer read-only }
{ data hashtable read-only } ;

! set outer to f if top level env

INSTANCE: malenv assoc

C: <malenv> malenv
: new-env ( outer -- malenv ) H{ } malenv boa ;

M:: malenv at* ( key assoc -- value/f ? )
    key name>> assoc data>> at*
    [ drop assoc outer>>
      [ key ?of ]
      [ f f ]
      if*
    ]
    unless* ;

M: malenv assoc-size ( assoc -- n )
    [ data>> ] [ outer>> ] bi [ assoc-size ] bi@ + ;

M: malenv >alist ( assoc -- n )
    [ data>> ] [ outer>> ] bi [ >alist ] bi@ append ;

M: malenv set-at ( value key assoc -- )
    [ name>> ] [ data>> ] bi* set-at ;

M: malenv delete-at ( key assoc -- )
    [ name>> ] [ data>> ] bi* delete-at ;

M: malenv clear-assoc ( assoc -- )
    data>> clear-assoc ;

: get-or-throw ( key assoc -- value )
    at* [ dup "no variable " append throw ] unless ;

Fix env variable not found message

! Copyright (C) 2015 Jordan Lewis.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel hashtables accessors assocs locals math sequences ;
IN: malenv

TUPLE: malenv
{ outer read-only }
{ data hashtable read-only } ;

! set outer to f if top level env

INSTANCE: malenv assoc

C: <malenv> malenv
: new-env ( outer -- malenv ) H{ } malenv boa ;

M:: malenv at* ( key assoc -- value/f ? )
    key name>> assoc data>> at*
    [ drop assoc outer>>
      [ key ?of ]
      [ f f ]
      if*
    ]
    unless* ;

M: malenv assoc-size ( assoc -- n )
    [ data>> ] [ outer>> ] bi [ assoc-size ] bi@ + ;

M: malenv >alist ( assoc -- n )
    [ data>> ] [ outer>> ] bi [ >alist ] bi@ append ;

M: malenv set-at ( value key assoc -- )
    [ name>> ] [ data>> ] bi* set-at ;

M: malenv delete-at ( key assoc -- )
    [ name>> ] [ data>> ] bi* delete-at ;

M: malenv clear-assoc ( assoc -- )
    data>> clear-assoc ;

: get-or-throw ( key assoc -- value )
    ?at [ dup name>> "no variable " prepend throw ] unless ;


! Copyright (C) 2008, 2009 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: cocoa cocoa.application cocoa.types cocoa.classes cocoa.windows
core-graphics.types kernel math.bitwise literals ;
IN: webkit-demo

FRAMEWORK: /System/Library/Frameworks/WebKit.framework
IMPORT: WebView

: rect ( -- rect ) 0 0 700 500 <CGRect> ;

: <WebView> ( -- id )
    WebView -> alloc
    rect f f -> initWithFrame:frameName:groupName: ;

CONSTANT: window-style ( -- n )
    flags{
        NSClosableWindowMask
        NSMiniaturizableWindowMask
        NSResizableWindowMask
        NSTitledWindowMask
    }

: <WebWindow> ( -- id )
    <WebView> rect window-style <ViewWindow> ;

: load-url ( window url -- )
    [ -> contentView ] [ <NSString> ] bi* -> setMainFrameURL: ;

: webkit-demo ( -- )
    <WebWindow>
    [ -> center ]
    [ f -> makeKeyAndOrderFront: ]
    [ "http://factorcode.org" load-url ] tri ;

: run-webkit-demo ( -- )
    [ webkit-demo ] cocoa-app ;

MAIN: run-webkit-demo

Fix typo in webkit demo

! Copyright (C) 2008, 2009 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: cocoa cocoa.application cocoa.types cocoa.classes cocoa.windows
core-graphics.types kernel math.bitwise literals ;
IN: webkit-demo

FRAMEWORK: /System/Library/Frameworks/WebKit.framework
IMPORT: WebView

: rect ( -- rect ) 0 0 700 500 <CGRect> ;

: <WebView> ( -- id )
    WebView -> alloc
    rect f f -> initWithFrame:frameName:groupName: ;

CONSTANT: window-style
    flags{
        NSClosableWindowMask
        NSMiniaturizableWindowMask
        NSResizableWindowMask
        NSTitledWindowMask
    }

: <WebWindow> ( -- id )
    <WebView> rect window-style <ViewWindow> ;

: load-url ( window url -- )
    [ -> contentView ] [ <NSString> ] bi* -> setMainFrameURL: ;

: webkit-demo ( -- )
    <WebWindow>
    [ -> center ]
    [ f -> makeKeyAndOrderFront: ]
    [ "http://factorcode.org" load-url ] tri ;

: run-webkit-demo ( -- )
    [ webkit-demo ] cocoa-app ;

MAIN: run-webkit-demo


! Copyright (C) 2010 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license.
USING: assocs dns io.encodings.utf8 io.files kernel
math.statistics sequences splitting system unicode.categories ;
IN: dns.unix

: load-resolve.conf ( -- seq )
    "/etc/resolv.conf" utf8 file-lines
    [ [ blank? ] trim ] map
    [ "#" head? not ] filter
    [ [ " " split1 swap ] dip push-at ] sequence>hashtable "nameserver" swap at ;

M: unix initial-dns-servers load-resolve.conf ;

Remove blank lines from resolve.conf just in case

! Copyright (C) 2010 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license.
USING: assocs dns io.encodings.utf8 io.files kernel
math.statistics sequences splitting system unicode.categories ;
IN: dns.unix

: load-resolve.conf ( -- seq )
    "/etc/resolv.conf" utf8 file-lines
    [ [ blank? ] trim ] map harvest
    [ "#" head? not ] filter
    [ [ " " split1 swap ] dip push-at ] sequence>hashtable "nameserver" swap at ;

M: unix initial-dns-servers load-resolve.conf ;


! Copyright (C) 2004, 2007 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel math strings kernel.private sequences.private
sequences strings growable strings.private sbufs.private ;
IN: sbufs

: <sbuf> ( n -- sbuf ) 0 <string> 0 string>sbuf ; inline

M: sbuf set-nth-unsafe
    underlying >r >r >fixnum r> >fixnum r> set-char-slot ;

M: sbuf new drop [ 0 <string> ] keep string>sbuf ;

: >sbuf ( seq -- sbuf ) SBUF" " clone-like ; inline

M: sbuf like
    drop dup sbuf? [
        dup string? [ dup length string>sbuf ] [ >sbuf ] if
    ] unless ;

M: sbuf new-resizable drop <sbuf> ;

M: sbuf equal?
    over sbuf? [ sequence= ] [ 2drop f ] if ;

M: string new-resizable drop <sbuf> ;

M: string like
    drop dup string? [
        dup sbuf? [
            dup length over underlying length number= [
                underlying dup reset-string-hashcode
            ] [
                >string
            ] if
        ] [
            >string
        ] if
    ] unless ;

INSTANCE: sbuf growable

Fix string buffer implementation bug

! Copyright (C) 2004, 2007 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel math strings kernel.private sequences.private
sequences strings growable strings.private sbufs.private ;
IN: sbufs

: <sbuf> ( n -- sbuf ) 0 <string> 0 string>sbuf ; inline

M: sbuf set-nth-unsafe
    underlying >r >r >fixnum r> >fixnum r> set-char-slot ;

M: sbuf new drop [ 0 <string> ] keep >fixnum string>sbuf ;

: >sbuf ( seq -- sbuf ) SBUF" " clone-like ; inline

M: sbuf like
    drop dup sbuf? [
        dup string? [ dup length string>sbuf ] [ >sbuf ] if
    ] unless ;

M: sbuf new-resizable drop <sbuf> ;

M: sbuf equal?
    over sbuf? [ sequence= ] [ 2drop f ] if ;

M: string new-resizable drop <sbuf> ;

M: string like
    drop dup string? [
        dup sbuf? [
            dup length over underlying length number= [
                underlying dup reset-string-hashcode
            ] [
                >string
            ] if
        ] [
            >string
        ] if
    ] unless ;

INSTANCE: sbuf growable


USING: cocoa cocoa.messages cocoa.application cocoa.nibs
assocs namespaces kernel words compiler.units sequences
ui.cocoa ;

"stop-after-last-window?" get
global [
    stop-after-last-window? set

    [ "MiniFactor.nib" load-nib ] cocoa-init-hook set-global

    ! Only keeps those methods that we actually call
    sent-messages get super-sent-messages get assoc-union
    objc-methods [ assoc-intersect ] change

    sent-messages get
    super-sent-messages get
    [ keys [ objc-methods get at dup ] H{ } map>assoc ] bi@
    super-message-senders [ assoc-intersect ] change
    message-senders [ assoc-intersect ] change

    sent-messages off
    super-sent-messages off

    ! We need this for strip-stack-traces to work fully
    { message-senders super-message-senders }
    [ get values compile ] each
] bind

Fix deployment on Mac OS X

USING: cocoa cocoa.messages cocoa.application cocoa.nibs
assocs namespaces kernel words compiler.units sequences
ui ui.cocoa ;

"stop-after-last-window?" get
global [
    stop-after-last-window? set

    [ "MiniFactor.nib" load-nib ] cocoa-init-hook set-global

    ! Only keeps those methods that we actually call
    sent-messages get super-sent-messages get assoc-union
    objc-methods [ assoc-intersect ] change

    sent-messages get
    super-sent-messages get
    [ keys [ objc-methods get at dup ] H{ } map>assoc ] bi@
    super-message-senders [ assoc-intersect ] change
    message-senders [ assoc-intersect ] change

    sent-messages off
    super-sent-messages off

    ! We need this for strip-stack-traces to work fully
    { message-senders super-message-senders }
    [ get values compile ] each
] bind


USING: kernel ctags tools.test io.backend sequences arrays prettyprint ;
IN: ctags.tests

[ t ] [
  91
  { { if  { "resource:extra/unix/unix.factor" 91 } } } ctag-lineno =
] unit-test

[ t ] [
  "resource:extra/unix/unix.factor"
  { { if  { "resource:extra/unix/unix.factor" 91 } } } ctag-path =
] unit-test

[ t ] [
  if
  { { if  { "resource:extra/unix/unix.factor" 91 } } } ctag-word =
] unit-test

[ t ] [
  "if\t" "resource:extra/unix/unix.factor" normalize-path "\t91" 3append
  { if  { "resource:extra/unix/unix.factor" 91 } } ctag =
] unit-test

[ t ] [
  "if\t" "resource:extra/unix/unix.factor" normalize-path "\t91" 3append 1array
  { { if  { "resource:extra/unix/unix.factor" 91 } } } ctag-strings =
] unit-test

Fix errors in new tests

USING: kernel ctags tools.test io.backend sequences arrays prettyprint ;
IN: ctags.tests

[ t ] [
  91
  { if  { "resource:extra/unix/unix.factor" 91 } } ctag-lineno =
] unit-test

[ t ] [
  "resource:extra/unix/unix.factor"
  { if  { "resource:extra/unix/unix.factor" 91 } } ctag-path =
] unit-test

[ t ] [
  \ if
  { if  { "resource:extra/unix/unix.factor" 91 } } ctag-word =
] unit-test

[ t ] [
  "if\t" "resource:extra/unix/unix.factor" normalize-path "\t91" 3append
  { if  { "resource:extra/unix/unix.factor" 91 } } ctag =
] unit-test

[ t ] [
  "if\t" "resource:extra/unix/unix.factor" normalize-path "\t91" 3append 1array
  { { if  { "resource:extra/unix/unix.factor" 91 } } } ctag-strings =
] unit-test


! Copyright (c) 2007 Aaron Schaefer
! See http://factorcode.org/license.txt for BSD license.
USING: arrays combinators io kernel math math.functions math.parser
    math.statistics namespaces sequences tools.time ;
IN: project-euler.ave-time

: collect-benchmarks ( quot n -- seq )
  [
    >r >r datastack r> [ benchmark , ] curry tuck
    [ with-datastack drop ] 2curry r> swap times call
  ] { } make ;

: ave-time ( quot n -- )
    [ collect-benchmarks ] keep swap mean round [
        # " ms run time - " % # " trials" %
    ] "" make print flush ; inline

Fix load error in project-euler

! Copyright (c) 2007 Aaron Schaefer
! See http://factorcode.org/license.txt for BSD license.
USING: arrays combinators io kernel math math.functions math.parser
    math.statistics namespaces sequences tools.time continuations ;
IN: project-euler.ave-time

: collect-benchmarks ( quot n -- seq )
  [
    >r >r datastack r> [ benchmark , ] curry tuck
    [ with-datastack drop ] 2curry r> swap times call
  ] { } make ;

: ave-time ( quot n -- )
    [ collect-benchmarks ] keep swap mean round [
        # " ms run time - " % # " trials" %
    ] "" make print flush ; inline


USING: system-info.linux strings splitting sequences
tools.test ;
IN: system-info.linux.tests

[ 6 ] [ uname length ] unit-test

[ t ] [ sysname    string? ] unit-test
[ t ] [ nodename   string? ] unit-test
[ t ] [ release    string? ] unit-test
[ t ] [ version    string? ] unit-test
[ t ] [ machine    string? ] unit-test
[ t ] [ domainname string? ] unit-test

[ t ] [
    release "." split1 drop { "2" "3" } member?
] unit-test

Fix a using in system-info.linux

USING: system-info.linux strings splitting sequences
tools.test kernel ;
IN: system-info.linux.tests

[ 6 ] [ uname length ] unit-test

[ t ] [ sysname    string? ] unit-test
[ t ] [ nodename   string? ] unit-test
[ t ] [ release    string? ] unit-test
[ t ] [ version    string? ] unit-test
[ t ] [ machine    string? ] unit-test
[ t ] [ domainname string? ] unit-test

[ t ] [
    release "." split1 drop { "2" "3" } member?
] unit-test


! Copyright (C) 2010 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license.
USING: alien.c-types alien.strings cuda cuda.syntax destructors
io.encodings.utf8 kernel locals math prettyprint sequences ;
IN: cuda.hello-world

CUDA-LIBRARY: hello vocab:cuda/hello.ptx

CUDA-FUNCTION: helloWorld ( char* string-ptr ) ;

:: cuda-hello-world ( -- )
    T{ launcher
        { device 0 }
        { path "vocab:cuda/hello.ptx" }
    } [
        "Hello World!" [ - ] map-index malloc-device-string &dispose dup :> str

        T{ function-launcher
            { dim-block { 6 1 1 } }
            { dim-grid { 2 1 } }
            { shared-size 0 }
        }
        helloWorld

        ! <<< { 6 1 1 } { 2 1 } 1 >>> helloWorld

        str device>host utf8 alien>string .
    ] with-cuda ;

MAIN: cuda-hello-world

Fix the ptx path in cuda demo

! Copyright (C) 2010 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license.
USING: alien.c-types alien.strings cuda cuda.syntax destructors
io.encodings.utf8 kernel locals math prettyprint sequences ;
IN: cuda.demos.hello-world

CUDA-LIBRARY: hello vocab:cuda/demos/hello-world/hello.ptx

CUDA-FUNCTION: helloWorld ( char* string-ptr ) ;

:: cuda-hello-world ( -- )
    T{ launcher
        { device 0 }
        { path "vocab:cuda/demos/hello-world/hello.ptx" }
    } [
        "Hello World!" [ - ] map-index malloc-device-string &dispose dup :> str

        T{ function-launcher
            { dim-block { 6 1 1 } }
            { dim-grid { 2 1 } }
            { shared-size 0 }
        }
        helloWorld

        ! <<< { 6 1 1 } { 2 1 } 1 >>> helloWorld

        str device>host utf8 alien>string .
    ] with-cuda ;

MAIN: cuda-hello-world


! Copyright (C) 2008, 2010 Slava Pestov, Jorge Acereda Macia.
! See http://factorcode.org/license.txt for BSD license.
USING: io.files io.files.temp io words alien kernel math.parser
alien.syntax io.launcher assocs arrays sequences namespaces make
system math io.encodings.ascii accessors tools.disassembler
tools.disassembler.private ;
IN: tools.disassembler.gdb

SINGLETON: gdb-disassembler

: in-file ( -- path ) "gdb-in.txt" temp-file ;

: out-file ( -- path ) "gdb-out.txt" temp-file ;

: make-disassemble-cmd ( from to -- )
    in-file ascii [
        "attach " write
        current-process-handle number>string print
        "disassemble " write
        [ number>string write bl ] bi@
    ] with-file-writer ;

: gdb-binary ( -- string ) "gdb" ;

: run-gdb ( -- lines )
    <process>
        +closed+ >>stdin
        out-file >>stdout
        [ gdb-binary , "-x" , in-file , "-batch" , ] { } make >>command
    try-process
    out-file ascii file-lines ;

M: gdb-disassembler disassemble*
    make-disassemble-cmd run-gdb ;

gdb-disassembler disassembler-backend set-global

Fix GDB-based disassemble for GDB 7

! Copyright (C) 2008, 2010 Slava Pestov, Jorge Acereda Macia.
! See http://factorcode.org/license.txt for BSD license.
USING: io.files io.files.temp io words alien kernel math.parser
alien.syntax io.launcher assocs arrays sequences namespaces make
system math io.encodings.ascii accessors tools.disassembler
tools.disassembler.private locals ;
IN: tools.disassembler.gdb

SINGLETON: gdb-disassembler

: in-file ( -- path ) "gdb-in.txt" temp-file ;

: out-file ( -- path ) "gdb-out.txt" temp-file ;

:: make-disassemble-cmd ( from to -- )
    in-file ascii [
        "attach " write
        current-process-handle number>string print
        "x/" write to from - 4 / number>string write
        "i" write bl from number>string write
    ] with-file-writer ;

: gdb-binary ( -- string ) "gdb" ;

: run-gdb ( -- lines )
    <process>
        +closed+ >>stdin
        out-file >>stdout
        [ gdb-binary , "-x" , in-file , "-batch" , ] { } make >>command
    try-process
    out-file ascii file-lines ;

M: gdb-disassembler disassemble*
    make-disassemble-cmd run-gdb ;

gdb-disassembler disassembler-backend set-global


require "./spec_helper"

describe Interpreter do
  it "runs the spec suite" do

    # Build the interpreter
    Process.run("crystal", [
      "build",
      "src/charly.cr",
      "--release",
      "--stats",
      "-o bin/charly"
    ], output: STDOUT, input: STDIN, error: STDERR)

    # Set the environment variable for the std-lib
    Process.run("export", [
      "CHARLYDIR=./src/charly/std-lib"
    ])

    # Run the spec
    result = Process.run("./bin/charly", [
      "test/main.charly"
    ], output: STDOUT, input: STDIN, error: STDERR)

    result.exit_status.should eq(0)
  end
end

Create the bin directory in the travis build

require "./spec_helper"

describe Interpreter do
  it "runs the spec suite" do

    # Create the bin directory
    Process.run("mkdir", [
      "bin"
    ])

    # Build the interpreter
    Process.run("crystal", [
      "build",
      "src/charly.cr",
      "--release",
      "--stats",
      "-o bin/charly"
    ], output: STDOUT, input: STDIN, error: STDERR)

    # Set the environment variable for the std-lib
    Process.run("export", [
      "CHARLYDIR=./src/charly/std-lib"
    ])

    # Run the spec
    result = Process.run("./bin/charly", [
      "test/main.charly"
    ], output: STDOUT, input: STDIN, error: STDERR)

    result.exit_status.should eq(0)
  end
end


class MainLayout
  include LuckyWeb::Page
  include LuckyWeb::Layout

  @page : BasePage

  render do
    html_doctype

    html do
      head do
        utf8_charset
        title @page.page_title
        css_link asset("css/app.css")
        js_link asset("js/app.js")
      end

      body do
        @page.render_inner
      end
    end
  end
end

Add default lang to layout

class MainLayout
  include LuckyWeb::Page
  include LuckyWeb::Layout

  @page : BasePage

  render do
    html_doctype

    html lang: "en" do
      head do
        utf8_charset
        title @page.page_title
        css_link asset("css/app.css")
        js_link asset("js/app.js")
      end

      body do
        @page.render_inner
      end
    end
  end
end


require "json"

module Discordcr
  module REST
    # A response to the Get Gateway REST API call.
    struct GatewayResponse
      JSON.mapping(
        url: String
      )
    end
  end
end

Add a mapping for gateway packets

require "json"

module Discordcr
  module REST
    # A response to the Get Gateway REST API call.
    struct GatewayResponse
      JSON.mapping(
        url: String
      )
    end
  end

  struct GatewayPacket
    JSON.mapping(
      op: UInt8,
      d: Nil,
      s: UInt32 | Nil,
      t: String | Nil
    )
end


require "http/client"
require "openssl/ssl/context"

require "./mappings"

module Discordcr
  module REST
    SSL_CONTEXT = OpenSSL::SSL::Context::Client.new

    def request(endpoint_key : Symbol, method : String, url : String | URI, headers : HTTP::Headers, body : String | Nil)
      HTTP::Client.exec(method: method, url: url, headers: headers, body: body, tls: SSL_CONTEXT)
    end

    API_BASE = "https://discordapp.com/api/v6"

    GET = "GET"
    POST = "POST"

    def gateway
      response = request(
        :gateway,
        GET,
        API_BASE + "/gateway",
        HTTP::Headers.new,
        nil
      )

      # TODO: Investigate failed nil assertion with body_io
      GatewayResponse.from_json(response.body)
    end

    def send_message(channel_id, content)
      response = request(
        :send_message,
        POST,
        API_BASE + "/channels/#{channel_id}/messages",
        HTTP::Headers{ "Content-Type" => "application/json" },
        { content: content }.to_json
      )
    end
  end
end

Set the Authorization header to the token

require "http/client"
require "openssl/ssl/context"

require "./mappings"

module Discordcr
  module REST
    SSL_CONTEXT = OpenSSL::SSL::Context::Client.new

    def request(endpoint_key : Symbol, method : String, url : String | URI, headers : HTTP::Headers, body : String | Nil)
      headers["Authorization"] = @token
      HTTP::Client.exec(method: method, url: url, headers: headers, body: body, tls: SSL_CONTEXT)
    end

    API_BASE = "https://discordapp.com/api/v6"

    GET = "GET"
    POST = "POST"

    def gateway
      response = request(
        :gateway,
        GET,
        API_BASE + "/gateway",
        HTTP::Headers.new,
        nil
      )

      # TODO: Investigate failed nil assertion with body_io
      GatewayResponse.from_json(response.body)
    end

    def send_message(channel_id, content)
      response = request(
        :send_message,
        POST,
        API_BASE + "/channels/#{channel_id}/messages",
        HTTP::Headers{ "Content-Type" => "application/json" },
        { content: content }.to_json
      )
    end
  end
end


require "./../spec_helper"

describe M3U8::Playlist do
  describe "when creating a playlist" do
    let(playlist) { M3U8::Playlist.new }

    it "inserts the base header" do
      output = \
          "#EXTM3U\n" \
          "\n"

      expect(playlist.to_s).to eq(output)
    end

    describe "media playlist" do
      it "accepts new segments" do
        playlist.add_segment("S01E01-1080-0001.ts", 9.003)

        output = \
          "#EXTM3U\n" \
          "\n" \
          "#EXTINF:9.003,\n" \
          "S01E01-1080-0001.ts\n"

        expect(playlist.to_s).to eq(output)
      end
    end
  end
end

Validate independent parts instead of the whole output

require "./../spec_helper"

describe M3U8::Playlist do
  describe "when creating a playlist" do
    let(playlist) { M3U8::Playlist.new }

    it "inserts the header" do
      expect(playlist.to_s).to match(/#EXTM3U\n/)
    end

    describe "media playlist" do
      it "accepts new segments" do
        playlist.add_segment("S01E01-1080-0001.ts", 9.003)

        output = "#EXTINF:9.003,\n" \
                 "S01E01-1080-0001.ts\n"

        expect(playlist.to_s).to match(/#{output}/)
      end
    end
  end
end


struct Pointer(T)
  def +(other : Int)
    self + other.to_i64
  end

  def []=(offset, value)
    (self + offset).value = value
  end
end

Add safety constraints to Pointer

struct Pointer(T)
  def +(other : Int)
    self + other.to_i64
  end

  def []=(offset : Int, value : T)
    (self + offset).value = value
  end
end


require "./tree.cr"

module Crustache
  abstract class FileSystem
    abstract def load(value : String) : Tree::Template?

    def load!(value : String) : Tree::Template
      if tmpl = self.load value
        return tmpl
      else
        raise "#{value} is not found"
      end
    end
  end

  class HashFileSystem < FileSystem
    def initialize
      @tmpls = {} of String => Tree::Template
    end

    def register(name, tmpl)
      @tmpls[name] = tmpl
    end

    def load(value)
      return @tmpls[value]?
    end
  end

  class ViewLoader < FileSystem
    def initialize(@basedir : String, @use_cache = false)
      @cache = {} of String => Tree::Template?
    end

    def load(value)
      if @cache.has_key?(value)
        return @cache[value]
      end

      filename = "#{@basedir}/#{value}"
      filename_ext = "#{filename}.mustache"
      if File.exists?(filename_ext)
        tmpl = Crustache.parseFile filename_ext
        @cache[value] = tmpl if @use_cache
        return tmpl
      end

      if File.exists?(filename)
        tmpl = Crustache.parse filename
        @cache[value] = tmpl if @use_cache
        return tmpl
      end

      @cache[value] = nil if @use_cache
      return nil
    end
  end
end

Add extensions for implicit loading

require "./tree.cr"

module Crustache
  abstract class FileSystem
    abstract def load(value : String) : Tree::Template?

    def load!(value : String) : Tree::Template
      if tmpl = self.load value
        return tmpl
      else
        raise "#{value} is not found"
      end
    end
  end

  class HashFileSystem < FileSystem
    def initialize
      @tmpls = {} of String => Tree::Template
    end

    def register(name, tmpl)
      @tmpls[name] = tmpl
    end

    def load(value)
      return @tmpls[value]?
    end
  end

  class ViewLoader < FileSystem
    EXTENSION = [".mustache", ".html", ""]

    def initialize(@basedir : String, @use_cache = false, @extension = EXTENSION)
      @cache = {} of String => Tree::Template?
    end

    def load(value)
      if @cache.has_key?(value)
        return @cache[value]
      end

      @extension.each do |ext|
        filename = "#{@basedir}/#{value}"
        filename_ext = "#{filename}#{ext}"
        if File.exists?(filename_ext)
          tmpl = Crustache.parseFile filename_ext
          @cache[value] = tmpl if @use_cache
          return tmpl
        end
      end

      @cache[value] = nil if @use_cache
      return nil
    end
  end
end


class MockRequest < HTTP::Request
  def initialize

  end
end

Add a basic MockRequest for mocking a HTTP::Request

class MockRequest

  def initialize
  end

  def get(path : String, headers = Hash.new : Hash(String, String | Array(String)))
    http_headers = HTTP::Headers.new

    headers.each do |key, value|
      http_headers.add(key, value)
    end

    HTTP::Request.new("GET", path, http_headers)
  end

end


AMBER_PORT = ENV["PORT"]? || 3008
AMBER_ENV  = ENV["AMBER_ENV"]? || "development"

require "amber"
require "./initializers/*"

Amber::Server.instance.config do |app|
  app_path = __FILE__
  app.name = "Crystal [ANN] web application."
  app.port = AMBER_PORT.to_i
  app.env = AMBER_ENV.to_s
  app.log = ::Logger.new(STDOUT)
  app.log.level = ::Logger::INFO
end

Amber::Server.instance.session = {
  :key     => "crystal.ann.session",
  :store   => :cookie,
  :expires => 120,
  :secret  => ENV["AMBER_SESSION_SECRET"]? || "",
}

Expire session when browser is closed

AMBER_PORT = ENV["PORT"]? || 3008
AMBER_ENV  = ENV["AMBER_ENV"]? || "development"

require "amber"
require "./initializers/*"

Amber::Server.instance.config do |app|
  app_path = __FILE__
  app.name = "Crystal [ANN] web application."
  app.port = AMBER_PORT.to_i
  app.env = AMBER_ENV.to_s
  app.log = ::Logger.new(STDOUT)
  app.log.level = ::Logger::INFO
end

Amber::Server.instance.session = {
  :key     => "crystal.ann.session",
  :store   => :cookie,
  :expires => 0,
  :secret  => ENV["AMBER_SESSION_SECRET"]? || "",
}


require "./crustache/*"

module Crustache
  # :nodoc:
  OPEN_TAG = "{{".to_slice
  # :nodoc:
  CLOSE_TAG = "}}".to_slice

  def self.parse(string : String, filename = "<inline>", row = 1) : Tree::Template
    self.parse StringIO.new(string), filename, row
  end

  def self.parse(io : IO, filename = "<inline>", row = 1) : Tree::Template
    Parser.new(OPEN_TAG, CLOSE_TAG, io, filename, row).parse
  end

  def self.parseFile(filename : String) : Tree::Template
    self.parse(File.new(filename), filename, 1)
  end

  def self.render(tmpl : Tree::Template, model, fs = FileSystem.new) : String
    String.build do |io|
      self.render tmpl, model, fs, io
    end
  end

  def self.render(tmpl : Tree::Template, model, fs : FileSystem, io : IO)
    tmpl.visit Renderer.new OPEN_TAG, CLOSE_TAG, [model], fs, io
  end
end

Fix a bug that FileSystem can't create instance

require "./crustache/*"

module Crustache
  # :nodoc:
  OPEN_TAG = "{{".to_slice
  # :nodoc:
  CLOSE_TAG = "}}".to_slice

  def self.parse(string : String, filename = "<inline>", row = 1) : Tree::Template
    self.parse StringIO.new(string), filename, row
  end

  def self.parse(io : IO, filename = "<inline>", row = 1) : Tree::Template
    Parser.new(OPEN_TAG, CLOSE_TAG, io, filename, row).parse
  end

  def self.parseFile(filename : String) : Tree::Template
    self.parse(File.new(filename), filename, 1)
  end

  def self.render(tmpl : Tree::Template, model, fs = HashFileSystem.new) : String
    String.build do |io|
      self.render tmpl, model, fs, io
    end
  end

  def self.render(tmpl : Tree::Template, model, fs : FileSystem, io : IO)
    tmpl.visit Renderer.new OPEN_TAG, CLOSE_TAG, [model], fs, io
  end
end


require "./converters"
require "./user"
require "./channel"

module Discord
  module Gateway
    struct ReadyPayload
      JSON.mapping(
        v: UInt8,
        user: User,
        private_channels: Array(PrivateChannel),
        guilds: Array(UnavailableGuild),
        session_id: String
      )
    end

    struct HelloPayload
      JSON.mapping(
        heartbeat_interval: UInt32,
        _trace: Array(String)
      )
    end

    struct GuildDeletePayload
      JSON.mapping(
        id: {type: UInt64, converter: SnowflakeConverter},
        unavailable: {type: Bool, nilable: true}
      )
    end

    struct GuildBanPayload
      JSON.mapping(
        username: String,
        id: {type: UInt64, converter: SnowflakeConverter},
        discriminator: String,
        avatar: String,
        bot: {type: Bool, nilable: true},
        guild_id: {type: UInt64, converter: SnowflakeConverter}
      )
    end
  end
end

Add a mapping for the GUILD_EMOJI_UPDATE dispatch payload

require "./converters"
require "./user"
require "./channel"
require "./guild"

module Discord
  module Gateway
    struct ReadyPayload
      JSON.mapping(
        v: UInt8,
        user: User,
        private_channels: Array(PrivateChannel),
        guilds: Array(UnavailableGuild),
        session_id: String
      )
    end

    struct HelloPayload
      JSON.mapping(
        heartbeat_interval: UInt32,
        _trace: Array(String)
      )
    end

    struct GuildDeletePayload
      JSON.mapping(
        id: {type: UInt64, converter: SnowflakeConverter},
        unavailable: {type: Bool, nilable: true}
      )
    end

    struct GuildBanPayload
      JSON.mapping(
        username: String,
        id: {type: UInt64, converter: SnowflakeConverter},
        discriminator: String,
        avatar: String,
        bot: {type: Bool, nilable: true},
        guild_id: {type: UInt64, converter: SnowflakeConverter}
      )
    end

    struct GuildEmojiUpdatePayload
      JSON.mapping(
        guild_id: {type: UInt64, converter: SnowflakeConverter},
        emoji: {type: Array(Emoji), key: "emojis"}
      )
    end
  end
end


require "http/client"
require "openssl/ssl/context"

require "./mappings"
require "./version"

module Discord
  module REST
    SSL_CONTEXT = OpenSSL::SSL::Context::Client.new
    USER_AGENT  = "DiscordBot (https://github.com/meew0/discordcr, #{Discord::VERSION})"

    def request(endpoint_key : Symbol, method : String, url : String | URI, headers : HTTP::Headers, body : String | Nil)
      headers["Authorization"] = @token
      headers["User-Agent"] = USER_AGENT

      spawn { HTTP::Client.exec(method: method, url: url, headers: headers, body: body, tls: SSL_CONTEXT) }
    end

    API_BASE = "https://discordapp.com/api/v6"

    def gateway
      response = request(
        :gateway,
        "GET",
        API_BASE + "/gateway",
        HTTP::Headers.new,
        nil
      )

      # TODO: Investigate failed nil assertion with body_io
      GatewayResponse.from_json(response.body)
    end

    def send_message(channel_id, content)
      response = request(
        :send_message,
        "POST",
        API_BASE + "/channels/#{channel_id}/messages",
        HTTP::Headers{"Content-Type" => "application/json"},
        {content: content}.to_json
      )
    end
  end
end

Remove the fiber spawn in request

require "http/client"
require "openssl/ssl/context"

require "./mappings"
require "./version"

module Discord
  module REST
    SSL_CONTEXT = OpenSSL::SSL::Context::Client.new
    USER_AGENT  = "DiscordBot (https://github.com/meew0/discordcr, #{Discord::VERSION})"

    def request(endpoint_key : Symbol, method : String, url : String | URI, headers : HTTP::Headers, body : String | Nil)
      headers["Authorization"] = @token
      headers["User-Agent"] = USER_AGENT

      HTTP::Client.exec(method: method, url: url, headers: headers, body: body, tls: SSL_CONTEXT)
    end

    API_BASE = "https://discordapp.com/api/v6"

    def gateway
      response = request(
        :gateway,
        "GET",
        API_BASE + "/gateway",
        HTTP::Headers.new,
        nil
      )

      # TODO: Investigate failed nil assertion with body_io
      GatewayResponse.from_json(response.body)
    end

    def send_message(channel_id, content)
      response = request(
        :send_message,
        "POST",
        API_BASE + "/channels/#{channel_id}/messages",
        HTTP::Headers{"Content-Type" => "application/json"},
        {content: content}.to_json
      )
    end
  end
end


struct Int
    def <<(count) unsafe_shl count end
    def >>(count) unsafe_shr count end
    def /(other : Int)
        if other == 0
            self
        end
        unsafe_div other
    end
    def times
        i = 0
        while i < self
            yield i
            i += 1
        end
    end
end

Add === override to Int

struct Int
    def <<(count) unsafe_shl count end
    def >>(count) unsafe_shr count end
    def ===(other : Int) self == other end
    def /(other : Int)
        if other == 0
            self
        end
        unsafe_div other
    end
    def times
        i = 0
        while i < self
            yield i
            i += 1
        end
    end
end


require "./../spec_helper"

describe M3U8::MasterPlaylist do
  context "when rendering a playlist" do
    let(streams) { [ M3U8::MasterPlaylist::Stream.new("low.m3u8"),
                     M3U8::MasterPlaylist::Stream.new("medium.m3u8"),
                     M3U8::MasterPlaylist::Stream.new("high.m3u8") ] }
    let(playlist) { M3U8::MasterPlaylist.new(streams) }

    it "inserts the header" do
      expect(playlist.to_s).to match(/#EXTM3U\n/)
    end

    it "inserts the version tag" do
      expect(playlist.to_s).to match(/#EXT-X-VERSION:3\n/)
    end

    context "with streams" do
      it "renders multiple streams" do
        streams.each do |stream|
          expect(playlist.to_s).to match(/#{streams.to_s}/)
        end
      end

      it "allows to add streams on the fly" do
        new_streams = M3U8::MasterPlaylist::Stream.new("another.m3u8")
        playlist.streams << new_streams

        expect(playlist.to_s).to match(/#{new_streams.to_s}/)
      end
    end
  end
end

Add scenario with empty line between header and the rest

require "./../spec_helper"

describe M3U8::MasterPlaylist do
  context "when rendering a playlist" do
    let(streams) { [ M3U8::MasterPlaylist::Stream.new("low.m3u8"),
                     M3U8::MasterPlaylist::Stream.new("medium.m3u8"),
                     M3U8::MasterPlaylist::Stream.new("high.m3u8") ] }
    let(playlist) { M3U8::MasterPlaylist.new(streams) }

    it "inserts the header" do
      expect(playlist.to_s).to match(/#EXTM3U\n/)
    end

    it "inserts the version tag" do
      expect(playlist.to_s).to match(/#EXT-X-VERSION:3\n/)
    end

    it "inserts an empty line between the header and rest" do
      expect(playlist.to_s.lines[2]).to eq("\n")
    end

    context "with streams" do
      it "renders multiple streams" do
        streams.each do |stream|
          expect(playlist.to_s).to match(/#{streams.to_s}/)
        end
      end

      it "allows to add streams on the fly" do
        new_streams = M3U8::MasterPlaylist::Stream.new("another.m3u8")
        playlist.streams << new_streams

        expect(playlist.to_s).to match(/#{new_streams.to_s}/)
      end
    end
  end
end


require "./converters"
require "./user"
require "./channel"

module Discord
  module Gateway
    struct ReadyPayload
      JSON.mapping(
        v: UInt8,
        user: User,
        private_channels: Array(PrivateChannel),
        guilds: Array(UnavailableGuild),
        session_id: String
      )
    end

    struct HelloPayload
      JSON.mapping(
        heartbeat_interval: UInt32,
        _trace: Array(String)
      )
    end
  end
end

Add a mapping for the GUILD_DELETE payload

require "./converters"
require "./user"
require "./channel"

module Discord
  module Gateway
    struct ReadyPayload
      JSON.mapping(
        v: UInt8,
        user: User,
        private_channels: Array(PrivateChannel),
        guilds: Array(UnavailableGuild),
        session_id: String
      )
    end

    struct HelloPayload
      JSON.mapping(
        heartbeat_interval: UInt32,
        _trace: Array(String)
      )
    end

    struct GuildDeletePayload
      JSON.mapping(
        id: {type: UInt64, converter: SnowflakeConverter},
        unavailable: {type: Bool, nilable: true}
      )
    end
  end
end


require "./mappings/*"

module Discord
  class Cache
    def initialize(@client : Client)
      @users = Hash(UInt64, User).new
    end

    def resolve_user(id : UInt64) : User
      @users.fetch(id) { @users[id] = @client.get_user(id) }
    end

    def cache(user : User)
      @users[user.id] = user
    end
  end
end

Add support for channel caching

require "./mappings/*"

module Discord
  class Cache
    def initialize(@client : Client)
      @users = Hash(UInt64, User).new
      @channels = Hash(UInt64, Channel).new
    end

    def resolve_user(id : UInt64) : User
      @users.fetch(id) { @users[id] = @client.get_user(id) }
    end

    def resolve_channel(id : UInt64) : Channel
      @channels.fetch(id) { @channels[id] = @client.get_channel(id) }
    end

    def cache(user : User)
      @users[user.id] = user
    end

    def cache(channel : Channel)
      @channels[channel.id] = channel
    end
  end
end


class Kemal::CommonExceptionHandler < HTTP::Handler
  INSTANCE = new

  def call(context)
    begin
      call_next context
    rescue ex : Kemal::Exceptions::RouteNotFound
      context.response.content_type = "text/html"
      Kemal.config.logger.write("Exception: #{ex.inspect_with_backtrace.colorize(:red)}\n")
      return render_404(context)
    rescue ex
      context.response.content_type = "text/html"
      Kemal.config.logger.write("Exception: #{ex.inspect_with_backtrace.colorize(:red)}\n")
      verbosity = Kemal.config.env == "production" ? false : true
      return render_500(context, ex.inspect_with_backtrace, verbosity)
    end
  end
end

Remove colorize from common exception handler

class Kemal::CommonExceptionHandler < HTTP::Handler
  INSTANCE = new

  def call(context)
    begin
      call_next context
    rescue ex : Kemal::Exceptions::RouteNotFound
      context.response.content_type = "text/html"
      Kemal.config.logger.write("Exception: #{ex.inspect_with_backtrace}\n")
      return render_404(context)
    rescue ex
      context.response.content_type = "text/html"
      Kemal.config.logger.write("Exception: #{ex.inspect_with_backtrace}\n")
      verbosity = Kemal.config.env == "production" ? false : true
      return render_500(context, ex.inspect_with_backtrace, verbosity)
    end
  end
end


require "http/web_socket"

require "./rest"

module Discordcr
  class Client
    include REST

    def initialize(@token : String, @client_id : UInt64)
    end

    def run
      url = gateway.url
      url += "?v=6&encoding=json"
      @websocket = HTTP::WebSocket.new(URI.parse(url))
      @websocket.not_nil!.on_message(&->on_message(String)) # TODO: better error handling
      @websocket.not_nil!.run
    end

    private def on_message(message : String)
    end
  end
end

Use a local variant of the websocket to avoid the not_nil! checks

require "http/web_socket"

require "./rest"

module Discordcr
  class Client
    include REST

    def initialize(@token : String, @client_id : UInt64)
    end

    def run
      url = gateway.url
      url += "?v=6&encoding=json"
      @websocket = websocket = HTTP::WebSocket.new(URI.parse(url))
      websocket.on_message(&->on_message(String))
      websocket.run
    end

    private def on_message(message : String)
    end
  end
end


require "../spec_helper"

describe Crow do
  it "should convert simple case statements" do
    crystal = <<-CR
    value = "foo"
    case value
    when "foo"
      p "yes"
    when "bar"
      p "no"
    else
      p "hiyaa"
    end
    CR

    flow = <<-JS
    const value = "foo";
    switch (value) {
    case "foo":
      console.log("yes");
      break;
    case "bar":
      console.log("no");
      break;
    default:
      console.log("hiyaa");
      break;
    }
    JS

    Crow.convert(crystal).should eq flow
  end

  it "should raise an exception when conditionless case statements are found" do
    crystal = <<-CR
    case
    when true
      p "yes"
    when false
      p "no"
    else
      p "hiyaa"
    end
    CR

    exception_raised = false

    begin
      Crow.convert(crystal)
    rescue e
      exception_raised = true
      e.message.should match /Case statements without conditions cannot be transpiled./
      e.message.should match %r{See https://github.com/geppetto-apps/crow/issues/8/}
    ensure
      Crow.strict = false
    end

    exception_raised.should eq true
  end
end

Add spec for missing else in case statement

require "../spec_helper"

describe Crow do
  it "should convert simple case statements" do
    crystal = <<-CR
    value = "foo"
    case value
    when "foo"
      p "yes"
    when "bar"
      p "no"
    else
      p "hiyaa"
    end
    CR

    flow = <<-JS
    const value = "foo";
    switch (value) {
    case "foo":
      console.log("yes");
      break;
    case "bar":
      console.log("no");
      break;
    default:
      console.log("hiyaa");
      break;
    }
    JS

    Crow.convert(crystal).should eq flow
  end

  it "should support case statements without default" do
    crystal = <<-CR
    value = "foo"
    case value
    when "foo"
      p "yes"
    when "bar"
      p "no"
    end
    CR

    flow = <<-JS
    const value = "foo";
    switch (value) {
    case "foo":
      console.log("yes");
      break;
    case "bar":
      console.log("no");
      break;
    }
    JS

    Crow.convert(crystal).should eq flow
  end

  it "should raise an exception when conditionless case statements are found" do
    crystal = <<-CR
    case
    when true
      p "yes"
    when false
      p "no"
    else
      p "hiyaa"
    end
    CR

    exception_raised = false

    begin
      Crow.convert(crystal)
    rescue e
      exception_raised = true
      e.message.should match /Case statements without conditions cannot be transpiled./
      e.message.should match %r{See https://github.com/geppetto-apps/crow/issues/8/}
    ensure
      Crow.strict = false
    end

    exception_raised.should eq true
  end
end


module Indexable(T)
  abstract def size
  abstract def unsafe_at(index : Int)

  def at(index : Int)
    index = check_index_out_of_bounds index do
      return yield
    end
    unsafe_at index
  end

  @[AlwaysInline]
  def at(index : Int)
    at(index) { panic "Invalid index!" }
  end

  @[AlwaysInline]
  def [](index : Int)
    at index
  end

  @[AlwaysInline]
  def []?(index : Int)
    at index { nil }
  end

  @[AlwaysInline]
  def []=(index : Int, value : T)
    index = check_index_out_of_bounds index
    @buffer[index] = value
  end

  def each
    each_index do |i|
      yield unsafe_at i
    end
  end

  def each_index
    i = 0
    while i < size
      yield i
      i += 1
    end
    self
  end

  def empty?
    size == 0
  end

  private def check_index_out_of_bounds(index)
    check_index_out_of_bounds(index) { panic "Index out of bounds!" }
  end

  private def check_index_out_of_bounds(index)
    index += size if index < 0
    if 0 <= index < size
      index
    else
      yield
    end
  end
end

Fix a bug in Indexable(T) This fixes a bug where calling `Indexable(T).[]?` wouldn't compile.

module Indexable(T)
  abstract def size
  abstract def unsafe_at(index : Int)

  def at(index : Int)
    index = check_index_out_of_bounds index do
      return yield
    end
    unsafe_at index
  end

  @[AlwaysInline]
  def at(index : Int)
    at(index) { panic "Invalid index!" }
  end

  @[AlwaysInline]
  def [](index : Int)
    at index
  end

  @[AlwaysInline]
  def []?(index : Int)
    at(index) { nil }
  end

  @[AlwaysInline]
  def []=(index : Int, value : T)
    index = check_index_out_of_bounds index
    @buffer[index] = value
  end

  def each
    each_index do |i|
      yield unsafe_at i
    end
  end

  def each_index
    i = 0
    while i < size
      yield i
      i += 1
    end
    self
  end

  def empty?
    size == 0
  end

  private def check_index_out_of_bounds(index)
    check_index_out_of_bounds(index) { panic "Index out of bounds!" }
  end

  private def check_index_out_of_bounds(index)
    index += size if index < 0
    if 0 <= index < size
      index
    else
      yield
    end
  end
end


require "./hooks_dsl"
require "./config_dsl"

module Soil
  class App
    extend HooksDSL
    extend RoutingDSL
    extend ConfigDSL

    @@namespace = ""
    @@before_hooks = [] of Http::Request, Http::Response ->
    @@after_hooks = [] of Http::Request, Http::Response ->
    @@router = Router.new
    @@config = Config.new
    @@logger = Logger.new(STDOUT)

    def initialize
      @server = HTTP::Server.new(
        @@config.host,
        @@config.port,
        [
          HTTP::LogHandler.new,
          HTTP::ErrorHandler.new,
          Http::MainHandler.new(self.class)
        ]
      )
    end

    def self.routes
      @@router.routes
    end

    def self.wrap_within_self_callables(callables)
      before_hooks + callables + after_hooks
    end

    def self.mount_route(method : String, path : String, callables)
      path = [@@namespace, path].join("/")
      all_callables = wrap_within_self_callables(callables)
      @@router.add(method, path, all_callables)
    end

    def self.find(request)
      @@router.find(request)
    end

    def self.namespace(name)
      @@namespace = name
    end

    def self.configuration
      @@config
    end

    def self.logger
      @@logger
    end

    def run
      self.class.logger.info(
        "An app emerged from soil running on #{self.class.configuration.host}:#{@server.port}"
      )
      @server.listen
    end
  end
end

Add class method to simplify boot

require "./hooks_dsl"
require "./config_dsl"

module Soil
  class App
    extend HooksDSL
    extend RoutingDSL
    extend ConfigDSL

    @@namespace = ""
    @@before_hooks = [] of Http::Request, Http::Response ->
    @@after_hooks = [] of Http::Request, Http::Response ->
    @@router = Router.new
    @@config = Config.new
    @@logger = Logger.new(STDOUT)

    def initialize
      @server = HTTP::Server.new(
        @@config.host,
        @@config.port,
        [
          HTTP::LogHandler.new,
          HTTP::ErrorHandler.new,
          Http::MainHandler.new(self.class)
        ]
      )
    end

    def self.routes
      @@router.routes
    end

    def self.wrap_within_self_callables(callables)
      before_hooks + callables + after_hooks
    end

    def self.mount_route(method : String, path : String, callables)
      path = [@@namespace, path].join("/")
      all_callables = wrap_within_self_callables(callables)
      @@router.add(method, path, all_callables)
    end

    def self.find(request)
      @@router.find(request)
    end

    def self.namespace(name)
      @@namespace = name
    end

    def self.configuration
      @@config
    end

    def self.logger
      @@logger
    end

    def self.run
      app = self.new
      app.run
    end

    def run
      self.class.logger.info(
        "An app emerged from soil running on #{self.class.configuration.host}:#{@server.port}"
      )
      @server.listen
    end
  end
end


abstract class MainLayout
  include LuckyWeb::HTMLPage
  include Shared::FieldErrorsComponent
  include Shared::FlashComponent

  # You can put things here that all pages need
  #
  # Example:
  #   needs current_user : User
  needs flash : LuckyWeb::Flash::Store

  abstract def inner

  render do
    html_doctype

    html lang: "en" do
      head do
        utf8_charset
        title page_title
        css_link asset("css/app.css")
        js_link asset("js/app.js")
      end

      body do
        render_flash
        inner
      end
    end
  end

  def page_title
    "Welcome to Lucky"
  end
end

Use render method instead of block in app skeleton

abstract class MainLayout
  include LuckyWeb::HTMLPage
  include Shared::FieldErrorsComponent
  include Shared::FlashComponent

  # You can put things here that all pages need
  #
  # Example:
  #   needs current_user : User
  needs flash : LuckyWeb::Flash::Store

  abstract def inner

  def render
    html_doctype

    html lang: "en" do
      head do
        utf8_charset
        title page_title
        css_link asset("css/app.css")
        js_link asset("js/app.js")
      end

      body do
        render_flash
        inner
      end
    end
  end

  def page_title
    "Welcome to Lucky"
  end
end


abstract class BaseAction < LuckyWeb::Action
  include LuckyWeb::Exposeable

  macro inherited
    # If something should always be exposed to your pages, expose them here.
    #
    # Example:
    #
    #  macro inherited
    #    expose :current_user
    #  end
    #
    #  def current_user
    #    find_the_user...
    #  end
    #
    # Then add an assign for it in your BasePage
  end
end

Make exposures work with latest LuckyWeb

abstract class BaseAction < LuckyWeb::Action
  # If something should always be exposed to your pages, expose them here.
  #
  # Example:
  #
  #  expose current_user
  #
  #  def current_user
  #    find_the_user...
  #  end
  #
  # Then add an assign for it in your BasePage
end


require "http/client"

module Discordcr
  module REST
    def request(endpoint_key : Symbol, method : String, url: String | URI, headers : HTTP::Headers | Nil, body : String | Nil)
      HTTP::Client.exec(method: method, url: url, headers: headers, body: body, tls: true)
    end
  end
end

Fix a syntax error in the request parameters

require "http/client"

module Discordcr
  module REST
    def request(endpoint_key : Symbol, method : String, url : String | URI, headers : HTTP::Headers | Nil, body : String | Nil)
      HTTP::Client.exec(method: method, url: url, headers: headers, body: body, tls: true)
    end
  end
end


require "json"
require "time/format"

module Discord
  module SnowflakeConverter
    def self.from_json(parser : JSON::PullParser) : UInt64
      str = parser.read_string_or_null
      str.not_nil!.to_u64
    end

    def self.to_json(value : UInt64, io : IO)
      io.puts(value.to_s)
    end
  end

  # Converts a value that may be a snowflake, but might also be nil, to a
  # UInt64.
  module MaybeSnowflakeConverter
    def self.from_json(parser : JSON::PullParser) : UInt64?
      str = parser.read_string_or_null

      if str
        str.to_u64
      else
        nil
      end
    end

    def self.to_json(value : UInt64?, io : IO)
      if value
        io.puts(value.to_s)
      else
        io.puts("null")
      end
    end
  end

  # Converts an array of strings to an array of UInt64s.
  module SnowflakeArrayConverter
    def self.from_json(parser : JSON::PullParser) : Array(UInt64)
      Array(String).new(parser).map &.to_u64
    end

    def self.to_json(value : Array(UInt64), io : IO)
      value.map(&.to_s).to_json(io)
    end
  end
end

Remove an unnecessary not_nil! from SnowflakeConverter

require "json"
require "time/format"

module Discord
  module SnowflakeConverter
    def self.from_json(parser : JSON::PullParser) : UInt64
      parser.read_string.to_u64
    end

    def self.to_json(value : UInt64, io : IO)
      io.puts(value.to_s)
    end
  end

  # Converts a value that may be a snowflake, but might also be nil, to a
  # UInt64.
  module MaybeSnowflakeConverter
    def self.from_json(parser : JSON::PullParser) : UInt64?
      str = parser.read_string_or_null

      if str
        str.to_u64
      else
        nil
      end
    end

    def self.to_json(value : UInt64?, io : IO)
      if value
        io.puts(value.to_s)
      else
        io.puts("null")
      end
    end
  end

  # Converts an array of strings to an array of UInt64s.
  module SnowflakeArrayConverter
    def self.from_json(parser : JSON::PullParser) : Array(UInt64)
      Array(String).new(parser).map &.to_u64
    end

    def self.to_json(value : Array(UInt64), io : IO)
      value.map(&.to_s).to_json(io)
    end
  end
end