removed failing list parser test

removed failing list parser test

require './assertions'

parser = require '../lib/parser/parser'
require './parserAssertions'
create terms () = require '../lib/parser/codeGenerator'.code generator ()

describe 'parser'
    terms = nil

    before
        terms := create terms ()

    describe 'terminals'
        it 'integer'
            (expression '5') should contain fields {
                integer 5
            }

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

        describe 'variables'
            it 'simple'
                (expression 'total weight') should contain fields {
                    variable ['total', 'weight']
                }

            it 'can use $ as a variable'
                (expression '$') should contain fields {
                    variable ['$']
                }

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

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

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

            it 'multiline string'
                (expression "  'one\n   two'") should contain fields {
                    is string
                    string "one\ntwo"
                }

            it 'multiline string with empty lines'
                (expression "  'one\n\n   two\n\n   three'") should contain fields {
                    is string
                    string "one\n\ntwo\n\nthree"
                }

            it 'multiline double-quote string'
                (expression "  \"one\n   two\"") should contain fields {
                    is string
                    string "one\ntwo"
                }

            it 'two multiline string in function'
                (expression "x 'one\n   two' y \"three\n           four\"") should contain fields {
                    is function call
                    function {variable ['x', 'y']}
                    function arguments [
                        {string "one\ntwo"}
                        {string "three\nfour"}
                    ]
                }

        describe 'interpolated strings'
            it 'simple'
                (expression '"a string"') should contain fields {
                    is string
                    string 'a string'
                }

            it 'empty'
                (expression '""') should contain fields {
                    is string
                    string ''
                }

            it 'with newline'
                (expression '"one\ntwo"') should contain fields {
                    string "one\ntwo"
                }

            it 'with newline escape and indentation should not remove indentation'
                (expression '  "one\n    two"') should contain fields {
                    string "one\n    two"
                }

            it 'with indentation'
                (expression "  \"one\n   two\"") should contain fields {
                    string "one\ntwo"
                }

            it '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'}
                    ]
                }

            it 'with hash # character at end'
                (expression '"this is a hash: #"') should contain fields {
                    is string
                    string 'this is a hash: #'
                }

            it 'with hash # character in middle'
                (expression '"this is a hash: #, ok?"') should contain fields {
                    is string
                    string 'this is a hash: #, ok?'
                }

            it 'with escaped #'
                (expression '"a boat \#(boat length) meters in length"') should contain fields {
                    is string
                    string 'a boat #(boat length) meters in length'
                }

            it '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']}
                            function arguments [{variable ['boat', 'database']}]
                        }
                        {string ' meters in length'}
                    ]
                }

            it 'in block'
                (expression "abc =\n    \"\#(stuff)\"") should contain fields {
                    is definition
                    target {
                        is variable
                        variable ['abc']
                    }
                    source {
                        is variable
                        variable ['stuff']
                    }
                }

            it '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'}
                    ]
                }

        describe 'sub expressions'
            it 'single expression'
                (expression '(x)') should contain fields {variable ['x']}

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

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

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

            it 'two items separated by newlines'
                (expression "[\n  1\n  2\n]") should contain fields {
                    is list
                    items [
                        {integer 1}
                        {integer 2}
                    ]
                }

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

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

            it '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'}
                        }
                    ]
                }

            it 'hash with two entries on different lines'
                (expression "{port = 1234\nip 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'}
                        }
                    ]
                }

            it 'hash with string with assignment'
                (expression "{'port' = 1234}") should contain fields {
                    is hash
                    entries [
                        {
                            field {string 'port'}
                            value {integer 1234}
                        }
                    ]
                }

            it 'values can be specified on a new line'
                (expression "{
                                 height =
                                     80
                             }") should contain fields {
                    is hash
                    entries [
                        {
                            field ['height']
                            value {integer 80}
                        }
                    ]
                }

            it 'should allow methods to be defined, redefining self'
                (expression '{say hi to (name, greeting: nil) = print (name)}') should contain fields {
                    is hash
                    entries [
                        {
                            field ['say', 'hi', 'to']
                            value {
                                is block
                                redefines self

                                body {
                                    statements [
                                        {
                                            is function call
                                            function {variable ['print']}
                                        }
                                    ]
                                }

                                parameters [{variable ['name']}]

                                optional parameters [{
                                    is hash entry
                                    field ['greeting']
                                }]
                            }
                        }
                    ]
                }

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

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

        it 'function call with two arguments in parens'
            (expression 'f (a, b)') should contain fields {
                function {variable ['f']}
                function arguments [
                    {variable ['a']}
                    {variable ['b']}
                ]
            }

        it 'two expressions'
            (expression '(x, y)') should contain fields {
                is function call
                function {variable ['x']}
                function arguments [
                    {variable ['y']}
                ]
            }

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

        it 'function call with splat argument'
            (expression 'copy (files, ..., dir)') should contain fields {
                function {variable ['copy']}
                function arguments [
                  {variable ['files']}
                  {is splat}
                  {variable ['dir']}
                ]
            }

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

        it 'async function call with no arguments'
            (expression 'delete everything!') should contain fields (
                terms.async statements [
                    terms.function call (terms.variable ['delete', 'everything'], [], async: true)
                ].statements.0
            )

        it 'function call with block with parameters'
            (expression "with file (file) @(stream)\n  stream") should contain fields {
                function {variable ['with', 'file']}
                function arguments [
                    {variable ['file']}
                    {
                        body {statements [{variable ['stream']}]}
                        parameters [{variable ['stream']}]
                    }
                ]
            }

        it 'function call with block with long parameters'
            (expression "open database @(database connection)\n  database connection") should contain fields {
                function {variable ['open', 'database']}
                function arguments [
                    {
                        parameters [
                            {variable ['database', 'connection']}
                        ]
                        body {statements [{variable ['database', 'connection']}]}
                    }
                ]
            }

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

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

    describe 'object operations'
        it 'method call'
            (expression 'object.method (argument)') should contain fields {
                is method call
                object {variable ['object']}
                name ['method']
                method arguments [{variable ['argument']}]
            }

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

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

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

        it 'indexer'
            (expression 'object.(x)') should contain fields {
                is indexer
                object {variable ['object']}
                indexer {variable ['x']}
            }

        it 'parses no argument method with ()'
            (expression 'object.method()') should contain fields {
                is method call
                object {variable ['object']}
                name ['method']
                method arguments []
            }

        it 'parses no argument method with () and field'
            (expression 'object.method().field') should contain fields {
                is field reference
                object {
                    is method call
                    object {variable ['object']}
                    name ['method']
                    method arguments []
                }
                name ['field']
            }

        it 'parses no argument method with ! and field'
            (expression 'object.method! . field') should contain fields (
                terms.async statements [
                    terms.field reference (
                        terms.method call (
                            terms.variable ['object']
                            ['method']
                            []
                            async: true
                        )
                        ['field']
                    )
                ].statements.0
            )

        it 'parses no argument method with ! and field'
            (expression 'object.method!.field') should contain fields (
                terms.async statements [
                    terms.field reference (
                        terms.method call (
                            terms.variable ['object']
                            ['method']
                            []
                            async: true
                        )
                        ['field']
                    )
                ].statements.0
            )

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

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

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

        it 'block in parens'
            (expression "(one\n  two\n)") should contain fields {
                is function call
                function {variable ['one']}
                function arguments [
                    {
                        is block
                        body {
                            statements [
                                {variable ['two']}
                            ]
                        }
                    }
                ]
            }

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

    describe 'statements'
        it 'can be separated by commas (,)'
            (statements 'a, b') should contain fields {
                statements [
                    {variable ['a']}
                    {variable ['b']}
                ]
            }

        it 'can be separated by unix new lines'
            (statements "a\nb") should contain fields {
                statements [
                    {variable ['a']}
                    {variable ['b']}
                ]
            }

        it 'can be separated by windows new lines'
            (statements "a\r\nb") should contain fields {
                statements [
                    {variable ['a']}
                    {variable ['b']}
                ]
            }

        it 'windows indentation'
            (statements "app (stuff) =\r
                           ok\r
                         \r
                         url (path) =\r
                           ok") should contain fields {
                statements [
                    {is definition}
                    {is definition}
                ]
            }

    describe 'operators'
        it 'should be lower precedence than object operation'
            (expression 'o.m 2 * o.x') should contain fields {
                is operator
                operator '*'

                operator arguments [
                    {
                        is method call
                        object {variable ['o']}
                        name ['m']
                        method arguments [{integer 2}]
                    }
                    {
                        is field reference
                        object {variable ['o']}
                        name ['x']
                    }
                ]
            }

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

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

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

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

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

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

        it 'assignment on next line'
            (expression "x =\n  y") should contain fields {
                is definition
                target {variable ['x']}
                source {variable ['y']}
            }

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

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

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

                source {variable ['y']}
            }

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

                source {variable ['y']}
            }

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

                    name ['z']
                }
            }

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

        it 'definition of function with no arguments, using empty parens "()"'
            (expression 'x () = 8') should contain fields {
                is definition
                target {variable ['x']}
                source {
                    is block
                    parameters []
                    body {
                        statements [{integer 8}]
                    }
                }
            }

        it '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']
                    method arguments [{variable ['a']}]
                }
            }

        it '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']
                    method arguments [{variable ['a']}]
                }
            }

    describe 'regexps'
        it 'simple'
            (expression 'r/abc/') should contain fields {
                is reg exp
                pattern 'abc'
            }

        it 'with options'
            (expression 'r/abc/img') should contain fields {
                is reg exp
                pattern 'abc'
                options 'img'
            }

        it 'with escaped back ticks'
            (expression 'r/abc\/def\/ghi/') should contain fields {
                is reg exp
                pattern 'abc/def/ghi'
            }

        it 'with various escapes'
            (expression 'r/abc\/def\nghi\/jkl/') should contain fields {
                is reg exp
                pattern 'abc/def\nghi/jkl'
            }

        it 'with newline'
            (expression "a = r/abc\n      def/") should contain fields {
                is definition
                target {
                    is variable
                    variable ['a']
                }
                source {
                    is reg exp
                    pattern "abc\\ndef"
                }
            }

    describe 'comments'
        it 'should not treat comment-like syntax as comments in strings'
            (statements "get 'http://pogoscript.org/'") should contain fields {
                is statements
                statements [{
                    is function call
                    function {variable ['get']}
                    function arguments [
                        {string 'http://pogoscript.org/'}
                    ]
                }]
            }

        describe 'should allow one-line C++ style comments, as in: // this is a comment'
            it 'when at the end of a line'
                (statements "a // this is a comment\nb") should contain fields {
                    is statements
                    statements [
                        {variable ['a']}
                        {variable ['b']}
                    ]
                }

            it 'before an indented block'
                (statements "a // this is a comment\n  b") should contain fields {
                    is statements
                    statements [{
                        is function call
                        function {variable ['a']}
                        function arguments [{
                            is block
                            body {
                                statements [
                                    {variable ['b']}
                                ]
                            }
                        }]
                    }]
                }

            it 'when at end of file'
                (statements "a // this is a comment") should contain fields {
                    is statements
                    statements [
                        {variable ['a']}
                    ]
                }

            it 'when between lines'
                (statements "a\n// this is a comment\nb") should contain fields {
                    is statements
                    statements [
                        {variable ['a']}
                        {variable ['b']}
                    ]
                }

        describe 'should allow multi-line C style comments, as in: /* this is a comment */'
            it 'when on one line'
                (statements "a /* comment */ b") should contain fields {
                    statements [
                        {variable ['a', 'b']}
                    ]
                }

            it 'when there are two'
                (statements "a /* comment */ b /* another comment */ c") should contain fields {
                    statements [
                        {variable ['a', 'b', 'c']}
                    ]
                }

            it 'when between lines'
                (statements "a\n/* comment */\nb\n/* another comment */\nc") should contain fields {
                    statements [
                        {variable ['a']}
                        {variable ['b']}
                        {variable ['c']}
                    ]
                }

            it 'when it contains a * character'
                (statements "a /* sh*t */ b") should contain fields {
                    statements [
                        {variable ['a', 'b']}
                    ]
                }

            it 'when it covers two lines'
                (statements "a /* line one\nline two */ b") should contain fields {
                    statements [{
                        is variable
                        variable ['a', 'b']
                    }]
                }

            it 'when it is terminated by the end of file'
                (statements "a /* comment to eof") should contain fields {
                    statements [
                        {variable ['a']}
                    ]
                }

            it 'when it extends to the end of the file'
                (statements "a /* comment to end */") should contain fields {
                    statements [
                        {variable ['a']}
                    ]
                }

            it 'when it extends to the end of the file followed by newline'
                (statements "a /* comment to end */\n") should contain fields {
                    statements [
                        {variable ['a']}
                    ]
                }

    it 'lexer'
        create parser = require '../lib/parser/parser'.create parser
        lex parser = create parser (terms: terms)
        tokens = lex parser.lex 'a (b)'
        (tokens) should contain fields [
            ['identifier', 'a']
            ['(']
            ['identifier', 'b']
            [')']
            ['eof']
        ]

    it 'ignores hash bang #!, at the beginning of the file'
        (statements '#! /usr/bin/env pogo
                     a
                     b') should contain fields (
            terms.async statements [
                terms.variable ['a']
                terms.variable ['b']
            ]
        )

