import assert

EQ = assert.strictEqual

# Three arguments, all with default values, producing a text concatenation
x = ^(a:1, b:2, c:3){ ''+a+b+c }
EQ x{b:9}, "193"
EQ x{a:9}, "923"
EQ x{a:9, b:8}, "983"
EQ x{a:9, c:8}, "928"
EQ x{a:9, b:8, c:7}, "987"
EQ x(9), "923"
EQ x(9,8), "983"
EQ x(9,8,7), "987"
EQ x(), "123"

# No default value for an argument should result in the "undefined" atom
x = ^(a:1, b, c:3){ ''+a+b+c }
EQ x{b:9}, "193"
EQ x{a:9}, "9undefined3"
EQ x{a:9, b:8}, "983"
EQ x{a:9, c:8}, "9undefined8"
EQ x{a:9, b:8, c:7}, "987"
EQ x(9), "9undefined3"
EQ x(9,8), "983"
EQ x(9,8,7), "987"
EQ x(), "1undefined3"

# Keyword argument call-style
EQ x{b:8}, "183"
# Using an arbitrary object as keyword arguments by "reference" call-style
kwargs = {b:8}
EQ x{&kwargs}, "183"
# Passing an object as the first value with explicit positional call-style
# (parens) should pass the object through w/o interference
EQ x({b:8}), "[object Object]undefined3"
# Tampering should not be possible from JSON input
assert.equal x(JSON.parse '{"b":8}'), "[object Object]undefined3"
# ...but when explicitly allowed, it should work
kwargs = JSON.parse '{"b":8}'
EQ x{&kwargs}, "183"
EQ x{&JSON.parse('{"b":8}')}, "183"

# Access to arguments.keywords
x = ^(a:1, b:2, c:3){
  arguments.keywords ? Object.keys(arguments.keywords).sort().join(', ')
                     : 'undefined'
}
# No arguments or positional call-style should not produce any keywords
EQ x(), "undefined"
EQ x(11,22,33), "undefined"
EQ x({a:11,b:22,c:33}), "undefined"
# When passing keyword arguments, arguments.keywords should reflect those keywords
EQ x{a:11,b:22,c:33}, "__kw, a, b, c"
EQ x{a:11,c:33}, "__kw, a, c"
EQ x{b:11}, "__kw, b"
EQ x{}, "__kw"

# Test keyword argument values accessed from arguments.keywords
x = ^(a:1, b:2, c:3){
  kwargs = arguments.keywords
  Object.keys(kwargs).sort().filter(^(key){
    key != '__kw'
  }).map(^(key){
    key+'='+kwargs[key]
  }).join(', ')
}
EQ x{a:11,b:22,c:33}, "a=11, b=22, c=33"
EQ x{a:11,c:33}, "a=11, c=33"
EQ x{c:'1,2,3'}, "c=1,2,3"

# Test passing null for keyword arguments
x = ^(a) { ""+a }
EQ x(null), "null"

# Test using "constructor" as a keyword argument ("constructor" is a special key)
foo = ^(constructor) { ''+constructor }
EQ foo{constructor:'Hello'}, 'Hello'
EQ foo(), 'undefined'
EQ foo('Hell-oh'), 'Hell-oh'

