diff --git a/parse.cm b/parse.cm index 7e490459..6dc0f54a 100644 --- a/parse.cm +++ b/parse.cm @@ -391,7 +391,7 @@ var parse = function(tokens, src, filename, tokenizer) { ast_node_end(param) if (tok.kind == "=" || tok.kind == "|") { advance() - param.expression = parse_expr() + param.expression = parse_assign_expr() } push(params, param) } else { diff --git a/parse.mach b/parse.mach index 76d89a78..03761786 100644 Binary files a/parse.mach and b/parse.mach differ diff --git a/vm_suite.ce b/vm_suite.ce index 7bbeb573..9ddc0d26 100644 --- a/vm_suite.ce +++ b/vm_suite.ce @@ -3671,6 +3671,167 @@ run("functino >>>! unsigned shift right", function() { assert_eq(>>>!(-1, 28), 15, ">>>! unsigned shift right") }) +// ============================================================================ +// FUNCTION DEFAULT PARAMETERS +// ============================================================================ + +run("default param basic", function() { + var fn = function(a = 3, b = 5) { return a + b } + assert_eq(fn(), 8, "both defaults") + assert_eq(fn(10), 15, "first provided") + assert_eq(fn(10, 20), 30, "both provided") +}) + +run("default param null triggers default", function() { + var fn = function(a = 99) { return a } + assert_eq(fn(null), 99, "null should trigger default") + assert_eq(fn(), 99, "missing should trigger default") + assert_eq(fn(0), 0, "zero should not trigger default") + assert_eq(fn(false), false, "false should not trigger default") + assert_eq(fn(""), "", "empty string should not trigger default") +}) + +run("default param pipe syntax", function() { + var fn = function(a | 3, b | 5) { return a + b } + assert_eq(fn(), 8, "pipe both defaults") + assert_eq(fn(10), 15, "pipe first provided") + assert_eq(fn(10, 20), 30, "pipe both provided") +}) + +run("default param object literal", function() { + var fn = function(a = {x: 1, y: 2}) { return a.x + a.y } + assert_eq(fn(), 3, "object default") + assert_eq(fn({x: 10, y: 20}), 30, "object provided") +}) + +run("default param array literal", function() { + var fn = function(a = [10, 20, 30]) { return a[1] } + assert_eq(fn(), 20, "array default") + assert_eq(fn([99]), null, "array provided") +}) + +run("default param nested object", function() { + var fn = function(a = {inner: {val: 42}}) { return a.inner.val } + assert_eq(fn(), 42, "nested object default") +}) + +run("default param expression", function() { + var fn = function(a = 1 + 2, b = 3 * 4) { return a + b } + assert_eq(fn(), 15, "expression defaults") +}) + +run("default param negative number", function() { + var fn = function(a = -1) { return a } + assert_eq(fn(), -1, "negative default") +}) + +run("default param function call", function() { + var double = function(x) { return x * 2 } + var fn = function(a = double(21)) { return a } + assert_eq(fn(), 42, "function call default") +}) + +run("default param function value", function() { + var fn = function(f = function() { return 42 }) { return f() } + assert_eq(fn(), 42, "function as default") +}) + +run("default param arrow function", function() { + var fn = function(f = (x) => x * 2) { return f(5) } + assert_eq(fn(), 10, "arrow function as default") +}) + +run("default param ternary in default", function() { + var fn = function(a = true ? 10 : 20) { return a } + assert_eq(fn(), 10, "ternary default") +}) + +run("default param string with comma", function() { + var fn = function(a = "hello, world") { return a } + assert_eq(fn(), "hello, world", "string comma default") +}) + +run("default param empty object", function() { + var fn = function(a = {}) { return is_object(a) } + assert_eq(fn(), true, "empty object default") +}) + +run("default param empty array", function() { + var fn = function(a = []) { return is_array(a) } + assert_eq(fn(), true, "empty array default") +}) + +run("default param multiple complex", function() { + var fn = function(a = {k: 1}, b = [4, 5], c = "hi") { + return text(a.k) + text(b[0]) + c + } + assert_eq(fn(), "14hi", "multiple complex defaults") + assert_eq(fn({k: 9}), "94hi", "first overridden") + assert_eq(fn({k: 9}, [7]), "97hi", "first two overridden") + assert_eq(fn({k: 9}, [7], "yo"), "97yo", "all overridden") +}) + +run("default param independent defaults", function() { + var fn = function(a = 1, b = 2) { return a + b } + assert_eq(fn(), 3, "both defaults") + assert_eq(fn(10), 12, "first provided") +}) + +run("default param arrow function syntax", function() { + var fn = (a = 7, b = 8) => a + b + assert_eq(fn(), 15, "arrow defaults") + assert_eq(fn(100), 108, "arrow first provided") + assert_eq(fn(100, 200), 300, "arrow both provided") +}) + +run("default param single arrow", function() { + var fn = (a = 42) => a + assert_eq(fn(), 42, "single arrow default") + assert_eq(fn(0), 0, "single arrow zero") +}) + +run("default param method shorthand", function() { + var obj = { + calc(a = 10, b = 20) { + return a + b + } + } + assert_eq(obj.calc(), 30, "method defaults") + assert_eq(obj.calc(1), 21, "method first provided") + assert_eq(obj.calc(1, 2), 3, "method both provided") +}) + +run("default param each fresh object", function() { + var fn = function(a = {count: 0}) { + a.count = a.count + 1 + return a.count + } + assert_eq(fn(), 1, "first call fresh object") + assert_eq(fn(), 1, "second call fresh object") +}) + +run("default param each fresh array", function() { + var fn = function(a = []) { + a[] = 1 + return length(a) + } + assert_eq(fn(), 1, "first call fresh array") + assert_eq(fn(), 1, "second call fresh array") +}) + +run("default param nested parens", function() { + var fn = function(a = (1 + 2) * 3) { return a } + assert_eq(fn(), 9, "nested parens default") +}) + +run("default param closure over outer", function() { + var base = 100 + var fn = function(a = base) { return a } + assert_eq(fn(), 100, "closure over outer") + base = 200 + assert_eq(fn(), 200, "closure sees updated outer") +}) + // ============================================================================ // SUMMARY // ============================================================================