require './assertions'

parser = require '../lib/parser/parser'
require './parserAssertions'
create terms () = require '../lib/parser/codeGenerator'.code generator ()

describe 'parser'
    terms = nil

    before
        terms := create terms ()

    describe 'terminals'
        it 'integer'
            (expression '5') should contain fields {
                integer 5
            }

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

        describe 'variables'
            it 'simple'
                (expression 'total weight') should contain fields {
                    variable ['total', 'weight']
                }

            it 'can use $ as a variable'
                (expression '$') should contain fields {
                    variable ['$']
                }

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

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

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

            it 'multiline string'
                (expression "  'one\n   two'") should contain fields {
                    is string
                    string "one\ntwo"
                }

            it 'multiline string with empty lines'
                (expression "  'one\n\n   two\n\n   three'") should contain fields {
                    is string
                    string "one\n\ntwo\n\nthree"
                }

            it 'multiline double-quote string'
                (expression "  \"one\n   two\"") should contain fields {
                    is string
                    string "one\ntwo"
                }

            it 'two multiline string in function'
                (expression "x 'one\n   two' y \"three\n           four\"") should contain fields {
                    is function call
                    function {variable ['x', 'y']}
                    function arguments [
                        {string "one\ntwo"}
                        {string "three\nfour"}
                    ]
                }

        describe 'interpolated strings'
            it 'simple'
                (expression '"a string"') should contain fields {
                    is string
                    string 'a string'
                }

            it 'empty'
                (expression '""') should contain fields {
                    is string
                    string ''
                }

            it 'with newline'
                (expression '"one\ntwo"') should contain fields {
                    string "one\ntwo"
                }

            it 'with newline escape and indentation should not remove indentation'
                (expression '  "one\n    two"') should contain fields {
                    string "one\n    two"
                }

            it 'with indentation'
                (expression "  \"one\n   two\"") should contain fields {
                    string "one\ntwo"
                }

            it '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'}
                    ]
                }

            it 'with hash # character at end'
                (expression '"this is a hash: #"') should contain fields {
                    is string
                    string 'this is a hash: #'
                }

            it 'with hash # character in middle'
                (expression '"this is a hash: #, ok?"') should contain fields {
                    is string
                    string 'this is a hash: #, ok?'
                }

            it 'with escaped #'
                (expression '"a boat \#(boat length) meters in length"') should contain fields {
                    is string
                    string 'a boat #(boat length) meters in length'
                }

            it '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']}
                            function arguments [{variable ['boat', 'database']}]
                        }
                        {string ' meters in length'}
                    ]
                }

            it 'in block'
                (expression "abc =\n    \"\#(stuff)\"") should contain fields {
                    is definition
                    target {
                        is variable
                        variable ['abc']
                    }
                    source {
                        is variable
                        variable ['stuff']
                    }
                }

            it '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'}
                    ]
                }

        describe 'sub expressions'
            it 'single expression'
                (expression '(x)') should contain fields {variable ['x']}

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

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

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

            it 'a hash entry'
                (expression '[x, width: 10]') should contain fields {
                    is list
                    items [
                        {variable ['x']}
                        {is hash entry, field ['width'], value {integer 10}}
                    ]
                }

            it 'two items separated by newlines'
                (expression "[\n  1\n  2\n]") should contain fields {
                    is list
                    items [
                        {integer 1}
                        {integer 2}
                    ]
                }

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

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

            it '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'}
                        }
                    ]
                }

            it 'hash with two entries on different lines'
                (expression "{port = 1234\nip 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'}
                        }
                    ]
                }

            it 'hash with string with assignment'
                (expression "{'port' = 1234}") should contain fields {
                    is hash
                    entries [
                        {
                            field {string 'port'}
                            value {integer 1234}
                        }
                    ]
                }

            it 'values can be specified on a new line'
                (expression "{
                                 height =
                                     80
                             }") should contain fields {
                    is hash
                    entries [
                        {
                            field ['height']
                            value {integer 80}
                        }
                    ]
                }

            it 'should allow methods to be defined, redefining self'
                (expression '{say hi to (name, greeting: nil) = print (name)}') should contain fields {
                    is hash
                    entries [
                        {
                            field ['say', 'hi', 'to']
                            value {
                                is block
                                redefines self

                                body {
                                    statements [
                                        {
                                            is function call
                                            function {variable ['print']}
                                        }
                                    ]
                                }

                                parameters [{variable ['name']}]

                                optional parameters [{
                                    is hash entry
                                    field ['greeting']
                                }]
                            }
                        }
                    ]
                }

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

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

        it 'function call with two arguments in parens'
            (expression 'f (a, b)') should contain fields {
                function {variable ['f']}
                function arguments [
                    {variable ['a']}
                    {variable ['b']}
                ]
            }

        it 'two expressions'
            (expression '(x, y)') should contain fields {
                is function call
                function {variable ['x']}
                function arguments [
                    {variable ['y']}
                ]
            }

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

        it 'function call with splat argument'
            (expression 'copy (files, ..., dir)') should contain fields {
                function {variable ['copy']}
                function arguments [
                  {variable ['files']}
                  {is splat}
                  {variable ['dir']}
                ]
            }

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

        it 'async function call with no arguments'
            (expression 'delete everything!') should contain fields (
                terms.async statements [
                    terms.function call (terms.variable ['delete', 'everything'], [], async: true)
                ].statements.0
            )

        it 'function call with block with parameters'
            (expression "with file (file) @(stream)\n  stream") should contain fields {
                function {variable ['with', 'file']}
                function arguments [
                    {variable ['file']}
                    {
                        body {statements [{variable ['stream']}]}
                        parameters [{variable ['stream']}]
                    }
                ]
            }

        it 'function call with block with long parameters'
            (expression "open database @(database connection)\n  database connection") should contain fields {
                function {variable ['open', 'database']}
                function arguments [
                    {
                        parameters [
                            {variable ['database', 'connection']}
                        ]
                        body {statements [{variable ['database', 'connection']}]}
                    }
                ]
            }

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

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

    describe 'object operations'
        it 'method call'
            (expression 'object.method (argument)') should contain fields {
                is method call
                object {variable ['object']}
                name ['method']
                method arguments [{variable ['argument']}]
            }

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

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

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

        it 'indexer'
            (expression 'object.(x)') should contain fields {
                is indexer
                object {variable ['object']}
                indexer {variable ['x']}
            }

        it 'parses no argument method with ()'
            (expression 'object.method()') should contain fields {
                is method call
                object {variable ['object']}
                name ['method']
                method arguments []
            }

        it 'parses no argument method with () and field'
            (expression 'object.method().field') should contain fields {
                is field reference
                object {
                    is method call
                    object {variable ['object']}
                    name ['method']
                    method arguments []
                }
                name ['field']
            }

        it 'parses no argument method with ! and field'
            (expression 'object.method! . field') should contain fields (
                terms.async statements [
                    terms.field reference (
                        terms.method call (
                            terms.variable ['object']
                            ['method']
                            []
                            async: true
                        )
                        ['field']
                    )
                ].statements.0
            )

        it 'parses no argument method with ! and field'
            (expression 'object.method!.field') should contain fields (
                terms.async statements [
                    terms.field reference (
                        terms.method call (
                            terms.variable ['object']
                            ['method']
                            []
                            async: true
                        )
                        ['field']
                    )
                ].statements.0
            )

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

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

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

        it 'block in parens'
            (expression "(one\n  two\n)") should contain fields {
                is function call
                function {variable ['one']}
                function arguments [
                    {
                        is block
                        body {
                            statements [
                                {variable ['two']}
                            ]
                        }
                    }
                ]
            }

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

    describe 'statements'
        it 'can be separated by commas (,)'
            (statements 'a, b') should contain fields {
                statements [
                    {variable ['a']}
                    {variable ['b']}
                ]
            }

        it 'can be separated by unix new lines'
            (statements "a\nb") should contain fields {
                statements [
                    {variable ['a']}
                    {variable ['b']}
                ]
            }

        it 'can be separated by windows new lines'
            (statements "a\r\nb") should contain fields {
                statements [
                    {variable ['a']}
                    {variable ['b']}
                ]
            }

        it 'windows indentation'
            (statements "app (stuff) =\r
                           ok\r
                         \r
                         url (path) =\r
                           ok") should contain fields {
                statements [
                    {is definition}
                    {is definition}
                ]
            }

    describe 'operators'
        it 'should be lower precedence than object operation'
            (expression 'o.m 2 * o.x') should contain fields {
                is operator
                operator '*'

                operator arguments [
                    {
                        is method call
                        object {variable ['o']}
                        name ['m']
                        method arguments [{integer 2}]
                    }
                    {
                        is field reference
                        object {variable ['o']}
                        name ['x']
                    }
                ]
            }

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

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

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

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

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

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

        it 'assignment on next line'
            (expression "x =\n  y") should contain fields {
                is definition
                target {variable ['x']}
                source {variable ['y']}
            }

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

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

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

                source {variable ['y']}
            }

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

                source {variable ['y']}
            }

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

                    name ['z']
                }
            }

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

        it 'definition of function with no arguments, using empty parens "()"'
            (expression 'x () = 8') should contain fields {
                is definition
                target {variable ['x']}
                source {
                    is block
                    parameters []
                    body {
                        statements [{integer 8}]
                    }
                }
            }

        it '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']
                    method arguments [{variable ['a']}]
                }
            }

        it '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']
                    method arguments [{variable ['a']}]
                }
            }

    describe 'regexps'
        it 'simple'
            (expression 'r/abc/') should contain fields {
                is reg exp
                pattern 'abc'
            }

        it 'with options'
            (expression 'r/abc/img') should contain fields {
                is reg exp
                pattern 'abc'
                options 'img'
            }

        it 'with escaped back ticks'
            (expression 'r/abc\/def\/ghi/') should contain fields {
                is reg exp
                pattern 'abc/def/ghi'
            }

        it 'with various escapes'
            (expression 'r/abc\/def\nghi\/jkl/') should contain fields {
                is reg exp
                pattern 'abc/def\nghi/jkl'
            }

        it 'with newline'
            (expression "a = r/abc\n      def/") should contain fields {
                is definition
                target {
                    is variable
                    variable ['a']
                }
                source {
                    is reg exp
                    pattern "abc\\ndef"
                }
            }

    describe 'comments'
        it 'should not treat comment-like syntax as comments in strings'
            (statements "get 'http://pogoscript.org/'") should contain fields {
                is statements
                statements [{
                    is function call
                    function {variable ['get']}
                    function arguments [
                        {string 'http://pogoscript.org/'}
                    ]
                }]
            }

        describe 'should allow one-line C++ style comments, as in: // this is a comment'
            it 'when at the end of a line'
                (statements "a // this is a comment\nb") should contain fields {
                    is statements
                    statements [
                        {variable ['a']}
                        {variable ['b']}
                    ]
                }

            it 'before an indented block'
                (statements "a // this is a comment\n  b") should contain fields {
                    is statements
                    statements [{
                        is function call
                        function {variable ['a']}
                        function arguments [{
                            is block
                            body {
                                statements [
                                    {variable ['b']}
                                ]
                            }
                        }]
                    }]
                }

            it 'when at end of file'
                (statements "a // this is a comment") should contain fields {
                    is statements
                    statements [
                        {variable ['a']}
                    ]
                }

            it 'when between lines'
                (statements "a\n// this is a comment\nb") should contain fields {
                    is statements
                    statements [
                        {variable ['a']}
                        {variable ['b']}
                    ]
                }

        describe 'should allow multi-line C style comments, as in: /* this is a comment */'
            it 'when on one line'
                (statements "a /* comment */ b") should contain fields {
                    statements [
                        {variable ['a', 'b']}
                    ]
                }

            it 'when there are two'
                (statements "a /* comment */ b /* another comment */ c") should contain fields {
                    statements [
                        {variable ['a', 'b', 'c']}
                    ]
                }

            it 'when between lines'
                (statements "a\n/* comment */\nb\n/* another comment */\nc") should contain fields {
                    statements [
                        {variable ['a']}
                        {variable ['b']}
                        {variable ['c']}
                    ]
                }

            it 'when it contains a * character'
                (statements "a /* sh*t */ b") should contain fields {
                    statements [
                        {variable ['a', 'b']}
                    ]
                }

            it 'when it covers two lines'
                (statements "a /* line one\nline two */ b") should contain fields {
                    statements [{
                        is variable
                        variable ['a', 'b']
                    }]
                }

            it 'when it is terminated by the end of file'
                (statements "a /* comment to eof") should contain fields {
                    statements [
                        {variable ['a']}
                    ]
                }

            it 'when it extends to the end of the file'
                (statements "a /* comment to end */") should contain fields {
                    statements [
                        {variable ['a']}
                    ]
                }

            it 'when it extends to the end of the file followed by newline'
                (statements "a /* comment to end */\n") should contain fields {
                    statements [
                        {variable ['a']}
                    ]
                }

    it 'lexer'
        create parser = require '../lib/parser/parser'.create parser
        lex parser = create parser (terms: terms)
        tokens = lex parser.lex 'a (b)'
        (tokens) should contain fields [
            ['identifier', 'a']
            ['(']
            ['identifier', 'b']
            [')']
            ['eof']
        ]

    it 'ignores hash bang #!, at the beginning of the file'
        (statements '#! /usr/bin/env pogo
                     a
                     b') should contain fields (
            terms.async statements [
                terms.variable ['a']
                terms.variable ['b']
            ]
        )


