diff --git a/mcode.mach b/mcode.mach index bde182bc..e3fdf4a9 100644 Binary files a/mcode.mach and b/mcode.mach differ diff --git a/parse.cm b/parse.cm index 40028e04..7e490459 100644 --- a/parse.cm +++ b/parse.cm @@ -499,6 +499,9 @@ var parse = function(tokens, src, filename, tokenizer) { var index = null var arg = null var args_list = null + var one_node = null + var binop_node = null + var op = null if (node == null) return null while (true) { start = tok @@ -574,6 +577,10 @@ var parse = function(tokens, src, filename, tokenizer) { var node = null var expr = null var k = tok.kind + var operand = null + var one_node = null + var binop_node = null + var op = null if (k == "!") { advance() node = ast_node("!", start) @@ -602,19 +609,22 @@ var parse = function(tokens, src, filename, tokenizer) { ast_node_end(node) return node } - if (k == "++") { + if (k == "++" || k == "--") { advance() - node = ast_node("++", start) - node.expression = parse_unary() - node.postfix = false - ast_node_end(node) - return node - } - if (k == "--") { - advance() - node = ast_node("--", start) - node.expression = parse_unary() - node.postfix = false + operand = parse_unary() + one_node = ast_node("number", start) + one_node.number = 1 + one_node.value = "1" + ast_node_end(one_node) + op = "+" + if (k == "--") op = "-" + binop_node = ast_node(op, start) + binop_node.left = operand + binop_node.right = one_node + ast_node_end(binop_node) + node = ast_node("assign", start) + node.left = operand + node.right = binop_node ast_node_end(node) return node } @@ -699,6 +709,13 @@ var parse = function(tokens, src, filename, tokenizer) { "&&=": "&&=", "||=": "||=" } + var compound_binop = { + "+=": "+", "-=": "-", "*=": "*", "/=": "/", "%=": "%", + "<<=": "<<", ">>=": ">>", ">>>=": ">>>", + "&=": "&", "^=": "^", "|=": "|", "**=": "**", + "&&=": "&&", "||=": "||" + } + parse_assign = function(unused) { var left_node = parse_ternary() var start = null @@ -707,6 +724,8 @@ var parse = function(tokens, src, filename, tokenizer) { var node = null var left_kind = null var right_kind = null + var binop = null + var binop_node = null if (left_node == null) return null start = tok kind = assign_ops[tok.kind] @@ -719,12 +738,23 @@ var parse = function(tokens, src, filename, tokenizer) { advance() right_node = parse_assign() - node = ast_node(kind, start) - node.left = left_node - node.right = right_node - if (left_node.kind == "[" && left_node.right == null) node.push = true - if (right_node != null && right_node.kind == "[" && right_node.right == null) node.pop = true + binop = compound_binop[kind] + if (binop != null) { + binop_node = ast_node(binop, start) + binop_node.left = left_node + binop_node.right = right_node + ast_node_end(binop_node) + node = ast_node("assign", start) + node.left = left_node + node.right = binop_node + } else { + node = ast_node(kind, start) + node.left = left_node + node.right = right_node + if (left_node.kind == "[" && left_node.right == null) node.push = true + if (right_node != null && right_node.kind == "[" && right_node.right == null) node.pop = true + } ast_node_end(node) return node diff --git a/parse.mach b/parse.mach index 02f25e6f..76d89a78 100644 Binary files a/parse.mach and b/parse.mach differ diff --git a/vm_suite.ce b/vm_suite.ce index 6a969b42..7bbeb573 100644 --- a/vm_suite.ce +++ b/vm_suite.ce @@ -1058,6 +1058,197 @@ run("modulo equals", function() { if (x != 1) fail("modulo equals failed") }) +// ============================================================================ +// COMPOUND ASSIGNMENT ON PROPERTIES +// ============================================================================ + +run("plus equals on property", function() { + var obj = {x: 10} + obj.x += 5 + if (obj.x != 15) fail("obj.x += 5") +}) + +run("minus equals on property", function() { + var obj = {x: 10} + obj.x -= 3 + if (obj.x != 7) fail("obj.x -= 3") +}) + +run("times equals on property", function() { + var obj = {x: 4} + obj.x *= 3 + if (obj.x != 12) fail("obj.x *= 3") +}) + +run("divide equals on property", function() { + var obj = {x: 12} + obj.x /= 3 + if (obj.x != 4) fail("obj.x /= 3") +}) + +run("modulo equals on property", function() { + var obj = {x: 10} + obj.x %= 3 + if (obj.x != 1) fail("obj.x %= 3") +}) + +run("compound assign preserves other properties", function() { + var obj = {x: 10, y: 20} + obj.x += 5 + if (obj.x != 15) fail("x wrong") + if (obj.y != 20) fail("y changed") +}) + +run("compound assign nested property", function() { + var obj = {inner: {val: 10}} + obj.inner.val += 5 + if (obj.inner.val != 15) fail("nested += failed") +}) + +run("compound assign chained", function() { + var obj = {x: 1} + obj.x += 2 + obj.x += 3 + obj.x += 4 + if (obj.x != 10) fail("chained += failed") +}) + +// ============================================================================ +// COMPOUND ASSIGNMENT ON ARRAY ELEMENTS +// ============================================================================ + +run("plus equals on array element", function() { + var arr = [100, 200, 300] + arr[0] += 50 + if (arr[0] != 150) fail("arr[0] += 50") +}) + +run("minus equals on array element", function() { + var arr = [100, 200, 300] + arr[1] -= 50 + if (arr[1] != 150) fail("arr[1] -= 50") +}) + +run("times equals on array element", function() { + var arr = [5] + arr[0] *= 6 + if (arr[0] != 30) fail("arr[0] *= 6") +}) + +run("compound assign computed property", function() { + var obj = {a: 10, b: 20} + var key = "a" + obj[key] += 5 + if (obj.a != 15) fail("obj[key] += 5") +}) + +run("compound assign array element preserves others", function() { + var arr = [10, 20, 30] + arr[1] += 5 + if (arr[0] != 10) fail("arr[0] changed") + if (arr[1] != 25) fail("arr[1] wrong") + if (arr[2] != 30) fail("arr[2] changed") +}) + +// ============================================================================ +// PREFIX INCREMENT/DECREMENT ON PROPERTIES +// ============================================================================ + +run("prefix increment on property", function() { + var obj = {x: 5} + var y = ++obj.x + if (y != 6) fail("return value") + if (obj.x != 6) fail("side effect") +}) + +run("prefix decrement on property", function() { + var obj = {x: 5} + var y = --obj.x + if (y != 4) fail("return value") + if (obj.x != 4) fail("side effect") +}) + +run("prefix increment on array element", function() { + var arr = [10] + var y = ++arr[0] + if (y != 11) fail("return value") + if (arr[0] != 11) fail("side effect") +}) + +run("prefix decrement on array element", function() { + var arr = [10] + var y = --arr[0] + if (y != 9) fail("return value") + if (arr[0] != 9) fail("side effect") +}) + +// ============================================================================ +// INCREMENT/DECREMENT IN LOOPS AND EXPRESSIONS +// ============================================================================ + +run("compound assign in for loop", function() { + var sum = 0 + var i = 0 + for (i = 0; i < 5; i++) { + sum += i + } + if (sum != 10) fail("sum should be 10") +}) + +run("compound assign property in loop", function() { + var obj = {count: 0} + var i = 0 + for (i = 0; i < 5; i++) { + obj.count += 1 + } + if (obj.count != 5) fail("count should be 5") +}) + +run("prefix increment in expression", function() { + var x = 5 + var y = 10 + ++x + if (y != 16) fail("10 + ++x should be 16") + if (x != 6) fail("x should be 6") +}) + +run("compound assign bitwise and-equals", function() { + var x = 15 + x &= 6 + if (x != 6) fail("15 & 6 should be 6") +}) + +run("compound assign bitwise or-equals", function() { + var x = 5 + x |= 3 + if (x != 7) fail("5 | 3 should be 7") +}) + +run("compound assign bitwise xor-equals", function() { + var x = 5 + x ^= 3 + if (x != 6) fail("5 ^ 3 should be 6") +}) + +run("compound assign left shift-equals", function() { + var x = 1 + x <<= 3 + if (x != 8) fail("1 << 3 should be 8") +}) + +run("compound assign right shift-equals", function() { + var x = 16 + x >>= 2 + if (x != 4) fail("16 >> 2 should be 4") +}) + +run("compound assign bitwise on property", function() { + var obj = {flags: 5} + obj.flags |= 8 + if (obj.flags != 13) fail("5 | 8 should be 13") + obj.flags &= 12 + if (obj.flags != 12) fail("13 & 12 should be 12") +}) + // ============================================================================ // EDGE CASES AND SPECIAL VALUES // ============================================================================