fixed failing test

fixed failing test

require 'cupoftea'
require './assertions.pogo'

parse = require './parser.pogo': parse

assume @term is module with statements @action =
    if (term: is module)
        action (term: statements)
    else
        throw (new (Error ('expected module, but found ' + term)))

assume @statements has just one statement @action =
    if (statements: statements: length == 1)
        action (statements: statements: 0)
    else
        throw (new (Error ('expected statements to have just one statement, found ' + statements: statements: length)))

expression @source =
    term = parse @source
    assume @term is module with statements #statements
        assume @statements has just one statement #s
            s

spec 'parser'
    spec 'terminals'
        spec 'integer'
            (expression '5') should contain fields {
                integer 5
            }

        spec 'float'
            (expression '5.6') should contain fields {
                float 5.6
            }

        spec 'variables'
            spec 'simple'
                (expression 'total weight') should contain fields {
                    variable ['total'. 'weight']
                }

        spec 'strings'
            spec 'simple string'
                (expression '''a string''') should contain fields {
                    is string
                    string 'a string'
                }

            spec 'string with single quotes'
                (expression '''''''alright!'''' he said''') should contain fields {
                    is string
                    string '''alright!'' he said'
                }

            spec 'string with backslash'
                (expression "'one \\ two'") should contain fields {
                    is string
                    string "one \\ two"
                }

        spec 'interpolated strings'
            spec 'simple'
                (expression '"a string"') should contain fields {
                    is interpolated string
                    components [
                        {string 'a string'}
                    ]
                }

            spec 'with newline'
                (expression '"one\ntwo"') should contain fields {
                    is interpolated string
                    components [
                        {string "one\ntwo"}
                    ]
                }

            spec 'with indentation'
                (expression '"one\n  two"') should contain fields {
                    is interpolated string
                    components [
                        {string "one\n two"}
                    ]
                }

            spec 'null string'
                (expression '""') should contain fields {
                    is interpolated string
                    components [
                    ]
                }

            spec 'with single identifier variable'
                (expression '"a boat @length meters in length"') should contain fields {
                    is interpolated string
                    components [
                        {string 'a boat '}
                        {variable ['length']}
                        {string ' meters in length'}
                    ]
                }

            spec 'with single variable expression'
                (expression '"a boat @(boat length) meters in length"') should contain fields {
                    is interpolated string
                    components [
                        {string 'a boat '}
                        {variable ['boat'. 'length']}
                        {string ' meters in length'}
                    ]
                }

            spec 'with complex expression'
                (expression '"a boat @(lookup boat length from (boat database)) meters in length"') should contain fields {
                    is interpolated string
                    components [
                        {string 'a boat '}
                        {
                            function {variable ['lookup'. 'boat'. 'length'. 'from']}
                            arguments [{variable ['boat'. 'database']}]
                        }
                        {string ' meters in length'}
                    ]
                }

            spec 'with inner interpolation'
                (expression '"a boat @("@(boat length) meters") in length"') should contain fields {
                    is interpolated string
                    components [
                        {string 'a boat '}
                        {
                            is interpolated string
                            components [
                                {variable ['boat'. 'length']}
                                {string ' meters'}
                            ]
                        }
                        {string ' in length'}
                    ]
                }

        spec 'lists'
            spec 'empty'
                (expression '[]') should contain fields {
                    is list
                    items []
                }

            spec 'one item'
                (expression '[1]') should contain fields {
                    is list
                    items [{integer 1}]
                }

            spec 'two items'
                (expression '[1. 2]') should contain fields {
                    is list
                    items [
                        {integer 1}
                        {integer 2}
                    ]
                }

        spec 'hashes'
            spec 'empty hash'
                (expression '{}') should contain fields {
                    is hash
                    entries []
                }

            spec 'hash with one entry'
                (expression '{port 1234}') should contain fields {
                    is hash
                    entries [
                        {
                            field ['port']
                            value {integer 1234}
                        }
                    ]
                }

            spec 'hash with two entries'
                (expression '{port 1234, ip address ''1.1.1.1''}') should contain fields {
                    is hash
                    entries [
                        {
                            field ['port']
                            value {integer 1234}
                        }
                        {
                            field ['ip'. 'address']
                            value {string '1.1.1.1'}
                        }
                    ]
                }

            spec 'hash with two entries on different lines'
                (expression '{port 1234. ip address ''1.1.1.1''}') should contain fields {
                    is hash
                    entries [
                        {
                            field ['port']
                            value {integer 1234}
                        }
                        {
                            field ['ip'. 'address']
                            value {string '1.1.1.1'}
                        }
                    ]
                }

            spec 'hash with true entry'
                (expression '{port 1234, readonly}') should contain fields {
                    is hash
                    entries [
                        {
                            field ['port']
                            value {integer 1234}
                        }
                        {
                            field ['readonly']
                            value @undefined
                        }
                    ]
                }

    spec 'function calls'
        spec 'function call'
            (expression 'touch @file') should contain fields {
                function {variable ['touch']}
                arguments [{variable ['file']}]
            }

        spec 'function call with self argument'
            (expression 'touch @:file') should contain fields {
                function {variable ['touch']}
                arguments [
                    {
                        is field reference
                        object {variable ['self']}
                        name ['file']
                    }
                ]
            }

        spec 'function call with splat argument'
            (expression 'touch @files ...') should contain fields {
                function {variable ['touch']}
                arguments [
                  {variable ['files']}
                  {is splat}
                ]
            }

        spec 'function call with no argument'
            (expression 'delete everything!') should contain fields {
                function {variable ['delete'. 'everything']}
                arguments []
            }

        spec 'function call with block with parameters'
            (expression "with file \@file #stream\n  stream") should contain fields {
                function {variable ['with'. 'file']}
                arguments [
                    {variable ['file']}
                    {
                        body {statements [{variable ['stream']}]}
                        parameters [{
                            is parameter
                            expression {variable ['stream']}
                        }]
                    }
                ]
            }

        spec 'function call with block with long parameters'
            (expression "open database #(database connection)\n  database connection") should contain fields {
                function {variable ['open'. 'database']}
                arguments [
                    {
                        parameters [
                            {
                                is parameter
                                expression {variable ['database'. 'connection']}
                            }
                        ]
                        body {statements [{variable ['database'. 'connection']}]}
                    }
                ]
            }

        spec 'function call with two blocks with parameters'
            (expression 'name #x @{x} #y @ {y}') should contain fields {
                function {variable ['name']}
                arguments [
                    {
                        body {statements [{variable ['x']}]}
                        parameters [{
                            is parameter
                            expression {variable ['x']}
                        }]
                    }
                    {
                        body {statements [{variable ['y']}]}
                        parameters [{
                            is parameter
                            expression {variable ['y']}
                        }]
                    }
                ]
            }

        spec 'function call with two optional arguments'
            (expression 'name @a, port 34, server @s') should contain fields {
                function {variable ['name']}
                arguments [
                    {variable ['a']}
                ]
                optional arguments [
                    {
                        field ['port']
                        value {integer 34}
                    }
                    {
                        field ['server']
                        value {variable ['s']}
                    }
                ]
            }

        spec 'function call with no arguments and one optional argument'
            (expression 'start server, port 34') should contain fields {
                function {variable ['start'. 'server']}
                arguments []
                optional arguments [
                    {
                        field ['port']
                        value {integer 34}
                    }
                ]
            }

    spec 'object operations'
        spec 'method call'
            (expression 'object: method @argument') should contain fields {
                is method call
                object {variable ['object']}
                name ['method']
                arguments [{variable ['argument']}]
            }

        spec 'method call with optional arguments'
            (expression 'object: method @argument, view @view') should contain fields {
                is method call
                object {variable ['object']}
                name ['method']
                arguments [{variable ['argument']}]
                optional arguments [
                    {field ['view'], value {variable ['view']}}
                ]
            }

        spec 'field reference'
            (expression 'object: field') should contain fields {
                is field reference
                object {variable ['object']}
                name ['field']
            }

        spec 'field reference with newline'
            (expression "object:\nfield") should contain fields {
                is field reference
                object {variable ['object']}
                name ['field']
            }

        spec 'self field reference'
            (expression ':field') should contain fields {
                is field reference
                object {variable ['self']}
                name ['field']
            }

        spec 'indexer'
            (expression 'object: @x') should contain fields {
                is indexer
                object {variable ['object']}
                indexer {variable ['x']}
            }

    spec 'blocks'
        spec 'empty block'
            (expression '@{}') should contain fields {
                is block
                parameters []
                redefines self @false
                body {statements []}
            }

        spec 'block'
            (expression '@{x.y}') should contain fields {
                is block
                parameters []
                redefines self @false
                body {statements [
                    {variable ['x']}
                    {variable ['y']}
                ]}
            }

        spec 'block with parameter'
            (expression "#x\n  x.y") should contain fields {
                is block
                parameters [{is parameter, expression {variable ['x']}}]
                redefines self @false
                body {
                    statements [
                        {variable ['x']}
                        {variable ['y']}
                    ]
                }
            }

        spec 'block with parameter, redefining self'
            (expression '#x => @{x.y}') should contain fields {
                is block
                parameters [{is parameter, expression {variable ['x']}}]
                redefines self @true
                body {
                    statements [
                        {variable ['x']}
                        {variable ['y']}
                    ]
                }
            }

    spec 'operators'
        spec 'should be lower precedence than object operation'
            (expression 'o:m 2 +- o:x') should contain fields {
                is method call
                object {
                    is method call
                    object {variable ['o']}
                    name ['m']
                    arguments [{integer 2}]
                }

                name ['+-']
                arguments [
                    {
                        is field reference
                        object {variable ['o']}
                        name ['x']
                    }
                ]
            }

        spec 'parses backslash'
            (expression "2 +\\+ 1") should contain fields {
                is method call
                object {integer 2}

                name ["+\\+"]
                arguments [
                    {integer 1}
                ]
            }

        spec 'unary operators should be higher precedence than binary operators'
            (expression 'a && ! b') should contain fields {
                is operator
                operator '&&'

                arguments [
                    {variable ['a']}
                    {
                        is operator
                        operator '!'
                        arguments [{variable ['b']}]
                    }
                ]
            }

        spec 'can have newlines immediately after operator'
            (expression "a &&\nb") should contain fields {
                is operator
                operator '&&'

                arguments [
                    {variable ['a']}
                    {variable ['b']}
                ]
            }

    spec 'assignment'
        spec 'assignment'
            (expression 'x = y') should contain fields {
                is definition
                target {variable ['x']}
                source {variable ['y']}
            }

        spec 'function definition'
            spec 'function with one parameter'
                (expression 'func @x = x') should contain fields {
                    is definition
                    target {variable ['func']}
                    source {
                        parameters [{is parameter, expression {variable ['x']}}]
                        body {statements [{variable ['x']}]}
                    }
                }

            spec 'function with one parameter, and one optional parameter'
                (expression 'func @x, port 80 = x') should contain fields {
                    is definition
                    target {variable ['func']}
                    source {
                        parameters [{is parameter, expression {variable ['x']}}]
                        optional parameters [{field ['port'], value {integer 80}}]
                        body {statements [{variable ['x']}]}
                    }
                }

        spec 'field assignment'
            (expression 'o: x = y') should contain fields {
                is definition
                target {
                    is field reference
                    object {variable ['o']}
                    name ['x']
                }

                source {variable ['y']}
            }

        spec 'index assignment'
            (expression 'o: @x = y') should contain fields {
                is definition
                target {
                    is indexer
                    object {variable ['o']}
                    indexer {variable ['x']}
                }

                source {variable ['y']}
            }

        spec 'assignment from field'
            (expression 'x = y: z') should contain fields {
                is definition
                target {variable ['x']}
                source {
                    is field reference
                    object {
                        variable ['y']
                    }

                    name ['z']
                }
            }

        spec 'assignment of command'
            (expression 'x! = 8') should contain fields {
                is definition
                target {variable ['x']}
                source {
                    is block
                    parameters []
                    body {
                        statements [{integer 8}]
                    }
                }
            }

        spec 'assignment of query'
            (expression 'x? = 8') should contain fields {
                is definition
                target {variable ['x']}
                source {
                    is block
                    parameters []
                    body {
                        statements [{integer 8}]
                    }
                }
            }

        spec 'assignment from method call'
            (expression 'x = y: z @a') should contain fields {
                is definition
                target {variable ['x']}
                source {
                    is method call
                    object {
                        variable ['y']
                    }

                    name ['z']
                    arguments [{variable ['a']}]
                }
            }

        spec 'field assignment from method call'
            (expression 'i: x = y: z @a') should contain fields {
                is definition
                target {
                    is field reference
                    object {variable ['i']}
                    name ['x']
                }

                source {
                    is method call
                    object {
                        variable ['y']
                    }

                    name ['z']
                    arguments [{variable ['a']}]
                }
            }

require 'cupoftea'
require './assertions.pogo'

parse = require './parser.pogo': parse
preparse = require './preparser.pogo': new file parser?

assume @term is module with statements @action =
    if (term: is module)
        action (term: statements)
    else
        throw (new (Error ('expected module, but found ' + term)))

assume @statements has just one statement @action =
    if (statements: statements: length == 1)
        action (statements: statements: 0)
    else
        throw (new (Error ('expected statements to have just one statement, found ' + statements: statements: length)))

expression @source =
    term = parse (preparse @source)
    assume @term is module with statements #statements
        assume @statements has just one statement #s
            s

spec 'parser'
    spec 'terminals'
        spec 'integer'
            (expression '5') should contain fields {
                integer 5
            }

        spec 'float'
            (expression '5.6') should contain fields {
                float 5.6
            }

        spec 'variables'
            spec 'simple'
                (expression 'total weight') should contain fields {
                    variable ['total'. 'weight']
                }

        spec 'strings'
            spec 'simple string'
                (expression '''a string''') should contain fields {
                    is string
                    string 'a string'
                }

            spec 'string with single quotes'
                (expression '''''''alright!'''' he said''') should contain fields {
                    is string
                    string '''alright!'' he said'
                }

            spec 'string with backslash'
                (expression "'one \\ two'") should contain fields {
                    is string
                    string "one \\ two"
                }

        spec 'interpolated strings'
            spec 'simple'
                (expression '"a string"') should contain fields {
                    is interpolated string
                    components [
                        {string 'a string'}
                    ]
                }

            spec 'with newline'
                (expression '"one\ntwo"') should contain fields {
                    is interpolated string
                    components [
                        {string "one\ntwo"}
                    ]
                }

            spec 'with indentation'
                (expression '"one\n  two"') should contain fields {
                    is interpolated string
                    components [
                        {string "one\n  two"}
                    ]
                }

            spec 'null string'
                (expression '""') should contain fields {
                    is interpolated string
                    components [
                    ]
                }

            spec 'with single identifier variable'
                (expression '"a boat @length meters in length"') should contain fields {
                    is interpolated string
                    components [
                        {string 'a boat '}
                        {variable ['length']}
                        {string ' meters in length'}
                    ]
                }

            spec 'with single variable expression'
                (expression '"a boat @(boat length) meters in length"') should contain fields {
                    is interpolated string
                    components [
                        {string 'a boat '}
                        {variable ['boat'. 'length']}
                        {string ' meters in length'}
                    ]
                }

            spec 'with complex expression'
                (expression '"a boat @(lookup boat length from (boat database)) meters in length"') should contain fields {
                    is interpolated string
                    components [
                        {string 'a boat '}
                        {
                            function {variable ['lookup'. 'boat'. 'length'. 'from']}
                            arguments [{variable ['boat'. 'database']}]
                        }
                        {string ' meters in length'}
                    ]
                }

            spec 'with inner interpolation'
                (expression '"a boat @("@(boat length) meters") in length"') should contain fields {
                    is interpolated string
                    components [
                        {string 'a boat '}
                        {
                            is interpolated string
                            components [
                                {variable ['boat'. 'length']}
                                {string ' meters'}
                            ]
                        }
                        {string ' in length'}
                    ]
                }

        spec 'lists'
            spec 'empty'
                (expression '[]') should contain fields {
                    is list
                    items []
                }

            spec 'one item'
                (expression '[1]') should contain fields {
                    is list
                    items [{integer 1}]
                }

            spec 'two items'
                (expression '[1. 2]') should contain fields {
                    is list
                    items [
                        {integer 1}
                        {integer 2}
                    ]
                }

        spec 'hashes'
            spec 'empty hash'
                (expression '{}') should contain fields {
                    is hash
                    entries []
                }

            spec 'hash with one entry'
                (expression '{port 1234}') should contain fields {
                    is hash
                    entries [
                        {
                            field ['port']
                            value {integer 1234}
                        }
                    ]
                }

            spec 'hash with two entries'
                (expression '{port 1234, ip address ''1.1.1.1''}') should contain fields {
                    is hash
                    entries [
                        {
                            field ['port']
                            value {integer 1234}
                        }
                        {
                            field ['ip'. 'address']
                            value {string '1.1.1.1'}
                        }
                    ]
                }

            spec 'hash with two entries on different lines'
                (expression '{port 1234. ip address ''1.1.1.1''}') should contain fields {
                    is hash
                    entries [
                        {
                            field ['port']
                            value {integer 1234}
                        }
                        {
                            field ['ip'. 'address']
                            value {string '1.1.1.1'}
                        }
                    ]
                }

            spec 'hash with true entry'
                (expression '{port 1234, readonly}') should contain fields {
                    is hash
                    entries [
                        {
                            field ['port']
                            value {integer 1234}
                        }
                        {
                            field ['readonly']
                            value @undefined
                        }
                    ]
                }

    spec 'function calls'
        spec 'function call'
            (expression 'touch @file') should contain fields {
                function {variable ['touch']}
                arguments [{variable ['file']}]
            }

        spec 'function call with self argument'
            (expression 'touch @:file') should contain fields {
                function {variable ['touch']}
                arguments [
                    {
                        is field reference
                        object {variable ['self']}
                        name ['file']
                    }
                ]
            }

        spec 'function call with splat argument'
            (expression 'touch @files ...') should contain fields {
                function {variable ['touch']}
                arguments [
                  {variable ['files']}
                  {is splat}
                ]
            }

        spec 'function call with no argument'
            (expression 'delete everything!') should contain fields {
                function {variable ['delete'. 'everything']}
                arguments []
            }

        spec 'function call with block with parameters'
            (expression "with file \@file #stream\n  stream") should contain fields {
                function {variable ['with'. 'file']}
                arguments [
                    {variable ['file']}
                    {
                        body {statements [{variable ['stream']}]}
                        parameters [{
                            is parameter
                            expression {variable ['stream']}
                        }]
                    }
                ]
            }

        spec 'function call with block with long parameters'
            (expression "open database #(database connection)\n  database connection") should contain fields {
                function {variable ['open'. 'database']}
                arguments [
                    {
                        parameters [
                            {
                                is parameter
                                expression {variable ['database'. 'connection']}
                            }
                        ]
                        body {statements [{variable ['database'. 'connection']}]}
                    }
                ]
            }

        spec 'function call with two blocks with parameters'
            (expression 'name #x @{x} #y @ {y}') should contain fields {
                function {variable ['name']}
                arguments [
                    {
                        body {statements [{variable ['x']}]}
                        parameters [{
                            is parameter
                            expression {variable ['x']}
                        }]
                    }
                    {
                        body {statements [{variable ['y']}]}
                        parameters [{
                            is parameter
                            expression {variable ['y']}
                        }]
                    }
                ]
            }

        spec 'function call with two optional arguments'
            (expression 'name @a, port 34, server @s') should contain fields {
                function {variable ['name']}
                arguments [
                    {variable ['a']}
                ]
                optional arguments [
                    {
                        field ['port']
                        value {integer 34}
                    }
                    {
                        field ['server']
                        value {variable ['s']}
                    }
                ]
            }

        spec 'function call with no arguments and one optional argument'
            (expression 'start server, port 34') should contain fields {
                function {variable ['start'. 'server']}
                arguments []
                optional arguments [
                    {
                        field ['port']
                        value {integer 34}
                    }
                ]
            }

    spec 'object operations'
        spec 'method call'
            (expression 'object: method @argument') should contain fields {
                is method call
                object {variable ['object']}
                name ['method']
                arguments [{variable ['argument']}]
            }

        spec 'method call with optional arguments'
            (expression 'object: method @argument, view @view') should contain fields {
                is method call
                object {variable ['object']}
                name ['method']
                arguments [{variable ['argument']}]
                optional arguments [
                    {field ['view'], value {variable ['view']}}
                ]
            }

        spec 'field reference'
            (expression 'object: field') should contain fields {
                is field reference
                object {variable ['object']}
                name ['field']
            }

        spec 'field reference with newline'
            (expression "object:\nfield") should contain fields {
                is field reference
                object {variable ['object']}
                name ['field']
            }

        spec 'self field reference'
            (expression ':field') should contain fields {
                is field reference
                object {variable ['self']}
                name ['field']
            }

        spec 'indexer'
            (expression 'object: @x') should contain fields {
                is indexer
                object {variable ['object']}
                indexer {variable ['x']}
            }

    spec 'blocks'
        spec 'empty block'
            (expression '@{}') should contain fields {
                is block
                parameters []
                redefines self @false
                body {statements []}
            }

        spec 'block'
            (expression '@{x.y}') should contain fields {
                is block
                parameters []
                redefines self @false
                body {statements [
                    {variable ['x']}
                    {variable ['y']}
                ]}
            }

        spec 'block with parameter'
            (expression "#x\n  x.y") should contain fields {
                is block
                parameters [{is parameter, expression {variable ['x']}}]
                redefines self @false
                body {
                    statements [
                        {variable ['x']}
                        {variable ['y']}
                    ]
                }
            }

        spec 'block with parameter, redefining self'
            (expression '#x => @{x.y}') should contain fields {
                is block
                parameters [{is parameter, expression {variable ['x']}}]
                redefines self @true
                body {
                    statements [
                        {variable ['x']}
                        {variable ['y']}
                    ]
                }
            }

    spec 'operators'
        spec 'should be lower precedence than object operation'
            (expression 'o:m 2 +- o:x') should contain fields {
                is method call
                object {
                    is method call
                    object {variable ['o']}
                    name ['m']
                    arguments [{integer 2}]
                }

                name ['+-']
                arguments [
                    {
                        is field reference
                        object {variable ['o']}
                        name ['x']
                    }
                ]
            }

        spec 'parses backslash'
            (expression "2 +\\+ 1") should contain fields {
                is method call
                object {integer 2}

                name ["+\\+"]
                arguments [
                    {integer 1}
                ]
            }

        spec 'unary operators should be higher precedence than binary operators'
            (expression 'a && ! b') should contain fields {
                is operator
                operator '&&'

                arguments [
                    {variable ['a']}
                    {
                        is operator
                        operator '!'
                        arguments [{variable ['b']}]
                    }
                ]
            }

        spec 'can have newlines immediately after operator'
            (expression "a &&\nb") should contain fields {
                is operator
                operator '&&'

                arguments [
                    {variable ['a']}
                    {variable ['b']}
                ]
            }

    spec 'assignment'
        spec 'assignment'
            (expression 'x = y') should contain fields {
                is definition
                target {variable ['x']}
                source {variable ['y']}
            }

        spec 'function definition'
            spec 'function with one parameter'
                (expression 'func @x = x') should contain fields {
                    is definition
                    target {variable ['func']}
                    source {
                        parameters [{is parameter, expression {variable ['x']}}]
                        body {statements [{variable ['x']}]}
                    }
                }

            spec 'function with one parameter, and one optional parameter'
                (expression 'func @x, port 80 = x') should contain fields {
                    is definition
                    target {variable ['func']}
                    source {
                        parameters [{is parameter, expression {variable ['x']}}]
                        optional parameters [{field ['port'], value {integer 80}}]
                        body {statements [{variable ['x']}]}
                    }
                }

        spec 'field assignment'
            (expression 'o: x = y') should contain fields {
                is definition
                target {
                    is field reference
                    object {variable ['o']}
                    name ['x']
                }

                source {variable ['y']}
            }

        spec 'index assignment'
            (expression 'o: @x = y') should contain fields {
                is definition
                target {
                    is indexer
                    object {variable ['o']}
                    indexer {variable ['x']}
                }

                source {variable ['y']}
            }

        spec 'assignment from field'
            (expression 'x = y: z') should contain fields {
                is definition
                target {variable ['x']}
                source {
                    is field reference
                    object {
                        variable ['y']
                    }

                    name ['z']
                }
            }

        spec 'assignment of command'
            (expression 'x! = 8') should contain fields {
                is definition
                target {variable ['x']}
                source {
                    is block
                    parameters []
                    body {
                        statements [{integer 8}]
                    }
                }
            }

        spec 'assignment of query'
            (expression 'x? = 8') should contain fields {
                is definition
                target {variable ['x']}
                source {
                    is block
                    parameters []
                    body {
                        statements [{integer 8}]
                    }
                }
            }

        spec 'assignment from method call'
            (expression 'x = y: z @a') should contain fields {
                is definition
                target {variable ['x']}
                source {
                    is method call
                    object {
                        variable ['y']
                    }

                    name ['z']
                    arguments [{variable ['a']}]
                }
            }

        spec 'field assignment from method call'
            (expression 'i: x = y: z @a') should contain fields {
                is definition
                target {
                    is field reference
                    object {variable ['i']}
                    name ['x']
                }

                source {
                    is method call
                    object {
                        variable ['y']
                    }

                    name ['z']
                    arguments [{variable ['a']}]
                }
            }


walking terms specs

walking terms specs

require 'cupoftea'
cg = require '../lib/codeGenerator'
cg new = require './codeGenerator/codeGenerator'
require './assertions.pogo'

spec 'term'
    spec 'subterms'
        term = cg: term =>
            :a = cg: identifier 'a'
            :b = cg: identifier 'b'
            :subterms 'a' 'b'

        (term: all subterms?) should contain fields [
            {identifier 'a'}
            {identifier 'b'}
        ]

    spec 'locations'
        location @fl @ll @fc @lc = {
            first_line @fl
            last_line @ll
            first_column @fc
            last_column @lc
        }

        spec 'location'
            id = cg new: loc (cg: identifier 'a') (location 1 2 3 4)

            (id: location?) should contain fields {
                first line 1
                last line 2
                first column 3
                last column 4
            }

        spec 'subterm location'
            term = cg: term
                this: a = cg new: loc (cg: identifier 'a') (location 1 1 3 10)
                this: b = cg new: loc (cg: identifier 'b') (location 1 1 2 12)
                this: subterms 'a' 'b'

            (term: location?) should contain fields {
                first line 1
                last line 1
                first column 2
                last column 12
            }

    spec 'depth first walk'
        terms @terms ... =
            cg: term =>
                :terms = terms

                :subterms 'terms'

        branch @left @right =
            cg: term =>
                :left = left
                :right = right

                :subterms 'left' 'right'

        leaf @name =
            cg: term =>
                :name = name

        root = terms (branch (leaf 'a') (leaf 'b')) (branch (leaf 'c') (branch (leaf 'd') (leaf 'e')))

        spec 'walks all terms depth first'
            leaf terms = []

            root: walk each subterm #term
                if (term: name)
                    leaf terms: push (term: name)

            (leaf terms) should contain fields ['a'. 'b'. 'c'. 'd'. 'e']

        spec "doesn't walk undefined subterms"
            term = branch (leaf 'a') @undefined

            leaf terms = []

            term: walk each subterm #term
                leaf terms: push @term

            (leaf terms) should contain fields [{name 'a'}]

require 'cupoftea'
cg = require '../lib/codeGenerator'
cg new = require './codeGenerator/codeGenerator'
require './assertions.pogo'

spec 'term'
    spec 'subterms'
        term = cg: term
            this: a = cg: identifier 'a'
            this: b = cg: identifier 'b'
            this: subterms 'a' 'b'

        (term: all subterms?) should contain fields [
            {identifier 'a'}
            {identifier 'b'}
        ]

    spec 'locations'
        location @fl @ll @fc @lc = {
            first_line @fl
            last_line @ll
            first_column @fc
            last_column @lc
        }

        spec 'location'
            id = cg new: loc (cg: identifier 'a') (location 1 2 3 4)

            (id: location?) should contain fields {
                first line 1
                last line 2
                first column 3
                last column 4
            }

        spec 'subterm location'
            term = cg: term
                this: a = cg new: loc (cg: identifier 'a') (location 1 1 3 10)
                this: b = cg new: loc (cg: identifier 'b') (location 1 1 2 12)
                this: subterms 'a' 'b'

            (term: location?) should contain fields {
                first line 1
                last line 1
                first column 2
                last column 12
            }


new syntax for indentStack.pogo

new syntax for indentStack.pogo

require './runtime'

exports.create indent stack = create indent stack () =
    object =>
        self.indents = [0]
        self.indentation regex = new (RegExp '\n( *)$')
        self.multi new line regex = new (RegExp '\n *\n')

        self.is (text) multi new line =
            self.multi new line regex.test (text)

        self.(text) has new line () =
            self.indentation regex.test (text)

        self.indentation (new line) =
            self.indentation regex.exec (new line).1.length

        self.current indentation () =
            self.indents.0

        self.set indentation (text) =
            if (self.(text) has new line ())
                self.indents.unshift 'bracket'
                self.indents.unshift (self.indentation (text))
            else
                current = self.current indentation ()
                self.indents.unshift 'bracket'
                self.indents.unshift (current)

        self.unset indentation () =
            self.indents.shift ()

            tokens = []
            while (self.indents.0 != 'bracket')
                tokens.push '}'
                self.indents.shift ()

            self.indents.shift ()
            tokens

        self.tokens for eof () =
            tokens = []
            indents = self.indents.length

            while (indents > 1)
                tokens.push '}'
                indents = indents - 1

            tokens.push 'eof'

            tokens

        self.tokens for new line (text) =
            if (self.(text) has new line ())
                current indentation = self.current indentation ()
                indentation = self.indentation (text)

                if (current indentation == indentation)
                    [',']
                else if (current indentation < indentation)
                    self.indents.unshift (indentation)
                    ['@{']
                else
                    tokens = []

                    while (self.indents.0 > indentation)
                        tokens.push '}'
                        self.indents.shift ()

                    if (self.is (text) multi new line)
                        tokens.push ','

                    if (self.indents.0 < indentation)
                        tokens.push '@{'
                        self.indents.unshift (indentation)

                    tokens
            else
                []

require './runtime'

exports: create indent stack = create indent stack! =
    object =>
        :indents = [0]
        :indentation regex = new (RegExp '\n( *)$')
        :multi new line regex = new (RegExp '\n *\n')

        :is (text) multi new line =
            :multi new line regex: test (text)

        :(text) has new line? =
            :indentation regex: test (text)

        :indentation (new line) =
            :indentation regex: exec (new line): 1: length

        :current indentation? =
            :indents: 0

        :set indentation (text) =
            if (:(text) has new line?)
                :indents:unshift 'bracket'
                :indents:unshift (:indentation (text))
            else
                current = :current indentation?
                :indents:unshift 'bracket'
                :indents:unshift (current)

        :unset indentation! =
            :indents: shift!

            tokens = []
            while (:indents: 0 != 'bracket')
                tokens: push '}'
                :indents: shift!

            :indents: shift!
            tokens

        :tokens for eof? =
            tokens = []
            indents = :indents: length

            while (indents > 1)
                tokens: push '}'
                indents = indents - 1

            tokens: push 'eof'

            tokens

        :tokens for new line (text) =
            if (:(text) has new line?)
                current indentation = :current indentation?
                indentation = :indentation (text)

                if (current indentation == indentation)
                    [',']
                else if (current indentation < indentation)
                    :indents: unshift (indentation)
                    ['@{']
                else
                    tokens = []

                    while (:indents: 0 > indentation)
                        tokens: push '}'
                        :indents: shift!

                    if (:is (text) multi new line)
                        tokens: push ','

                    if (:indents: 0 < indentation)
                        tokens: push '@{'
                        :indents: unshift (indentation)

                    tokens
            else
                []


class

class

window = window || global

window.$class (prototype) =
  constructor (args, ...) = =>
    prototype.constructor.apply (self, args)
    nil

  constructor.prototype = prototype
  constructor

window.$class extending (base constructor, prototype members) =
  prototype constructor () = =>
    for @(field) in (prototype members)
      if (prototype members.has own property (field))
        self.(field) = prototype members.(field)

  prototype constructor.prototype = base constructor.prototype
  prototype = new (prototype constructor ())
  constructor (args, ...) = =>
    prototype members.constructor.apply (self, args)
    nil

  constructor.prototype = prototype
  constructor

error: pathspec 'src/lib/class.pogo' did not match any file(s) known to git


should have added this ages ago

should have added this ages ago

require 'cupoftea'
cg = require './codeGenerator/codeGenerator'
require './assertions.pogo'

spec 'unary operator expression'
    spec 'as expression'
        op expr = cg: new unary operator expression, operator '%', expression {variable ['a']}

        (op expr: expression?) should contain fields {
            is method call
            object {variable ['a']}
            name ['%']
            arguments []
        }

    spec 'as expression with macro'
        op expr = cg: new unary operator expression, operator '!', expression {variable ['a']}

        (op expr: expression?) should contain fields {
            is operator
            operator '!'
            arguments [{variable ['a']}]
        }

error: pathspec 'src/bootstrap/unaryOperatorExpressionSpec.pogo' did not match any file(s) known to git


coroutines sample

coroutines sample

last yield continuation = nil
last result continuation = nil

yield (n, yield continuation) =
    last yield continuation = yield continuation
    last result continuation (nil, n)

coroutine (block) =
    run (result continuation) =
        last result continuation = result continuation

        if (last yield continuation)
            last yield continuation ()
        else
            block
                last yield continuation = nil
                last result continuation = nil

strings = coroutine
    yield! 1
    yield! 2
    yield! 3

console.log (strings!)
console.log (strings!)
console.log (strings!)
console.log 'finished'

error: pathspec 'src/samples/coroutines.pogo' did not match any file(s) known to git


fork sample

fork sample

fs = require 'fs'

fork (block) = block @{}

fork
    console.log "reading contents of file #(__filename)"
    contents = fs.read file (__filename,  'utf-8')!
    console.log ("contents of #(__filename):")
    process.stdout.write (contents)

console.log 'processing other stuff'

error: pathspec 'src/samples/fork.pogo' did not match any file(s) known to git


continuation example

continuation example

current continuation (callback) =
    continuation () =
        callback (nil, continuation)

    callback (nil, continuation)

n = 0

cont = current continuation!

console.log (n)
n = n + 1

if (n < 10)
    cont ()

error: pathspec 'src/samples/continuations.pogo' did not match any file(s) known to git


multi-methods?

multi-methods?

require '../bootstrap/runtime'

generic method =
    generic method id = 0

    with last non nil constructor and index (constructors, block) =
        find last non nil constructor index () =
            for (n = constructors.length - 1, n >= 0, --n)
                if (constructors.(n))
                    return (n)

        last index = find last non nil constructor index ()
        last constructor = constructors.(last index)
        if (last constructor)
            block (last constructor, last index)

    for each constructor index next index in (constructors, block) =
        find next non nil constructor index after (n) =
            for (i = n + 1, i < constructors.length, ++i)
                if (constructors.(i))
                    return (i)

        for (n = 0, n < constructors.length - 1, ++n)
            constructor = constructors.(n)
            if (constructor)
                block (constructor, n, find next non nil constructor index after (n))

    gm () =
        name = "genericMethod$#(++generic method id)"
        body = "var method = void 0;
                for (var n = 0; n < arguments.length; n++) {
                    if (method = arguments[n][\"#(name)$\" + n]) {
                        break;
                    }
                }
                if (method) {
                    return method.apply(void 0, arguments);
                }
                throw new Error(\"no such method for arguments\")"
        fn = @new Function (body)

        fn.add method (constructors, ... , method) =
            for each constructor @(constructor) index @(index) next index @(next index) in (constructors)
                debugger
                body = "return arguments[#(next index)][\"#(name)$#(next index)\"].apply(void 0, arguments);"
                constructor.prototype."#(name)$#(index)" = @new Function (body)

            with last non nil constructor @(constructor) and index @(index) (constructors)
                constructor.prototype."#(name)$#(index)" = method

        fn

point = class {
    to string () = "(#(self.x), #(self.y))"

    constructor (x, y) =
        self.x = x
        self.y = y
}

circle = class {
    to string () = "#(self.center) radius #(self.radius)"

    constructor (center: center, radius: radius) =
        self.center = center
        self.radius = radius
}

file = class {
    to string () = "`#(self.name)'"

    constructor (name) =
        self.name = name
}

move to = generic method ()

move to.add method @(w) (circle) @(p) (point)
    console.log "moving circle #(w), to point #(p)"

move (@new circle (center: @new point (0, 0), radius: 30)) to (@new point (3, 4))

move to.add method @(f) (file) @(dir) (nil)
    console.log "moving file #(f) to dir `#(dir)'"

move (@new file 'stuff.txt') to 'dir'

move to.add method @(a) (nil) @(f) (file)
    console.log "moving #(a) into file #(f)"

move (7) to (@new file '7.txt')

// is this a good idea?

error: pathspec 'src/samples/multimethods.pogo' did not match any file(s) known to git


tests for instanceof

tests for instanceof

script = require './scriptAssertions.pogo'

describe 'instanceof operator (::)'
    it "returns true when an object is an instance of a constructor"
        script.'print ([] :: Array)' should output 'true'

    it "returns false when an object is not an instance of a constructor"
        script.'print ({} :: Array)' should output 'false'

    it "returns true when an object is an instance of a distant constructor"
        script.'print ([] :: Object)' should output 'true'

error: pathspec 'test/instanceof.pogo' did not match any file(s) known to git


spec for futures

spec for futures

script = require './scriptAssertions'

async should output = script.async should output
should output = script.should output

describe 'futures'
    it 'can defer the result of an asynchronous call'
        async! 'x! = 5
                f = x?
                print (f!)
                done ()' should output '5'

error: pathspec 'test/futureSpec.pogo' did not match any file(s) known to git


start on atoms

start on atoms

require './runtime.pogo'

atom prototype = object {
    arguments? = []
    block parameters? = []
    word? = null
    block? = null
}

exports: atom (members) = object extending (atom prototype) (members)

exports: argument atom (argument) = atom {
    arguments? = [argument]
}

exports: block atom (block) = atom {
    block? = block
}

exports: word atom (word) = atom {
    word? = word
}

exports: block parameters atom (parameters) = atom {
    block parameters? = parameters
}

error: pathspec 'src/bootstrap/atoms.pogo' did not match any file(s) known to git


accounts example from SICP

accounts example from SICP

create account, balance = {
    withdraw @amount =
        if (balance >= amount)
            balance = balance - amount
        else
            throw 'insufficient funds'

    deposit @amount =
        balance = balance + amount

    balance? =
        balance
}

account = create account, balance 100

account: withdraw 30
account: deposit 100
console: log (account: balance?)

error: pathspec 'src/samples/account.pogo' did not match any file(s) known to git


pogo rewrite of parserSpec

pogo rewrite of parserSpec

require 'cupoftea'
require './assertions.pogo'

parse = require './parser.pogo': parse

assume @term is module with statements @action =
    if (term: is module)
        action (term: statements)
    else
        throw (new (Error ('expected module, but found ' + term)))

assume @statements has just one statement @action =
    if (statements: statements: length == 1)
        action (statements: statements: 0)
    else
        throw (new (Error ('expected statements to have just one statement, found ' + statements: statements: length)))

expression @source =
    term = parse @source
    assume @term is module with statements ?statements
        assume @statements has just one statement ?s
            s

spec 'parser'
    spec 'terminals'
        spec 'integer'
            (expression '5') should contain fields #
                integer 5

        spec 'float'
            (expression '5.6') should contain fields #
                float 5.6

        spec 'variables'
            spec 'simple'
                (expression 'total weight') should contain fields #
                    variable ['total'. 'weight']

        spec 'strings'
            spec 'simple string'
                (expression '''a string''') should contain fields #
                    is string
                    string 'a string'

            spec 'string with single quotes'
                (expression '''''''alright!'''' he said''') should contain fields #
                    is string
                    string '''alright!'' he said'

        spec 'interpolated strings'
            spec 'simple'
                (expression '"a string"') should contain fields #
                    is interpolated string
                    components [
                        #{string 'a string'}
                    ]

            spec 'null string'
                (expression '""') should contain fields #
                    is interpolated string
                    components [
                    ]

            spec 'with single identifier variable'
                (expression '"a boat @length meters in length"') should contain fields #
                    is interpolated string
                    components [
                        #{string 'a boat '}
                        #{variable ['length']}
                        #{string ' meters in length'}
                    ]

            spec 'with single variable expression'
                (expression '"a boat @(boat length) meters in length"') should contain fields #
                    is interpolated string
                    components [
                        #{string 'a boat '}
                        #{variable ['boat'. 'length']}
                        #{string ' meters in length'}
                    ]

            spec 'with complex expression'
                (expression '"a boat @(lookup boat length from (boat database)) meters in length"') should contain fields #
                    is interpolated string
                    components [
                        #{string 'a boat '}
                        #
                            function #{variable ['lookup'. 'boat'. 'length'. 'from']}
                            arguments [#{variable ['boat'. 'database']}]

                        #{string ' meters in length'}
                    ]

            spec 'with inner interpolation'
                (expression '"a boat @("@(boat length) meters") in length"') should contain fields #
                    is interpolated string
                    components [
                        #{string 'a boat '}
                        #
                            is interpolated string
                            components [
                                #{variable ['boat'. 'length']}
                                #{string ' meters'}
                            ]

                        #{string ' in length'}
                    ]

        spec 'lists'
            spec 'empty'
                (expression '[]') should contain fields #
                    is list
                    items []

            spec 'one item'
                (expression '[1]') should contain fields #
                    is list
                    items [#{integer 1}]

            spec 'two items'
                (expression '[1. 2]') should contain fields #
                    is list
                    items [
                        #{integer 1}
                        #{integer 2}
                    ]

        spec 'hashes'
            spec 'empty hash'
                (expression '#{}') should contain fields #
                    is hash
                    entries []

            spec 'hash with one entry'
                (expression '#{port 1234}') should contain fields #
                    is hash
                    entries [
                        #
                            field ['port']
                            value #{integer 1234}
                    ]

            spec 'hash with two entries'
                (expression '#{port 1234, ip address ''1.1.1.1''}') should contain fields #
                    is hash
                    entries [
                        #
                            field ['port']
                            value #{integer 1234}

                        #
                            field ['ip'. 'address']
                            value #{string '1.1.1.1'}
                    ]

            spec 'hash with two entries on different lines'
                (expression '#{port 1234. ip address ''1.1.1.1''}') should contain fields #
                    is hash
                    entries [
                        #
                            field ['port']
                            value #{integer 1234}

                        #
                            field ['ip'. 'address']
                            value #{string '1.1.1.1'}
                    ]

            spec 'hash with true entry'
                (expression '#{port 1234, readonly}') should contain fields #
                    is hash
                    entries [
                        #
                            field ['port']
                            value #{integer 1234}

                        #
                            field ['readonly']
                            value @undefined
                    ]

    spec 'function calls'
        spec 'function call'
            (expression 'touch @file') should contain fields #
                function #{variable ['touch']}
                arguments [#{variable ['file']}]

        spec 'function call with self argument'
            (expression 'touch @:file') should contain fields #
                function #{variable ['touch']}
                arguments [
                  #
                    is field reference
                    object #{variable ['self']}
                    name ['file']
                ]

        spec 'function call with no argument'
            (expression 'delete everything!') should contain fields #
                function #{variable ['delete'. 'everything']}
                arguments []

        spec 'function call with block with parameters'
            (expression 'with file @file ?stream {stream}') should contain fields #
                function #{variable ['with'. 'file']}
                arguments [
                    #{variable ['file']}
                    #
                        body #{statements [#{variable ['stream']}]}
                        parameters [#{parameter ['stream']}]
                ]

        spec 'function call with two blocks with parameters'
            (expression 'name ?x {x} ?y {y}') should contain fields #
                function #{variable ['name']}
                arguments [
                    #
                        body #{statements [#{variable ['x']}]}
                        parameters [#{parameter ['x']}]

                    #
                        body #{statements [#{variable ['y']}]}
                        parameters [#{parameter ['y']}]
                ]

        spec 'function call with two optional arguments'
            (expression 'name @a, port 34, server @s') should contain fields #
                function #{variable ['name']}
                arguments [
                    #{variable ['a']}
                ]
                optional arguments [
                    #
                        field ['port']
                        value #{integer 34}

                    #
                        field ['server']
                        value #{variable ['s']}
                ]

        spec 'function call with no arguments and one optional argument'
            (expression 'start server, port 34') should contain fields #
                function #{variable ['start'. 'server']}
                arguments []
                optional arguments [
                    #
                        field ['port']
                        value #{integer 34}
                ]

    spec 'object operations'
        spec 'method call'
            (expression 'object: method @argument') should contain fields #
                is method call
                object #{variable ['object']}
                name ['method']
                arguments [#{variable ['argument']}]

        spec 'method call with optional arguments'
            (expression 'object: method @argument, view @view') should contain fields #
                is method call
                object #{variable ['object']}
                name ['method']
                arguments [#{variable ['argument']}]
                optional arguments [
                    #{field ['view'], value #{variable ['view']}}
                ]

        spec 'field reference'
            (expression 'object: field') should contain fields #
                is field reference
                object #{variable ['object']}
                name ['field']

        spec 'self field reference'
            (expression ':field') should contain fields #
                is field reference
                object #{variable ['self']}
                name ['field']

        spec 'indexer'
            (expression 'object: @x') should contain fields #
                is indexer
                object #{variable ['object']}
                indexer #{variable ['x']}

    spec 'blocks'
        spec 'empty block'
            (expression '{}') should contain fields #
                is block
                parameters []
                redefines self @false
                body #{statements []}

        spec 'block'
            (expression '{x.y}') should contain fields #
                is block
                parameters []
                redefines self @false
                body #{statements [
                    #{variable ['x']}
                    #{variable ['y']}
                ]}

        spec 'block with parameter'
            (expression '?x {x.y}') should contain fields #
                is block
                parameters [#{parameter ['x']}]
                redefines self @false
                body #
                    statements [
                        #{variable ['x']}
                        #{variable ['y']}
                    ]

        spec 'block with parameter, redefining self'
            (expression '?x => {x.y}') should contain fields #
                is block
                parameters [#{parameter ['x']}]
                redefines self @true
                body #
                    statements [
                        #{variable ['x']}
                        #{variable ['y']}
                    ]

    spec 'operators'
        spec 'should be lower precedence than object operation'
            (expression 'o:m 2 +- o:x') should contain fields #
                is method call
                object #
                    is method call
                    object #{variable ['o']}
                    name ['m']
                    arguments [#{integer 2}]

                name ['+-']
                arguments [
                    #
                        is field reference
                        object #{variable ['o']}
                        name ['x']
                ]

    spec 'assignment'
        spec 'assignment'
            (expression 'x = y') should contain fields #
                is definition
                target #{variable ['x']}
                source #{variable ['y']}

        spec 'function definition'
            spec 'function with one parameter'
                (expression 'func @x = x') should contain fields #
                    is definition
                    target #{variable ['func']}
                    source #
                        parameters [#{parameter ['x']}]
                        body #{statements [#{variable ['x']}]}

            spec 'function with one parameter, and one optional parameter'
                (expression 'func @x, port 80 = x') should contain fields #
                    is definition
                    target #{variable ['func']}
                    source #
                        parameters [#{parameter ['x']}]
                        optional parameters [#{field ['port'], value #{integer 80}}]
                        body #{statements [#{variable ['x']}]}

        spec 'field assignment'
            (expression 'o: x = y') should contain fields #
                is definition
                target #
                    is field reference
                    object #{variable ['o']}
                    name ['x']

                source #{variable ['y']}

        spec 'index assignment'
            (expression 'o: @x = y') should contain fields #
                is definition
                target #
                    is indexer
                    object #{variable ['o']}
                    indexer #{variable ['x']}

                source #{variable ['y']}

        spec 'assignment from field'
            (expression 'x = y: z') should contain fields #
                is definition
                target #{variable ['x']}
                source #
                    is field reference
                    object #
                        variable ['y']

                    name ['z']

        spec 'assignment of command'
            (expression 'x! = 8') should contain fields #
                is definition
                target #{variable ['x']}
                source #
                    is block
                    parameters []
                    body #
                        statements [#{integer 8}]

        spec 'assignment of query'
            (expression 'x? = 8') should contain fields #
                is definition
                target #{variable ['x']}
                source #
                    is block
                    parameters []
                    body #
                        statements [#{integer 8}]

        spec 'assignment from method call'
            (expression 'x = y: z @a') should contain fields #
                is definition
                target #{variable ['x']}
                source #
                    is method call
                    object #
                        variable ['y']

                    name ['z']
                    arguments [#{variable ['a']}]

        spec 'field assignment from method call'
            (expression 'i: x = y: z @a') should contain fields #
                is definition
                target #
                    is field reference
                    object #{variable ['i']}
                    name ['x']

                source #
                    is method call
                    object #
                        variable ['y']

                    name ['z']
                    arguments [#{variable ['a']}]

error: pathspec 'src/bootstrap/parserSpec.pogo' did not match any file(s) known to git


pogo rewrite for assertions

pogo rewrite for assertions

assert = require 'assert'

global: should contain fields = (require '../spec/containsFields'): contains fields
global: @x should equal @y = assert: equal @x @y
global: @x should be truthy = assert: ok @x
global: @x should be falsy = assert: ok (not @x)

error: pathspec 'src/bootstrap/assertions.pogo' did not match any file(s) known to git


event emitter example

event emitter example

EventEmitter = require('events').EventEmitter

shouter = new (EventEmitter)

shouter.on 'shout'
    console.log "calm down"

shouter.emit 'shout'

error: pathspec 'examples/event_emitter.pogo' did not match any file(s) known to git


async for loop for ls

async for loop for ls

fs = require 'fs'

for each in (list, action) asynchronously (done) =
    outstanding callbacks = 0

    for each @(item) in (list)
        outstanding callbacks = outstanding callbacks + 1
        action (item) @(error, result)
            outstanding callbacks = outstanding callbacks - 1

            if (outstanding callbacks == 0)
                done (error)

filenames = fs.readdir! '.'

for each! @(filename) in (filenames) asynchronously
    stat = fs.stat! (filename)
    console.log "#(filename): #(stat.size)"

console.log ()
console.log "#(filenames.length) files in total"

error: pathspec 'src/samples/ls.pogo' did not match any file(s) known to git