async test for async fn as arg

async test for async fn as arg

script = require './scriptAssertions'

async should output = script.async should output

describe 'async'
    it 'can make one async call in statements' @(done)
        async 'f (callback) =
                   set timeout
                       callback (nil, "result")
                   0

               x = f ()!
               print (x)
               done ()' should output ("'result'", done)

    it 'can make one async call as argument to another function' @(done)
        async 'f (callback) =
                   set timeout
                       callback (nil, "result")
                   0

               print (f ()!)
               done ()' should output ("'result'", done)


nested functions test

nested functions test

dslify = require '../index'
escodegen = require 'escodegen'
esprima = require 'esprima'

(actual) rewrites as (expected) =
    normalise (dslify.transform (actual)).should.equal (normalise(expected))

normalise (fn) =
    parsed = esprima.parse "(#(fn.to string()))".body.0
    escodegen.generate (parsed).replace "function anonymous(" "function ("

describe 'dslify'

    it 'rewrites a function so that global variables become DSL accessors'
        dsl = {
            scream (word) = "#(word)!!"
            nice word = "unicorns"
        }
        scream it () =
            scream (nice word)

        transformed = dslify.transform(scream it)
        transformed(dsl).should.equal "unicorns!!"

    it 'takes and returns JavaScript strings'
        scream it (nice word) =
            scream (nice word, global value)

        scream it string = scream it.to string()

        transformed = dslify.transform(scream it string, as string: true)
        transformed.should.be.a String

    it 'does not rewrite local variables'
        scream it (nice word) =
            opts = 123
            scream (opts, nice word, global value)

        scream it string = scream it.to string()

        transformed = dslify.transform(scream it string, as string: true)
        transformed.should.not.match(r/var ;/g)
        transformed.should.not.match(r/_dsl.opts/g)
        transformed.should.not.match(r/_dsl.niceWord/g)

    it 'preserves any existing function parameters'
        dsl = {
            scream (word1, word2) = "#(word1)!! #(word2)!!"
        }
        scream them (a, b) =
            scream (a, b)

        transformed = dslify.transform(scream them)
        transformed(dsl, "lasers", "sharks").should.equal "lasers!! sharks!!"

    it 'rewrites a function with global variables bound to a new _dsl argument'
        @{
            x + y
        } rewrites as @(_dsl)
            _dsl.x + _dsl.y

    it 'allows overriding the _dsl argument name'
        multiply (a, b) = a * b * c
        transformed = dslify.transform(multiply, dsl name: "xx")
        expected (xx, a, b) = a * b * xx.c
        normalise (transformed).should.equal (normalise(expected))

    it 'prepends the _dsl argument to any existing arguments'
        @(a, b) @{
            a + b + x
        } rewrites as @(_dsl, a, b) @{
            a + b + _dsl.x
        }

    it 'preserves property accessors'
        @{
            window.foo.bar
        } rewrites as @(_dsl)
            _dsl.window.foo.bar

    it 'does not bind property literals'
        @{
            foo { a = 1, b = 2 }
        } rewrites as @(_dsl)
            _dsl.foo { a = 1, b = 2 }

    it 'preserves variables declared in the outermost scope'
        @{
            foo = 1
            bar { a = foo }
        } rewrites as @(_dsl)
            foo = 1
            _dsl.bar { a = foo }

    it 'preserves variables declared in nested scopes'
        @{
            foo
                bar = 1
                { a = bar }
        } rewrites as @(_dsl)
            _dsl.foo
                bar = 1
                { a = bar }

    it 'rewrites global accessors in nested scopes'
        fn (x) =
            a (y) =
                b (z)
                fn (o) = p (o)
                fn (x)

            a 123

        (fn) rewrites as @(_dsl, x)
            a (y) =
                _dsl.b (_dsl.z)
                fn (o) = _dsl.p (o)
                fn (x)

            a 123

    it 'preserves nested function parameters'
        fn (x) =
            foo (a, b) @(c, d)
                x.bar(baz(a, b, c, d))
                bar (c) @(x)
                    c + x

        (fn) rewrites as @(_dsl, x)
            _dsl.foo (_dsl.a, _dsl.b) @(c, d)
                x.bar(_dsl.baz(_dsl.a, _dsl.b, c, d))
                _dsl.bar (c) @(x)
                    c + x

