reverted failing tests

reverted failing tests

script = require './scriptAssertions'
require 'chai'.should()

shouldOutput = script.shouldOutput
shouldThrow = script.shouldThrow

describe 'hashes'
    describe 'evaluation'
        it "a `true` hash entry does not need it's value specified"
            'print {one}' shouldOutput '{ one: true }'

        it 'a hash can have multiple entries, delimited by commas'
            "print {color 'red', size 'large'}" shouldOutput "{ color: 'red', size: 'large' }"

        it 'a hash can have multiple entries, delimited by new lines'
            "print {
                 color 'red'
                 size 'large'
             }" shouldOutput "{ color: 'red', size: 'large' }"

        it 'hash entries can be written with an equals "=" operator'
            "print {color = 'red', size = 'large'}" shouldOutput "{ color: 'red', size: 'large' }"

        it 'can define a hash as a statement'
            "{color = print 'red', size = print 'large'}
             print 'done'" shouldOutput "'red'
                                         'large'
                                         'done'"

    context "when it doesn't contain hash entries"
        it 'when it contains a method call, it should throw a parsing exception'
            @{
                script.evaluate script "print {
                                            console.log ('aaahh!')
                                        }"
            }.should.throw r/cannot be a hash entry/

        it 'when it contains a function call, it should throw a parsing exception'
            @{
                script.evaluate script "print {
                                            x (1, 23, 3)
                                        }"
            }.should.throw r/cannot be a hash entry/

        it 'when it contains a colon, it should throw a parsing exception'
            @{
                script.evaluate script "print {
                                          x: 8
                                        }"
            }.should.throw r/Parse error/

script = require './scriptAssertions'
require 'chai'.should()

shouldOutput = script.shouldOutput
shouldThrow = script.shouldThrow

describe 'hashes'
    describe 'evaluation'
        it "a `true` hash entry does not need it's value specified"
            'print {one}' shouldOutput '{ one: true }'

        it 'a hash can have multiple entries, delimited by commas'
            "print {color 'red', size 'large'}" shouldOutput "{ color: 'red', size: 'large' }"

        it 'a hash can have multiple entries, delimited by new lines'
            "print {
                 color 'red'
                 size 'large'
             }" shouldOutput "{ color: 'red', size: 'large' }"

        it 'hash entries can be written with an equals "=" operator'
            "print {color = 'red', size = 'large'}" shouldOutput "{ color: 'red', size: 'large' }"

        it 'can define a hash as a statement'
            "{color = print 'red', size = print 'large'}
             print 'done'" shouldOutput "'red'
                                         'large'
                                         'done'"

        it 'can define methods for strings'
            "obj = {'a' () = 'a'}
             print (obj.a ())" shouldOutput "'a'"

        it 'can define properties for numbers'
            "obj = {9 = 9}
             print (obj.(9))" shouldOutput "'a'"

    context "when it doesn't contain hash entries"
        it 'when it contains a method call, it should throw a parsing exception'
            @{
                script.evaluate script "print {
                                            console.log ('aaahh!')
                                        }"
            }.should.throw r/cannot be a hash entry/

        it 'when it contains a function call, it should throw a parsing exception'
            @{
                script.evaluate script "print {
                                            x (1, 23, 3)
                                        }"
            }.should.throw r/cannot be a hash entry/

        it 'when it contains a colon, it should throw a parsing exception'
            @{
                script.evaluate script "print {
                                          x: 8
                                        }"
            }.should.throw r/Parse error/


extending timeout of debug test, seems to take a bit longer on travis

path = require 'path'
fs = require 'fs'
script = require './pogoAssertions'
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