dslify = require '../index'
escodegen = require 'escodegen'
esprima = require 'esprima'

(actual) rewrites as (expected) =
    normalise (dslify.transform (actual)).should.equal (normalise(expected))

normalise (fn) =
    parsed = esprima.parse "(#(fn.to string()))".body.0
    escodegen.generate (parsed).replace "function anonymous(" "function ("

describe 'dslify'

    it 'rewrites a function so that global variables become DSL accessors'
        dsl = {
            scream (word) = "#(word)!!"
            nice word = "unicorns"
        }
        scream it () =
            scream (nice word)

        transformed = dslify.transform(scream it)
        transformed(dsl).should.equal "unicorns!!"

    it 'takes and returns JavaScript strings'
        scream it (nice word) =
            scream (nice word, global value)

        scream it string = scream it.to string()

        transformed = dslify.transform(scream it string, as string: true)
        transformed.should.be.a String

    it 'does not rewrite local variables'
        scream it (nice word) =
            opts = 123
            scream (opts, nice word, global value)

        scream it string = scream it.to string()

        transformed = dslify.transform(scream it string, as string: true)
        transformed.should.not.match(r/var ;/g)
        transformed.should.not.match(r/_dsl.opts/g)
        transformed.should.not.match(r/_dsl.niceWord/g)

    it 'preserves any existing function parameters'
        dsl = {
            scream (word1, word2) = "#(word1)!! #(word2)!!"
        }
        scream them (a, b) =
            scream (a, b)

        transformed = dslify.transform(scream them)
        transformed(dsl, "lasers", "sharks").should.equal "lasers!! sharks!!"

    it 'rewrites a function with global variables bound to a new _dsl argument'
        @{
            x + y
        } rewrites as @(_dsl)
            _dsl.x + _dsl.y

    it 'allows overriding the _dsl argument name'
        multiply (a, b) = a * b * c
        transformed = dslify.transform(multiply, dsl name: "xx")
        expected (xx, a, b) = a * b * xx.c
        normalise (transformed).should.equal (normalise(expected))

    it 'prepends the _dsl argument to any existing arguments'
        @(a, b) @{
            a + b + x
        } rewrites as @(_dsl, a, b) @{
            a + b + _dsl.x
        }

    it 'preserves property accessors'
        @{
            window.foo.bar
        } rewrites as @(_dsl)
            _dsl.window.foo.bar

    it 'does not bind property literals'
        @{
            foo { a = 1, b = 2 }
        } rewrites as @(_dsl)
            _dsl.foo { a = 1, b = 2 }

    it 'preserves variables declared in the outermost scope'
        @{
            foo = 1
            bar { a = foo }
        } rewrites as @(_dsl)
            foo = 1
            _dsl.bar { a = foo }

    it 'preserves variables declared in nested scopes'
        @{
            foo
                bar = 1
                { a = bar }
        } rewrites as @(_dsl)
            _dsl.foo
                bar = 1
                { a = bar }

    it 'preserves nested function parameters'
        fn (x) =
            foo (a, b) @(c, d)
                x.bar(baz(a, b, c, d))
                bar (c) @(x)
                    c + x

        (fn) rewrites as @(_dsl, x)
            _dsl.foo (_dsl.a, _dsl.b) @(c, d)
                x.bar(_dsl.baz(_dsl.a, _dsl.b, c, d))
                _dsl.bar (c) @(x)
                    c + x


new syntax for interpolation.pogo

new syntax for interpolation.pogo

exports.create interpolation () = {
    stack = []

    start interpolation () =
        self.stack.unshift {brackets = 0}

    open bracket () =
        self.stack.0.brackets = self.stack.0.brackets + 1

    close bracket () =
        self.stack.0.brackets = self.stack.0.brackets - 1

    finished interpolation () =
        self.stack.0.brackets < 0

    stop interpolation () =
        self.stack.shift ()

    interpolating () =
        self.stack.length > 0
}

exports: create interpolation! = {
    stack = []

    start interpolation! =
        :stack: unshift {brackets = 0}

    open bracket! =
        :stack: 0: brackets = :stack: 0: brackets + 1

    close bracket! =
        :stack: 0: brackets = :stack: 0: brackets - 1

    finished interpolation? =
        :stack: 0: brackets < 0

    stop interpolation! =
        :stack: shift!

    interpolating? =
        :stack: length > 0
}


macrosSpec with new syntax

macrosSpec with new syntax

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

expression @e =
    cg: complex expression [e]: expression?

id @name = cg: identifier @name
variable @name = cg: variable [name]
block @name = cg: block [] (cg: statements [variable @name])

spec 'macros'
    spec 'if'
        spec 'if'
            (expression [id 'if'. variable 'true'. block 'a']) should contain fields {
                is if expression
                cases [
                    {
                        condition {variable ['true']}
                        action {statements [{variable ['a']}]}
                    }
                ]
            }

        spec 'if else'
            (expression [id 'if'. variable 'true'. block 'a'. id 'else'. block 'b']) should contain fields {
                is if expression
                _else {statements [{variable ['b']}]}
                cases [
                    {
                        condition {variable ['true']}
                        action {statements [{variable ['a']}]}
                    }
                ]
            }

        spec 'if else if'
            (expression [id 'if'. variable 'true'. block 'a'. id 'else'. id 'if'. variable 'false'. block 'b']) should contain fields {
                is if expression
                _else @undefined
                cases [
                    {
                        condition {variable ['true']}
                        action {statements [{variable ['a']}]}
                    }
                    {
                        condition {variable ['false']}
                        action {statements [{variable ['b']}]}
                    }
                ]
            }

        spec 'if else if else'
            (expression [id 'if'. variable 'true'. block 'a'. id 'else'. id 'if'. variable 'false'. block 'b'. id 'else'. block 'c']) should contain fields {
                is if expression
                _else {statements [{variable ['c']}]}
                cases [
                    {
                        condition {variable ['true']}
                        action {statements [{variable ['a']}]}
                    }
                    {
                        condition {variable ['false']}
                        action {statements [{variable ['b']}]}
                    }
                ]
            }

        spec 'operators'
          spec 'a + b'
            op = cg: operator expression (cg: complex expression [[id 'a']])
            op: add operator '+' expression (cg: complex expression [[id 'b']])

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

          spec 'a + b + c'
            op = cg: operator expression (cg: complex expression [[id 'a']])
            op: add operator '+' expression (cg: complex expression [[id 'b']])
            op: add operator '+' expression (cg: complex expression [[id 'c']])

            (op: expression?) should contain fields {
              is operator
              operator '+'
              arguments [
                {variable ['a']}
                {variable ['b']}
                {variable ['c']}
              ]
            }

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