wait (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')"
        wait! (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'"
        wait! (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'
        this.timeout 3000

        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"

describe '`pogo` (interactive)'
    util = require 'util'

    pogo session () =
        pogo = child process.spawn (expand pogo command 'pogo', []) {
            cwd = __dirname
            stdio = 'pipe'
        }

        handle result = nil
        current output = ''
        first prompt = true

        pogo.stdout.on 'data' @(data)
            out = data.to string ()
            current output := current output + out
            if (r/^> $/m.test (current output))
                command output = current output.replace (r/\n?> $/, '')
                current output := ''
                if (first prompt)
                    first prompt := false
                else
                    handle result (command output)

        pogo.stderr.on 'data' @(data)
            console.log 'error'
            console.log (data.to string ())

        {
            issue (command) and expect (result, done) =
                handle result (actual result) :=
                    actual result.should.equal (result)
                    done ()

                pogo.stdin.write "#(command)\n"

            exit (done) =
                pogo.on 'exit' @(code)
                    done (nil, code)

                pogo.stdin.end ()
        }

    it 'evaluates a simple line of pogoscript'
        pogo = pogo session ()
        pogo.issue '8' and expect! '8'
        pogo.exit!

    it 'variables are shared among different lines'
        pogo = pogo session ()
        pogo.issue 'a = 8' and expect! '8'
        pogo.issue 'a' and expect! '8'
        pogo.exit!

path = require 'path'
fs = require 'fs'
script = require './pogoAssertions'
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

wait (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')"
        wait! (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'"
        wait! (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"

describe '`pogo` (interactive)'
    util = require 'util'

    pogo session () =
        pogo = child process.spawn (expand pogo command 'pogo', []) {
            cwd = __dirname
            stdio = 'pipe'
        }

        handle result = nil
        current output = ''
        first prompt = true

        pogo.stdout.on 'data' @(data)
            out = data.to string ()
            current output := current output + out
            if (r/^> $/m.test (current output))
                command output = current output.replace (r/\n?> $/, '')
                current output := ''
                if (first prompt)
                    first prompt := false
                else
                    handle result (command output)

        pogo.stderr.on 'data' @(data)
            console.log 'error'
            console.log (data.to string ())

        {
            issue (command) and expect (result, done) =
                handle result (actual result) :=
                    actual result.should.equal (result)
                    done ()

                pogo.stdin.write "#(command)\n"

            exit (done) =
                pogo.on 'exit' @(code)
                    done (nil, code)

                pogo.stdin.end ()
        }

    it 'evaluates a simple line of pogoscript'
        pogo = pogo session ()
        pogo.issue '8' and expect! '8'
        pogo.exit!

    it 'variables are shared among different lines'
        pogo = pogo session ()
        pogo.issue 'a = 8' and expect! '8'
        pogo.issue 'a' and expect! '8'
        pogo.exit!


test for preserving variables in nested scopes

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 '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 'preserves 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

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 '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 'preserves 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 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


fixed broken for each in versions

fixed broken for each in versions

_ = require 'underscore'

exports.(a) is less than (b) =
    parse version (v) =
        if (v.0 == 'v')
            v := v.substring (1)

        v.split '.'

    compare (v1, v2) =
        if (v1 > v2)
            1
        else if (v2 > v1)
            -1
        else
            0

    for each @(version numbers) in (_.zip (parse version (a), parse version (b)))
        comparison = compare (version numbers.0, version numbers.1)

        if (comparison)
            return (comparison < 0)

    false

_ = require 'underscore'

exports.(a) is less than (b) =
    parse version (v) =
        if (v.0 == 'v')
            v := v.substring (1)

        v.split '.'

    compare (v1, v2) =
        if (v1 > v2)
            1
        else if (v2 > v1)
            -1
        else
            0

    for each @(version numbers) in (_.zip (parse version (a), parse version (b))) @(version number)
        comparison = compare (version numbers.0, version numbers.1)

        if (comparison)
            return (comparison < 0)

    false


improved when example

improved when example

is (expected) (action) =
    action if (actual) matched =
        if (expected == actual)
            action

otherwise (action) =
    action if (actual) matched =
        action

when (actual, cases, ...) =
    for each @(action if matched) in (cases)
        action = action if (actual) matched
        if (action)
            return (action ())

print (args, ...) = console.log (args, ...)

x = 2

when (x) (
    is 0
        print "x is zero"

    is 1
        print "x is one"

    otherwise
        print "x is not zero or one"
)

is (expected) (action) =
    action if (actual) matched =
        if (expected == actual)
            action

otherwise (action) =
    action if (actual) matched =
        action

when (actual, cases) =
    for each @(action if matched) in (cases)
        action = action if (actual) matched
        if (action)
            return (action ())

print (args, ...) = console.log (args, ...)

x = 2

when (x) [
    is 0
        print "x is zero"

    is 1
        print "x is one"

    otherwise
        print "x is not zero or one"
]


if bug

if bug

script = require './scriptAssertions'

shouldOutput = script.shouldOutput

describe 'if'
  describe 'evaluation'
    it "returns the body"
      'print (
         if (true)
           "true"
         else
           "false"
       )' shouldOutput "'true'"

  describe 'promises'
    context 'when there no else clause'
      it 'resolves promises when in the then clause'
        'result = if (true)
           p ()!
           "result"

         print (result)' shouldOutput "'result'"

    context 'when there is an else clause'
      it 'resolves promises when in the else clause'
        'result = if (false)
           p ()!
           print "then"
         else
           p()!
           print "else"
           "result"

         print (result)' shouldOutput "'else'
                                       'result'"

    context 'when there are if else and else clauses'
      it 'resolves promises when in the else clause'
        'result = if (false)
           p ()!
           print "then"
         else if (true)
           p()!
           print "else if"
           "result"
         else
           p()!
           print "else"

         print (result)' shouldOutput "'else if'
                                       'result'"

    describe 'executing only enough conditions to find a true one'
      it 'only executes the first one'
        'condition (n, c) =
           p()!
           print "condition: #(n)"
           c

         result = if (condition (1, true)!)
           p ()!
           1
         else if (condition (2, true)!)
           p()!
           2
         else
           3

         print (result)' shouldOutput "'condition: 1'
                                       1"

      it 'executes the first and second'
        'condition (n, c) =
           p()!
           print "condition: #(n)"
           c

         result = if (condition (1, false)!)
           p ()!
           1
         else if (condition (2, true)!)
           p()!
           2
         else
           3

         print (result)' shouldOutput "'condition: 1'
                                       'condition: 2'
                                       2"


rename

rename

fs = require 'fs'
preparser = require './preparser'
ms = require '../lib/memorystream'
parse = require './parser': parse
uglify = require 'uglify-js'
errors = require './codeGenerator/errors'

preparse = preparser: create file parser!

generate code @term =
  memory stream = new (ms: MemoryStream)
  term: generate java script (memory stream)
  memory stream: to string?

beautify @code =
  ast = uglify: parser: parse @code
  uglify: uglify: gen_code @ast, beautify

exports: compile file @filename, ugly =
  js = js from pogo file @filename
  if (not @ugly)
    js = beautify @js

  js filename = js filename from pogo filename @filename
  fs: write file sync (js filename) @js

exports: preparse file @filename =
  contents = fs: read file sync @filename 'utf-8'
  preparsed pogo = preparse @contents
  console: log (preparsed pogo)

js filename from pogo filename @pogo =
  pogo: replace (new (RegExp '\.pogo$')) '' + '.js'

exports: run file @filename =
  js = js from pogo file @filename

  module: filename = fs: realpath sync @filename
  process: argv: 1 = module: filename
  module: _compile @js @filename

js from pogo file @filename =
  contents = fs: read file sync @filename 'utf-8'
  p = preparse @contents
  term = parse @p

  if (errors: has errors?)
    errors: print errors (source location printer, filename @filename, source @contents)
    process: exit 1
  else
    generate code @term

source location printer, filename, source =
  object =>
    :lines in range @range =
      lines = source: split (new (RegExp '\n'))
      lines: slice (range:from - 1) (range:to)

    :print lines in range, prefix '', from, to =
      for each ?line in (:lines in range, from @from, to @to)
          process:stderr:write (prefix + line + '\n')

    :print location @location =
      process:stderr:write (filename + ':' + location: first line + '\n')

      if (location: first line == location: last line)
        :print lines in range, from (location: first line), to (location: last line)
        spaces = :' ' times (location: first column)
        markers = :'^' times (location: last column - location:first column)
        process:stderr:write (spaces + markers + '\n')
      else
        :print lines in range, prefix '> ', from (location: first line), to (location: last line)

    :@s times @n =
        strings = []
        for {i = 0} {i < n} {i = i + 1}
          strings: push @s

        strings: join ''

require: extensions: '.pogo' = ?module ?filename
  content = js from pogo file @filename
  module: _compile @content @filename

fs = require 'fs'
preparser = require './preparser'
ms = require '../lib/memorystream'
parse = require './parser': parse
uglify = require 'uglify-js'
errors = require './codeGenerator/errors'

preparse = preparser: create file parser!

generate code @term =
  memory stream = new (ms: MemoryStream)
  term: generate java script (memory stream)
  memory stream: to string?

beautify @code =
  ast = uglify: parser: parse @code
  uglify: uglify: gen_code @ast, beautify

exports: compile file @filename, ugly =
  js = js from pogo file @filename
  if (not @ugly)
    js = beautify @js

  js filename = js filename from pogo filename @filename
  fs: write file sync (js filename) @js

exports: preparse file @filename =
  contents = fs: read file sync @filename 'utf-8'
  preparsed pogo = preparse @contents
  console: log (preparsed pogo)

js filename from pogo filename @pogo =
  pogo: replace (new (RegExp '\.pogo$')) '' + '.js'

exports: run file @filename =
  js = js from pogo file @filename

  module: filename = fs: realpath sync @filename
  process: argv: 1 = module: filename
  module: _compile @js @filename

js from pogo file @filename =
  contents = fs: read file sync @filename 'utf-8'
  p = preparse @contents
  term = parse @p

  if (errors: has errors?)
    errors: print errors (source location printer, filename @filename, source @contents)
    process: exit 1
  else
    generate code @term

source location printer, filename, source =
  object =>
    :lines in range @range =
      lines = source: split (new (RegExp '\n'))
      lines: slice (range:from - 1) (range:to)

    :print lines in range, prefix '', from, to =
      for each ?line in (:lines in range, from @from, to @to)
          process:stderr:write (prefix + line + '\n')

    :print location @location =
      process:stderr:write (filename + ':' + location: first line + '\n')

      if (location: first line == location: last line)
        :print lines in range, from (location: first line), to (location: last line)
        spaces = :duplicate string ' ' (location: first column) times
        markers = :duplicate string '^' (location: last column - location:first column) times
        process:stderr:write (spaces + markers + '\n')
      else
        :print lines in range, prefix '> ', from (location: first line), to (location: last line)

    :duplicate string @s @n times =
        strings = []
        for {i = 0} {i < n} {i = i + 1}
          strings: push @s

        strings: join ''

require: extensions: '.pogo' = ?module ?filename
  content = js from pogo file @filename
  module: _compile @content @filename


fixed failing test for async function in parser

require './assertions'

parser = require '../src/bootstrap/parser'
require './parserAssertions'

describe 'parser'
    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 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 scope
                        statements [{
                            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 {
                                is scope
                                statements [{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 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 {
                is statements
                statements [
                    {
                        is definition
                        target { is variable, name ['async', 'result'] }
                        source {
                            function {variable ['delete', 'everything']}
                            function arguments []
                        }
                    }
                    { is variable, name ['async', 'result'] }
                ]
            }

        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 {
                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 {
                is field reference
                object {
                    is method call
                    object {variable ['object']}
                    name ['method']
                    method arguments []
                }
                name ['field']
            }

    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 method call
                object {
                    is method call
                    object {variable ['o']}
                    name ['m']
                    method arguments [{integer 2}]
                }

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

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

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

        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 'assignment'
        it 'assignment'
            (expression 'x = 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 command'
            (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 'when 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'
        tokens = 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 {
            statements [
                {variable ['a']}
                {variable ['b']}
            ]
        }

require './assertions'

parser = require '../src/bootstrap/parser'
require './parserAssertions'

describe 'parser'
    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 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 scope
                        statements [{
                            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 {
                                is scope
                                statements [{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 no argument'
            (expression 'delete everything!') should contain fields {
                function {variable ['delete', 'everything']}
                function arguments []
            }

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

        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 {
                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 {
                is field reference
                object {
                    is method call
                    object {variable ['object']}
                    name ['method']
                    method arguments []
                }
                name ['field']
            }

    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 method call
                object {
                    is method call
                    object {variable ['o']}
                    name ['m']
                    method arguments [{integer 2}]
                }

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

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

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

        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 'assignment'
        it 'assignment'
            (expression 'x = 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 command'
            (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 'when 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'
        tokens = 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 {
            statements [
                {variable ['a']}
                {variable ['b']}
            ]
        }


better output for server.pogo

better output for server.pogo

http = require 'http'

after @time @block =
    set timeout @block @time

@n seconds =
    n * 1000

@n minutes =
    (@n * 60) seconds

server = http : create server #req #res
    res : write "stuff\n"

    after (0.05 minutes)
        res : write "stuff after 0.05 minutes\n"

        after (0.5 seconds)
            res : end "stuff after 0.5 seconds\n"

port = 8000
server : listen @port
console: log "run > curl localhost:@port"

http = require 'http'

after @time @block =
    set timeout @block @time

@n seconds =
    n * 1000

@n minutes =
    (@n * 60) seconds

server = http : create server #req #res
    res : write "stuff\n"

    after (0.2 minutes)
        res : write "stuff\n"

        after (0.5 seconds)
            res : end "hello world\n"

server : listen 8000