expression @e =
    cg: complex expression [e]: expression?

id @name = cg: identifier @name
variable @name = cg: variable [name]
block @name = cg: block [] (cg: statements [variable @name])

spec 'macros'
    spec 'if'
        spec 'if'
            (expression [id 'if'. variable 'true'. block 'a']) should contain fields #
                is if expression
                cases [
                    #
                        condition #{variable ['true']}
                        action #{statements [#{variable ['a']}]}
                ]

        spec 'if else'
            (expression [id 'if'. variable 'true'. block 'a'. id 'else'. block 'b']) should contain fields #
                is if expression
                _else #{statements [#{variable ['b']}]}
                cases [
                    #
                        condition #{variable ['true']}
                        action #{statements [#{variable ['a']}]}
                ]

        spec 'if else if'
            (expression [id 'if'. variable 'true'. block 'a'. id 'else'. id 'if'. variable 'false'. block 'b']) should contain fields #
                is if expression
                _else @undefined
                cases [
                    #
                        condition #{variable ['true']}
                        action #{statements [#{variable ['a']}]}

                    #
                        condition #{variable ['false']}
                        action #{statements [#{variable ['b']}]}
                ]

        spec 'if else if else'
            (expression [id 'if'. variable 'true'. block 'a'. id 'else'. id 'if'. variable 'false'. block 'b'. id 'else'. block 'c']) should contain fields #
                is if expression
                _else #{statements [#{variable ['c']}]}
                cases [
                    #
                        condition #{variable ['true']}
                        action #{statements [#{variable ['a']}]}

                    #
                        condition #{variable ['false']}
                        action #{statements [#{variable ['b']}]}
                ]

        spec 'operators'
          spec 'a + b'
            op = cg: operator expression (cg: complex expression [[id 'a']])
            op: add operator '+' expression (cg: complex expression [[id 'b']])

            (op: expression?) should contain fields #
              is operator
              operator '+'
              arguments [
                #{variable ['a']}
                #{variable ['b']}
              ]

          spec 'a + b + c'
            op = cg: operator expression (cg: complex expression [[id 'a']])
            op: add operator '+' expression (cg: complex expression [[id 'b']])
            op: add operator '+' expression (cg: complex expression [[id 'c']])

            (op: expression?) should contain fields #
              is operator
              operator '+'
              arguments [
                #{variable ['a']}
                #{variable ['b']}
                #{variable ['c']}
              ]


added test for --help

added test for --help

path = require 'path'
fs = require 'fs'
script = require '../scriptAssertions'
with args should output = script.with args should output
child process = require 'child_process'
net = require 'net'

describe 'pogo command'
    it "`process.argv` contains 'pogo', the name of the
         script executed, and the arguments from the command line" @(done)

        'console.log (process.argv)' with args ['one', 'two'] should output "[ 'pogo',
                                                                               '#(path.resolve '343111c34d666435dd7e88265c816cbfdbe68cd3.pogo')',
                                                                               'one',
                                                                               'two' ]" (done)

    it "`__filename` should be the name of the script" @(done)
        'console.log (__filename)' with args [] should output (path.resolve "5be55a44c52f14d048d19c020fd913199ae2e61c.pogo") (done)

    it "`__dirname` should be the name of the script" @(done)
        'console.log (__dirname)' with args [] should output (path.resolve ".") (done)

    it "runs script files even if they don't use the .pogo extension" @(done)
        'console.log "hi"' with args [] should output 'hi' (script filename: 'ascript', done)

    it "script can take same switches as pogo script, like --compile" @(done)
        'console.log (process.argv)' with args ['--compile'] should output "[ 'pogo',
                                                                              '#(path.resolve '343111c34d666435dd7e88265c816cbfdbe68cd3.pogo')',
                                                                              '--compile' ]" (done)

write file (filename, content, done) =
    fs.write file ("#(__dirname)/#(filename)", content, done)

expand pogo command (command) =
    if (r/^pogo /.test (command))
        pogo = __dirname + "/../../bin/pogo"

        command.replace r/^pogo/ (pogo)
    else
        command

spawn (command, args, done) =
    process = child process.spawn (expand pogo command (command), args, {cwd = __dirname, custom fds = [0, 1, 2]})
    done (nil, process)

run (command, callback) =
    expanded command = expand pogo command (command)
    child process.exec (expanded command, {cwd = __dirname}) @(error, stdout, stderr)
        callback (error, {stdout = stdout, stderr = stderr})

describe 'pogo --compile'
    after each
        fs.unlink! "#(__dirname)/toCompile.pogo"
        fs.unlink! "#(__dirname)/toCompile.js"

    it 'can compile a script'
        write file! "toCompile.pogo" "console.log 'hi'"
        pogo output = run! "pogo -c toCompile.pogo"
        pogo output.stdout.should.equal ''
        pogo output.stderr.should.equal ''

        node output = run! "node toCompile.js"
        node output.stdout.should.equal "hi\n"
        node output.stderr.should.equal ''

(n)ms = n
(n)s = n * 1000

after (milliseconds, do something) =
    set timeout (do something, milliseconds)

unlink! (file) =
    try
        fs.unlink! (file)
    catch (error)
        if (error.code != 'ENOENT')
            throw (error)

        // bug #6, remove this
        nil

describe 'pogo --help'
    it 'prints out help'
        pogo output = run! "pogo --help"

        pogo output.stdout.should.match r/usage:/
        pogo output.stdout.should.match r/--compile/
        pogo output.stdout.should.match r/--watch/

describe 'pogo --compile --if-stale'
    before each
        unlink! "#(__dirname)/toCompile.pogo"
        unlink! "#(__dirname)/toCompile.js"

    after each
        unlink! "#(__dirname)/toCompile.pogo"
        unlink! "#(__dirname)/toCompile.js"

    it 'compiles a pogo script if the js is missing'
        write file! "toCompile.pogo" "console.log 'hi'"
        pogo output = run! "pogo -cs toCompile.pogo"
        pogo output.stdout.should.equal "compiling toCompile.pogo => toCompile.js\n"
        pogo output.stderr.should.equal ''

        node output = run! "node toCompile.js"
        node output.stdout.should.equal "hi\n"
        node output.stderr.should.equal ''

    it 'compiles a pogo script if the js is out of date'
        write file! "toCompile.js" "console.log('old')"
        after! (1s)
        write file! "toCompile.pogo" "console.log 'new'"

        pogo output = run! "pogo -cs toCompile.pogo"
        pogo output.stdout.should.equal "compiling toCompile.pogo => toCompile.js\n"
        pogo output.stderr.should.equal ''

        node output = run! "node toCompile.js"
        node output.stdout.should.equal "new\n"
        node output.stderr.should.equal ''

    it "doesn't recompile the js if it the pogo is older"
        write file! "toCompile.pogo" "console.log 'pogo'"
        after! (1s)
        write file! "toCompile.js" "console.log('js')"

        pogo output = run! "pogo -cs toCompile.pogo"
        pogo output.stdout.should.equal ''
        pogo output.stderr.should.equal ''

        node output = run! "node toCompile.js"
        node output.stdout.should.equal "js\n"
        node output.stderr.should.equal ''

describe 'debugging'
    describe '--debug'
        it 'starts remote debugging'
            write file! "toDebug.pogo" "console.log 'bug!'"
            pogo output = run! 'pogo --debug toDebug.pogo'
            pogo output.stderr.should.equal "debugger listening on port 5858\n"
            pogo output.stdout.should.equal "bug!\n"

path = require 'path'
fs = require 'fs'
script = require '../scriptAssertions'
with args should output = script.with args should output
child process = require 'child_process'
net = require 'net'

describe 'pogo command'
    it "`process.argv` contains 'pogo', the name of the
         script executed, and the arguments from the command line" @(done)

        'console.log (process.argv)' with args ['one', 'two'] should output "[ 'pogo',
                                                                               '#(path.resolve '343111c34d666435dd7e88265c816cbfdbe68cd3.pogo')',
                                                                               'one',
                                                                               'two' ]" (done)

    it "`__filename` should be the name of the script" @(done)
        'console.log (__filename)' with args [] should output (path.resolve "5be55a44c52f14d048d19c020fd913199ae2e61c.pogo") (done)

    it "`__dirname` should be the name of the script" @(done)
        'console.log (__dirname)' with args [] should output (path.resolve ".") (done)

    it "runs script files even if they don't use the .pogo extension" @(done)
        'console.log "hi"' with args [] should output 'hi' (script filename: 'ascript', done)

    it "script can take same switches as pogo script, like --compile" @(done)
        'console.log (process.argv)' with args ['--compile'] should output "[ 'pogo',
                                                                              '#(path.resolve '343111c34d666435dd7e88265c816cbfdbe68cd3.pogo')',
                                                                              '--compile' ]" (done)

on success callback for (callback) =
    @(on success, always do)
        @(error, args, ...)
            if (error)
                callback (error)
            else
                on success (args, ...)

write file (filename, content, done) =
    fs.write file ("#(__dirname)/#(filename)", content, done)

expand pogo command (command) =
    if (r/^pogo /.test (command))
        pogo = __dirname + "/../../bin/pogo"

        command.replace r/^pogo/ (pogo)
    else
        command

spawn (command, args, done) =
    process = child process.spawn (expand pogo command (command), args, {cwd = __dirname, custom fds = [0, 1, 2]})
    done (nil, process)

run (command, done) =
    expanded command = expand pogo command (command)
    child process.exec (expanded command, {cwd = __dirname}, done)

describe 'pogo --compile'
    it 'can compile a script' @(done)
        on success = on success callback for (done)

        write file "toCompile.pogo" "console.log 'hi'" (on success
            run "pogo -c toCompile.pogo" (on success @(stdout, stderr)
                stdout.should.equal ''
                stderr.should.equal ''

                run "node toCompile.js" (on success @(stdout, stderr)
                    stdout.should.equal "hi\n"
                    stderr.should.equal ''

                    fs.unlink "#(__dirname)/toCompile.pogo"
                        fs.unlink "#(__dirname)/toCompile.js"
                            done ()
                )
            )
        )

(n)ms = n
(n)s = n * 1000

after (milliseconds, do something) =
    set timeout (do something, milliseconds)

describe 'pogo --compile --if-stale'
    it 'compiles a pogo script if the js is missing' @(done)
        on success = on success callback for (done)

        fs.unlink "#(__dirname)/toCompile.js"
            write file "toCompile.pogo" "console.log 'hi'" (on success
                run "pogo -cs toCompile.pogo" (on success @(stdout, stderr)
                    stdout.should.equal "compiling toCompile.pogo => toCompile.js\n"
                    stderr.should.equal ''

                    run "node toCompile.js" (on success @(stdout, stderr)
                        stdout.should.equal "hi\n"
                        stderr.should.equal ''

                        fs.unlink "#(__dirname)/toCompile.pogo"
                            fs.unlink "#(__dirname)/toCompile.js"
                                done ()
                    )
                )
            )

    it 'compiles a pogo script if the js is out of date' @(done)
        on success = on success callback for (done)

        write file "toCompile.js" "console.log('old')" (on success
            after (1s)
                write file "toCompile.pogo" "console.log 'new'" (on success
                    run "pogo -cs toCompile.pogo" (on success @(stdout, stderr)
                        stdout.should.equal "compiling toCompile.pogo => toCompile.js\n"
                        stderr.should.equal ''

                        run "node toCompile.js" (on success @(stdout, stderr)
                            stdout.should.equal "new\n"
                            stderr.should.equal ''

                            fs.unlink "#(__dirname)/toCompile.pogo"
                                fs.unlink "#(__dirname)/toCompile.js"
                                    done ()
                        )
                    )
                )
        )

    it "doesn't recompile the js if it the pogo is older" @(done)
        on success = on success callback for (done)

        write file "toCompile.pogo" "console.log 'pogo'" (on success
            after (1s)
                write file "toCompile.js" "console.log('js')" (on success
                    run "pogo -cs toCompile.pogo" (on success @(stdout, stderr)
                        stdout.should.equal ''
                        stderr.should.equal ''

                        run "node toCompile.js" (on success @(stdout, stderr)
                            stdout.should.equal "js\n"
                            stderr.should.equal ''

                            fs.unlink "#(__dirname)/toCompile.pogo"
                                fs.unlink "#(__dirname)/toCompile.js"
                                    done ()
                        )
                    )
                )
        )

describe 'debugging'
    describe '--debug'
        it 'starts remote debugging' @(done)
            on success = on success callback for (done)

            write file "toDebug.pogo" "console.log 'bug!'" (on success
                run 'pogo --debug toDebug.pogo' (on success @(stdout, stderr)
                    stderr.should.equal "debugger listening on port 5858\n"
                    stdout.should.equal "bug!\n"

                    done ()
                )
            )