From 747227de40e656ef22f65f745490c4c0d4aac5b8 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Tue, 10 Feb 2026 06:51:26 -0600 Subject: [PATCH 1/4] better parse errors --- docs/language.md | 2 +- parse.cm | 89 ++++++++++++++++++++ tests/decl_restrictions.ce | 165 +++++++++++++++++++++++++++++++++++++ 3 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 tests/decl_restrictions.ce diff --git a/docs/language.md b/docs/language.md index 62224d14..962450de 100644 --- a/docs/language.md +++ b/docs/language.md @@ -11,7 +11,7 @@ type: "docs" ### Variables and Constants -Variables are declared with `var`, constants with `def`. All declarations must be initialized and must appear at the function body level — not inside `if`, `while`, `for`, or bare `{}` blocks. +Variables are declared with `var`, constants with `def`. All declarations must be initialized and must appear at the function body level — not inside `if`, `while`, `for`, or `do` blocks. ```javascript var x = 10 diff --git a/parse.cm b/parse.cm index da590c27..40028e04 100644 --- a/parse.cm +++ b/parse.cm @@ -9,6 +9,9 @@ var parse = function(tokens, src, filename, tokenizer) { var tok = null var got_lf = false var prev_tok = null + var _control_depth = 0 + var _control_type = null + var _expecting_body = false var advance = function() { var t = null @@ -176,6 +179,9 @@ var parse = function(tokens, src, filename, tokenizer) { var sub_ast = null var sub_stmt = null var sub_expr = null + var meth_old_cd = 0 + var meth_old_ct = null + var meth_old_eb = false if (k == "number") { node = ast_node("number", start) @@ -399,6 +405,12 @@ var parse = function(tokens, src, filename, tokenizer) { else if (tok.kind == "eof") parse_error(tok, "unterminated method parameter list") if (length(params) > 4) parse_error(tok, "functions cannot have more than 4 parameters") fn.arity = length(params) + meth_old_cd = _control_depth + meth_old_ct = _control_type + meth_old_eb = _expecting_body + _control_depth = 0 + _control_type = null + _expecting_body = false if (tok.kind == "{") { advance() fn.statements = parse_block_statements() @@ -407,6 +419,9 @@ var parse = function(tokens, src, filename, tokenizer) { } else { parse_error(tok, "expected '{' for method body") } + _control_depth = meth_old_cd + _control_type = meth_old_ct + _expecting_body = meth_old_eb fn.function_nr = function_nr function_nr = function_nr + 1 ast_node_end(fn) @@ -788,6 +803,9 @@ var parse = function(tokens, src, filename, tokenizer) { var prev_names = null var pname = null var old_dis = 0 + var old_cd = _control_depth + var old_ct = _control_type + var old_eb = _expecting_body if (in_disruption) { parse_error(tok, "cannot define function inside disruption clause") @@ -834,6 +852,9 @@ var parse = function(tokens, src, filename, tokenizer) { if (length(params) > 4) parse_error(tok, "functions cannot have more than 4 parameters") node.arity = length(params) + _control_depth = 0 + _control_type = null + _expecting_body = false if (tok.kind == "{") { advance() stmts = parse_block_statements() @@ -859,6 +880,9 @@ var parse = function(tokens, src, filename, tokenizer) { } } + _control_depth = old_cd + _control_type = old_ct + _expecting_body = old_eb node.function_nr = function_nr function_nr = function_nr + 1 ast_node_end(node) @@ -875,6 +899,9 @@ var parse = function(tokens, src, filename, tokenizer) { var expr = null var prev_names = null var pname = null + var old_cd = _control_depth + var old_ct = _control_type + var old_eb = _expecting_body node.arrow = true if (in_disruption) { @@ -925,6 +952,9 @@ var parse = function(tokens, src, filename, tokenizer) { advance() } + _control_depth = 0 + _control_type = null + _expecting_body = false if (tok.kind == "{") { advance() stmts = parse_block_statements() @@ -940,6 +970,9 @@ var parse = function(tokens, src, filename, tokenizer) { node.statements = stmts } + _control_depth = old_cd + _control_type = old_ct + _expecting_body = old_eb node.function_nr = function_nr function_nr = function_nr + 1 ast_node_end(node) @@ -971,8 +1004,25 @@ var parse = function(tokens, src, filename, tokenizer) { var elif = null var p1_tok = null var labeled_stmt = null + var depth = 0 + var saved_ct = null + var saved_cd = 0 + var saved_eb = false if (k == "{") { + if (!_expecting_body) { + parse_error(start, "bare block '{ ... }' is not a valid statement; use a function, if, while, or for instead") + advance() + depth = 1 + while (tok.kind != "eof" && depth > 0) { + if (tok.kind == "{") depth = depth + 1 + else if (tok.kind == "}") depth = depth - 1 + if (depth > 0) advance() + } + if (tok.kind == "}") advance() + return null + } + _expecting_body = false node = ast_node("block", start) advance() stmts = parse_block_statements() @@ -983,6 +1033,9 @@ var parse = function(tokens, src, filename, tokenizer) { } if (k == "var" || k == "def") { + if (_control_depth > 0) { + parse_error(start, "'" + k + "' declarations must appear at function body level, not inside '" + _control_type + "'; move this declaration before the '" + _control_type + "' statement") + } kind_name = k is_def = (k == "def") advance() @@ -1009,6 +1062,8 @@ var parse = function(tokens, src, filename, tokenizer) { } } else if (is_def) { parse_error(start, "missing initializer for constant '" + var_name + "'") + } else { + parse_error(start, "'var' declarations must be initialized; use 'var " + var_name + " = null' if no value is needed") } ast_node_end(node) push(decls, node) @@ -1037,6 +1092,11 @@ var parse = function(tokens, src, filename, tokenizer) { else parse_error(tok, "expected ')' after if condition") then_stmts = [] node.then = then_stmts + saved_ct = _control_type + saved_cd = _control_depth + _control_type = "if" + _control_depth = _control_depth + 1 + _expecting_body = true body = parse_statement() if (body != null) push(then_stmts, body) else_ifs = [] @@ -1044,15 +1104,22 @@ var parse = function(tokens, src, filename, tokenizer) { if (tok.kind == "else") { advance() if (tok.kind == "if") { + _control_depth = saved_cd + _control_type = saved_ct elif = parse_statement() if (elif != null) push(else_ifs, elif) + ast_node_end(node) + return node } else { else_stmts = [] node.else = else_stmts + _expecting_body = true body = parse_statement() if (body != null) push(else_stmts, body) } } + _control_depth = saved_cd + _control_type = saved_ct ast_node_end(node) return node } @@ -1068,8 +1135,15 @@ var parse = function(tokens, src, filename, tokenizer) { else parse_error(tok, "expected ')' after while condition") stmts = [] node.statements = stmts + saved_ct = _control_type + saved_cd = _control_depth + _control_type = "while" + _control_depth = _control_depth + 1 + _expecting_body = true body = parse_statement() if (body != null) push(stmts, body) + _control_depth = saved_cd + _control_type = saved_ct ast_node_end(node) return node } @@ -1079,8 +1153,15 @@ var parse = function(tokens, src, filename, tokenizer) { advance() stmts = [] node.statements = stmts + saved_ct = _control_type + saved_cd = _control_depth + _control_type = "do" + _control_depth = _control_depth + 1 + _expecting_body = true body = parse_statement() if (body != null) push(stmts, body) + _control_depth = saved_cd + _control_type = saved_ct if (tok.kind == "while") advance() else parse_error(tok, "expected 'while' after do body") if (tok.kind == "(") advance() @@ -1101,6 +1182,7 @@ var parse = function(tokens, src, filename, tokenizer) { else parse_error(tok, "expected '(' after for") if (tok.kind != ";") { if (tok.kind == "var" || tok.kind == "def") { + parse_error(tok, "'" + tok.kind + "' declarations cannot appear in the for initializer; declare variables before the for loop") init = parse_statement() node.init = init } else { @@ -1124,8 +1206,15 @@ var parse = function(tokens, src, filename, tokenizer) { else parse_error(tok, "expected ')' after for clauses") stmts = [] node.statements = stmts + saved_ct = _control_type + saved_cd = _control_depth + _control_type = "for" + _control_depth = _control_depth + 1 + _expecting_body = true body = parse_statement() if (body != null) push(stmts, body) + _control_depth = saved_cd + _control_type = saved_ct ast_node_end(node) return node } diff --git a/tests/decl_restrictions.ce b/tests/decl_restrictions.ce new file mode 100644 index 00000000..c2ce2280 --- /dev/null +++ b/tests/decl_restrictions.ce @@ -0,0 +1,165 @@ +// Declaration restriction tests +// Run: ./cell tests/decl_restrictions.ce + +var tokenize = use("tokenize") +var parse = use("parse") + +var passed = 0 +var failed = 0 +var error_names = [] +var error_reasons = [] +var fail_msg = "" + +var _i = 0 +for (_i = 0; _i < 20; _i++) { + error_names[] = null + error_reasons[] = null +} + +var fail = function(msg) { + fail_msg = msg + disrupt +} + +var run = function(name, fn) { + fail_msg = "" + fn() + passed = passed + 1 +} disruption { + error_names[failed] = name + error_reasons[failed] = fail_msg == "" ? "disruption" : fail_msg + failed = failed + 1 +} + +var parse_snippet = function(src) { + var result = tokenize(src, "") + var ast = parse(result.tokens, src, "", tokenize) + return ast +} + +var has_error = function(ast, substring) { + if (ast.errors == null) return false + var i = 0 + while (i < length(ast.errors)) { + if (search(ast.errors[i].message, substring) != null) return true + i = i + 1 + } + return false +} + +var has_no_errors = function(ast) { + return ast.errors == null || length(ast.errors) == 0 +} + +// === BARE BLOCK === + +run("bare block rejected", function() { + var ast = parse_snippet("{ var x = 1 }") + if (!has_error(ast, "bare block")) fail("expected 'bare block' error, got: " + text(ast.errors)) +}) + +// === VAR IN IF (braces) === + +run("var in if braces", function() { + var ast = parse_snippet("if (true) { var x = 1 }") + if (!has_error(ast, "not inside 'if'")) fail("expected 'not inside if' error, got: " + text(ast.errors)) +}) + +// === VAR IN IF (no braces) === + +run("var in if no braces", function() { + var ast = parse_snippet("if (true) var x = 1") + if (!has_error(ast, "not inside 'if'")) fail("expected 'not inside if' error, got: " + text(ast.errors)) +}) + +// === VAR IN WHILE === + +run("var in while", function() { + var ast = parse_snippet("while (true) { var x = 1; break }") + if (!has_error(ast, "not inside 'while'")) fail("expected 'not inside while' error, got: " + text(ast.errors)) +}) + +// === VAR IN FOR INIT === + +run("var in for init", function() { + var ast = parse_snippet("for (var i = 0; i < 1; i++) {}") + if (!has_error(ast, "for initializer")) fail("expected 'for initializer' error, got: " + text(ast.errors)) +}) + +// === VAR IN FOR BODY === + +run("var in for body", function() { + var ast = parse_snippet("var i = 0; for (i = 0; i < 1; i++) { var x = 1 }") + if (!has_error(ast, "not inside 'for'")) fail("expected 'not inside for' error, got: " + text(ast.errors)) +}) + +// === VAR IN DO === + +run("var in do", function() { + var ast = parse_snippet("do { var x = 1; break } while (true)") + if (!has_error(ast, "not inside 'do'")) fail("expected 'not inside do' error, got: " + text(ast.errors)) +}) + +// === DEF IN IF === + +run("def in if", function() { + var ast = parse_snippet("if (true) { def x = 1 }") + if (!has_error(ast, "not inside 'if'")) fail("expected 'not inside if' error, got: " + text(ast.errors)) +}) + +// === UNINITIALIZED VAR === + +run("uninitialized var", function() { + var ast = parse_snippet("var x") + if (!has_error(ast, "must be initialized")) fail("expected 'must be initialized' error, got: " + text(ast.errors)) +}) + +// === NESTED: VAR IN IF INSIDE WHILE === + +run("nested var in if inside while", function() { + var ast = parse_snippet("while (true) { if (true) { var x = 1 }; break }") + if (!has_error(ast, "not inside 'if'")) fail("expected 'not inside if' error, got: " + text(ast.errors)) +}) + +// === VALID: NESTED FUNCTION === + +run("valid nested fn in control flow", function() { + var ast = parse_snippet("if (true) { var fn = function() { var x = 1; return x } }") + // The var inside the function is fine; only the var fn = ... inside if should error + if (!has_error(ast, "not inside 'if'")) fail("expected error for var fn inside if") + // But there should NOT be an error about var x inside the function body + var i = 0 + var bad = false + if (ast.errors != null) { + while (i < length(ast.errors)) { + if (search(ast.errors[i].message, "var x") != null) bad = true + i = i + 1 + } + } + if (bad) fail("should not error on var inside nested function") +}) + +// === VALID: NORMAL VAR === + +run("valid normal var", function() { + var ast = parse_snippet("var x = 1") + if (!has_no_errors(ast)) fail("expected no errors, got: " + text(ast.errors)) +}) + +// === VALID: VAR IN FUNCTION BODY === + +run("valid var in function body", function() { + var ast = parse_snippet("var fn = function() { var x = 1; return x }") + if (!has_no_errors(ast)) fail("expected no errors, got: " + text(ast.errors)) +}) + +// === SUMMARY === + +print(text(passed) + " passed, " + text(failed) + " failed out of " + text(passed + failed)) +var _j = 0 +if (failed > 0) { + print("") + for (_j = 0; _j < failed; _j++) { + print(" FAIL " + error_names[_j] + ": " + error_reasons[_j]) + } +} From 877250b1d85906fc8667e5bd371bbf30ba742445 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Tue, 10 Feb 2026 07:12:27 -0600 Subject: [PATCH 2/4] decomposed mcode --- mcode.cm | 479 +++++++++++++++++++++++++++++++++++++++++++++++-- qbe.cm | 295 +++++++++++++++++++++++++++++- source/mcode.c | 214 ++++++++++++++++++++++ 3 files changed, 977 insertions(+), 11 deletions(-) diff --git a/mcode.cm b/mcode.cm index a4720e73..d8a30ecd 100644 --- a/mcode.cm +++ b/mcode.cm @@ -227,6 +227,463 @@ var mcode = function(ast) { add_instr([op, slot, label]) } + // ---- Decomposed op emission helpers ---- + + // Helper: check if an AST node is a known-int literal + var is_known_int = function(node) { + if (node == null) { return false } + return node.kind == "number" && is_integer(node.number) + } + + // Helper: check if an AST node is a known-text literal + var is_known_text = function(node) { + if (node == null) { return false } + return node.kind == "text" || node.kind == "text literal" + } + + // Helper: check if an AST node is a known-number literal (int or float) + var is_known_number = function(node) { + if (node == null) { return false } + return node.kind == "number" + } + + // Helper: check if an AST node is a known-bool literal + var is_known_bool = function(node) { + if (node == null) { return false } + return node.kind == "true" || node.kind == "false" + } + + // Helper: check if an AST node is a known-null literal + var is_known_null = function(node) { + if (node == null) { return false } + return node.kind == "null" + } + + // emit_add_decomposed: int path -> text path -> float path -> disrupt + var emit_add_decomposed = function(dest, left, right, left_node, right_node) { + var t0 = 0 + var t1 = 0 + var left_is_int = is_known_int(left_node) + var left_is_text = is_known_text(left_node) + var left_is_num = is_known_number(left_node) + var right_is_int = is_known_int(right_node) + var right_is_text = is_known_text(right_node) + var right_is_num = is_known_number(right_node) + var not_int = null + var not_text = null + var done = null + var err = null + + // Both sides known int + if (left_is_int && right_is_int) { + emit_3("add_int", dest, left, right) + return null + } + // Both sides known text + if (left_is_text && right_is_text) { + emit_3("concat", dest, left, right) + return null + } + // Both sides known number (but not both int) + if (left_is_num && right_is_num) { + if (left_is_int && right_is_int) { + emit_3("add_int", dest, left, right) + } else { + emit_3("add_float", dest, left, right) + } + return null + } + + not_int = gen_label("add_ni") + not_text = gen_label("add_nt") + done = gen_label("add_done") + err = gen_label("add_err") + + // Int path + t0 = alloc_slot() + if (!left_is_int) { + emit_2("is_int", t0, left) + emit_jump_cond("jump_false", t0, not_int) + } + t1 = alloc_slot() + if (!right_is_int) { + emit_2("is_int", t1, right) + emit_jump_cond("jump_false", t1, not_int) + } + emit_3("add_int", dest, left, right) + emit_jump(done) + + // Text path + emit_label(not_int) + if (!left_is_text) { + emit_2("is_text", t0, left) + emit_jump_cond("jump_false", t0, not_text) + } + if (!right_is_text) { + emit_2("is_text", t1, right) + emit_jump_cond("jump_false", t1, not_text) + } + emit_3("concat", dest, left, right) + emit_jump(done) + + // Float path + emit_label(not_text) + if (!left_is_num) { + emit_2("is_num", t0, left) + emit_jump_cond("jump_false", t0, err) + } + if (!right_is_num) { + emit_2("is_num", t1, right) + emit_jump_cond("jump_false", t1, err) + } + emit_3("add_float", dest, left, right) + emit_jump(done) + + emit_label(err) + emit_0("disrupt") + emit_label(done) + return null + } + + // emit_numeric_binop: int path -> float path -> disrupt + var emit_numeric_binop = function(int_op, float_op, dest, left, right, left_node, right_node) { + var t0 = 0 + var t1 = 0 + var left_is_int = is_known_int(left_node) + var left_is_num = is_known_number(left_node) + var right_is_int = is_known_int(right_node) + var right_is_num = is_known_number(right_node) + var not_int = null + var done = null + var err = null + + // Both sides known int + if (left_is_int && right_is_int) { + emit_3(int_op, dest, left, right) + return null + } + // Both sides known number (but not both int) + if (left_is_num && right_is_num) { + emit_3(float_op, dest, left, right) + return null + } + + not_int = gen_label("num_ni") + done = gen_label("num_done") + err = gen_label("num_err") + + t0 = alloc_slot() + if (!left_is_int) { + emit_2("is_int", t0, left) + emit_jump_cond("jump_false", t0, not_int) + } + t1 = alloc_slot() + if (!right_is_int) { + emit_2("is_int", t1, right) + emit_jump_cond("jump_false", t1, not_int) + } + emit_3(int_op, dest, left, right) + emit_jump(done) + + emit_label(not_int) + if (!left_is_num) { + emit_2("is_num", t0, left) + emit_jump_cond("jump_false", t0, err) + } + if (!right_is_num) { + emit_2("is_num", t1, right) + emit_jump_cond("jump_false", t1, err) + } + emit_3(float_op, dest, left, right) + emit_jump(done) + + emit_label(err) + emit_0("disrupt") + emit_label(done) + return null + } + + // emit_eq_decomposed: identical -> int -> float -> text -> null -> bool -> mismatch(false) + var emit_eq_decomposed = function(dest, left, right, left_node, right_node) { + var t0 = 0 + var t1 = 0 + var done = gen_label("eq_done") + var not_int = gen_label("eq_ni") + var not_num = gen_label("eq_nn") + var not_text = gen_label("eq_nt") + var not_null = gen_label("eq_nnl") + var not_bool = gen_label("eq_nb") + + // Identical check + emit_3("is_identical", dest, left, right) + emit_jump_cond("jump_true", dest, done) + + // Int path + t0 = alloc_slot() + emit_2("is_int", t0, left) + emit_jump_cond("jump_false", t0, not_int) + t1 = alloc_slot() + emit_2("is_int", t1, right) + emit_jump_cond("jump_false", t1, not_int) + emit_3("eq_int", dest, left, right) + emit_jump(done) + + // Float path + emit_label(not_int) + emit_2("is_num", t0, left) + emit_jump_cond("jump_false", t0, not_num) + emit_2("is_num", t1, right) + emit_jump_cond("jump_false", t1, not_num) + emit_3("eq_float", dest, left, right) + emit_jump(done) + + // Text path + emit_label(not_num) + emit_2("is_text", t0, left) + emit_jump_cond("jump_false", t0, not_text) + emit_2("is_text", t1, right) + emit_jump_cond("jump_false", t1, not_text) + emit_3("eq_text", dest, left, right) + emit_jump(done) + + // Null path + emit_label(not_text) + emit_2("is_null", t0, left) + emit_jump_cond("jump_false", t0, not_null) + emit_2("is_null", t1, right) + emit_jump_cond("jump_false", t1, not_null) + emit_1("true", dest) + emit_jump(done) + + // Bool path + emit_label(not_null) + emit_2("is_bool", t0, left) + emit_jump_cond("jump_false", t0, not_bool) + emit_2("is_bool", t1, right) + emit_jump_cond("jump_false", t1, not_bool) + emit_3("eq_bool", dest, left, right) + emit_jump(done) + + // Mismatch -> false + emit_label(not_bool) + emit_1("false", dest) + emit_label(done) + return null + } + + // emit_ne_decomposed: identical -> int -> float -> text -> null -> bool -> mismatch(true) + var emit_ne_decomposed = function(dest, left, right, left_node, right_node) { + var t0 = 0 + var t1 = 0 + var done = gen_label("ne_done") + var not_ident = gen_label("ne_nid") + var not_int = gen_label("ne_ni") + var not_num = gen_label("ne_nn") + var not_text = gen_label("ne_nt") + var not_null = gen_label("ne_nnl") + var not_bool = gen_label("ne_nb") + + // Identical -> false + emit_3("is_identical", dest, left, right) + emit_jump_cond("jump_true", dest, not_ident) + // If jump_true doesn't fire, dest already holds false, continue to checks + emit_jump(not_int) + + emit_label(not_ident) + emit_1("false", dest) + emit_jump(done) + + // Int path + emit_label(not_int) + t0 = alloc_slot() + emit_2("is_int", t0, left) + emit_jump_cond("jump_false", t0, not_num) + t1 = alloc_slot() + emit_2("is_int", t1, right) + emit_jump_cond("jump_false", t1, not_num) + emit_3("ne_int", dest, left, right) + emit_jump(done) + + // Float path + emit_label(not_num) + emit_2("is_num", t0, left) + emit_jump_cond("jump_false", t0, not_text) + emit_2("is_num", t1, right) + emit_jump_cond("jump_false", t1, not_text) + emit_3("ne_float", dest, left, right) + emit_jump(done) + + // Text path + emit_label(not_text) + emit_2("is_text", t0, left) + emit_jump_cond("jump_false", t0, not_null) + emit_2("is_text", t1, right) + emit_jump_cond("jump_false", t1, not_null) + emit_3("ne_text", dest, left, right) + emit_jump(done) + + // Null path + emit_label(not_null) + emit_2("is_null", t0, left) + emit_jump_cond("jump_false", t0, not_bool) + emit_2("is_null", t1, right) + emit_jump_cond("jump_false", t1, not_bool) + emit_1("false", dest) + emit_jump(done) + + // Bool path + var mismatch = gen_label("ne_mis") + emit_label(not_bool) + emit_2("is_bool", t0, left) + emit_jump_cond("jump_false", t0, mismatch) + emit_2("is_bool", t1, right) + emit_jump_cond("jump_false", t1, mismatch) + emit_3("ne_bool", dest, left, right) + emit_jump(done) + + // Mismatch -> true (ne of different types is true) + emit_label(mismatch) + emit_1("true", dest) + emit_label(done) + return null + } + + // emit_relational: int -> float -> text -> disrupt + var emit_relational = function(int_op, float_op, text_op, dest, left, right, left_node, right_node) { + var t0 = 0 + var t1 = 0 + var left_is_int = is_known_int(left_node) + var left_is_num = is_known_number(left_node) + var left_is_text = is_known_text(left_node) + var right_is_int = is_known_int(right_node) + var right_is_num = is_known_number(right_node) + var right_is_text = is_known_text(right_node) + var not_int = null + var not_num = null + var done = null + var err = null + + // Both known int + if (left_is_int && right_is_int) { + emit_3(int_op, dest, left, right) + return null + } + // Both known number + if (left_is_num && right_is_num) { + emit_3(float_op, dest, left, right) + return null + } + // Both known text + if (left_is_text && right_is_text) { + emit_3(text_op, dest, left, right) + return null + } + + not_int = gen_label("rel_ni") + not_num = gen_label("rel_nn") + done = gen_label("rel_done") + err = gen_label("rel_err") + + t0 = alloc_slot() + emit_2("is_int", t0, left) + emit_jump_cond("jump_false", t0, not_int) + t1 = alloc_slot() + emit_2("is_int", t1, right) + emit_jump_cond("jump_false", t1, not_int) + emit_3(int_op, dest, left, right) + emit_jump(done) + + emit_label(not_int) + emit_2("is_num", t0, left) + emit_jump_cond("jump_false", t0, not_num) + emit_2("is_num", t1, right) + emit_jump_cond("jump_false", t1, not_num) + emit_3(float_op, dest, left, right) + emit_jump(done) + + emit_label(not_num) + emit_2("is_text", t0, left) + emit_jump_cond("jump_false", t0, err) + emit_2("is_text", t1, right) + emit_jump_cond("jump_false", t1, err) + emit_3(text_op, dest, left, right) + emit_jump(done) + + emit_label(err) + emit_0("disrupt") + emit_label(done) + return null + } + + // emit_neg_decomposed: int path -> float path -> disrupt + var emit_neg_decomposed = function(dest, src, src_node) { + var t0 = 0 + var not_int = null + var done = null + var err = null + + if (is_known_int(src_node)) { + emit_2("neg_int", dest, src) + return null + } + if (is_known_number(src_node)) { + emit_2("neg_float", dest, src) + return null + } + + not_int = gen_label("neg_ni") + done = gen_label("neg_done") + err = gen_label("neg_err") + + t0 = alloc_slot() + emit_2("is_int", t0, src) + emit_jump_cond("jump_false", t0, not_int) + emit_2("neg_int", dest, src) + emit_jump(done) + + emit_label(not_int) + emit_2("is_num", t0, src) + emit_jump_cond("jump_false", t0, err) + emit_2("neg_float", dest, src) + emit_jump(done) + + emit_label(err) + emit_0("disrupt") + emit_label(done) + return null + } + + // Central router: maps op string to decomposition helper + var emit_binop = function(op_str, dest, left, right, left_node, right_node) { + if (op_str == "add") { + emit_add_decomposed(dest, left, right, left_node, right_node) + } else if (op_str == "subtract") { + emit_numeric_binop("sub_int", "sub_float", dest, left, right, left_node, right_node) + } else if (op_str == "multiply") { + emit_numeric_binop("mul_int", "mul_float", dest, left, right, left_node, right_node) + } else if (op_str == "divide") { + emit_numeric_binop("div_int", "div_float", dest, left, right, left_node, right_node) + } else if (op_str == "modulo") { + emit_numeric_binop("mod_int", "mod_float", dest, left, right, left_node, right_node) + } else if (op_str == "eq") { + emit_eq_decomposed(dest, left, right, left_node, right_node) + } else if (op_str == "ne") { + emit_ne_decomposed(dest, left, right, left_node, right_node) + } else if (op_str == "lt") { + emit_relational("lt_int", "lt_float", "lt_text", dest, left, right, left_node, right_node) + } else if (op_str == "le") { + emit_relational("le_int", "le_float", "le_text", dest, left, right, left_node, right_node) + } else if (op_str == "gt") { + emit_relational("gt_int", "gt_float", "gt_text", dest, left, right, left_node, right_node) + } else if (op_str == "ge") { + emit_relational("ge_int", "ge_float", "ge_text", dest, left, right, left_node, right_node) + } else { + // Passthrough for bitwise, pow, in, etc. + emit_3(op_str, dest, left, right) + } + return null + } + var emit_get_prop = function(dest, obj, prop) { add_instr(["load", dest, obj, prop]) } @@ -463,7 +920,7 @@ var mcode = function(ast) { if (op == null) { op = "add" } - emit_3(op, dest, left_slot, right_slot) + emit_binop(op, dest, left_slot, right_slot, left, right) return dest } @@ -515,7 +972,7 @@ var mcode = function(ast) { } right_slot = gen_expr(right, -1) dest = alloc_slot() - emit_3(op, dest, left_slot, right_slot) + emit_binop(op, dest, left_slot, right_slot, null, right) if (level == 0) { local = find_var(name) if (local >= 0) { @@ -538,7 +995,7 @@ var mcode = function(ast) { emit_get_prop(old_val, obj_slot, prop) right_slot = gen_expr(right, -1) dest = alloc_slot() - emit_3(op, dest, old_val, right_slot) + emit_binop(op, dest, old_val, right_slot, null, right) emit_set_prop(obj_slot, prop, dest) return dest } else if (left_kind == "[") { @@ -550,7 +1007,7 @@ var mcode = function(ast) { emit_get_elem(old_val, obj_slot, idx_slot) right_slot = gen_expr(right, -1) dest = alloc_slot() - emit_3(op, dest, old_val, right_slot) + emit_binop(op, dest, old_val, right_slot, null, right) emit_set_elem(obj_slot, idx_slot, dest) return dest } @@ -679,6 +1136,7 @@ var mcode = function(ast) { var arith_op = null var operand_kind = null var one_slot = 0 + var one_node = null var old_slot = 0 var local = 0 var new_slot = 0 @@ -899,7 +1357,7 @@ var mcode = function(ast) { a0 = gen_expr(args_list[0], -1) a1 = gen_expr(args_list[1], -1) d = alloc_slot() - emit_3(mop, d, a0, a1) + emit_binop(mop, d, a0, a1, args_list[0], args_list[1]) return d } @@ -946,7 +1404,7 @@ var mcode = function(ast) { if (kind == "-unary") { operand_slot = gen_expr(expr.expression, -1) slot = alloc_slot() - emit_2("neg", slot, operand_slot) + emit_neg_decomposed(slot, operand_slot, expr.expression) return slot } if (kind == "+unary") { @@ -961,6 +1419,7 @@ var mcode = function(ast) { operand_kind = operand.kind one_slot = alloc_slot() emit_2("int", one_slot, 1) + one_node = {kind: "number", number: 1} if (operand_kind == "name") { name = operand.name @@ -983,7 +1442,7 @@ var mcode = function(ast) { emit_access_intrinsic(old_slot, name) } new_slot = alloc_slot() - emit_3(arith_op, new_slot, old_slot, one_slot) + emit_binop(arith_op, new_slot, old_slot, one_slot, null, one_node) if (level == 0) { local = find_var(name) if (local >= 0) { @@ -1003,7 +1462,7 @@ var mcode = function(ast) { old_slot = alloc_slot() emit_get_prop(old_slot, obj_slot, prop) new_slot = alloc_slot() - emit_3(arith_op, new_slot, old_slot, one_slot) + emit_binop(arith_op, new_slot, old_slot, one_slot, null, one_node) emit_set_prop(obj_slot, prop, new_slot) return postfix ? old_slot : new_slot } else if (operand_kind == "[") { @@ -1014,7 +1473,7 @@ var mcode = function(ast) { old_slot = alloc_slot() emit_get_elem(old_slot, obj_slot, idx_slot) new_slot = alloc_slot() - emit_3(arith_op, new_slot, old_slot, one_slot) + emit_binop(arith_op, new_slot, old_slot, one_slot, null, one_node) emit_set_elem(obj_slot, idx_slot, new_slot) return postfix ? old_slot : new_slot } @@ -1452,7 +1911,7 @@ var mcode = function(ast) { case_expr = case_node.expression case_val = gen_expr(case_expr, -1) cmp_slot = alloc_slot() - emit_3("eq", cmp_slot, switch_val, case_val) + emit_binop("eq", cmp_slot, switch_val, case_val, null, case_expr) emit_jump_cond("jump_true", cmp_slot, case_label) push(case_labels, case_label) } diff --git a/qbe.cm b/qbe.cm index 25fe173c..6dbc6947 100644 --- a/qbe.cm +++ b/qbe.cm @@ -705,6 +705,257 @@ var ushr = function(p, ctx, a, b) { return shift_op(p, ctx, a, b, "shr") } +// ============================================================ +// Decomposed per-type-path operations +// These map directly to the new IR ops emitted by mcode.cm. +// ============================================================ + +// --- Arithmetic (int path) --- +// add_int: assume both operands are tagged ints. Overflow -> float. +var add_int = function(p, ctx, a, b) { + return ` %${p}.ia =l sar ${a}, 1 + %${p}.ib =l sar ${b}, 1 + %${p}.sum =l add %${p}.ia, %${p}.ib + %${p}.lo =w csltl %${p}.sum, ${int32_min} + %${p}.hi =w csgtl %${p}.sum, ${int32_max} + %${p}.ov =w or %${p}.lo, %${p}.hi + jnz %${p}.ov, @${p}.ov, @${p}.ok +@${p}.ok + %${p}.rw =w copy %${p}.sum + %${p}.rext =l extuw %${p}.rw + %${p} =l shl %${p}.rext, 1 + jmp @${p}.done +@${p}.ov + %${p}.fd =d sltof %${p}.sum + %${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd) +@${p}.done +` +} + +var sub_int = function(p, ctx, a, b) { + return ` %${p}.ia =l sar ${a}, 1 + %${p}.ib =l sar ${b}, 1 + %${p}.diff =l sub %${p}.ia, %${p}.ib + %${p}.lo =w csltl %${p}.diff, ${int32_min} + %${p}.hi =w csgtl %${p}.diff, ${int32_max} + %${p}.ov =w or %${p}.lo, %${p}.hi + jnz %${p}.ov, @${p}.ov, @${p}.ok +@${p}.ok + %${p}.rw =w copy %${p}.diff + %${p}.rext =l extuw %${p}.rw + %${p} =l shl %${p}.rext, 1 + jmp @${p}.done +@${p}.ov + %${p}.fd =d sltof %${p}.diff + %${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd) +@${p}.done +` +} + +var mul_int = function(p, ctx, a, b) { + return ` %${p}.ia =l sar ${a}, 1 + %${p}.ib =l sar ${b}, 1 + %${p}.prod =l mul %${p}.ia, %${p}.ib + %${p}.lo =w csltl %${p}.prod, ${int32_min} + %${p}.hi =w csgtl %${p}.prod, ${int32_max} + %${p}.ov =w or %${p}.lo, %${p}.hi + jnz %${p}.ov, @${p}.ov, @${p}.ok +@${p}.ok + %${p}.rw =w copy %${p}.prod + %${p}.rext =l extuw %${p}.rw + %${p} =l shl %${p}.rext, 1 + jmp @${p}.done +@${p}.ov + %${p}.fd =d sltof %${p}.prod + %${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd) +@${p}.done +` +} + +var div_int = function(p, ctx, a, b) { + return ` %${p}.ia =w copy 0 + %${p}.tmp =l sar ${a}, 1 + %${p}.ia =w copy %${p}.tmp + %${p}.ib =w copy 0 + %${p}.tmp2 =l sar ${b}, 1 + %${p}.ib =w copy %${p}.tmp2 + %${p}.div0 =w ceqw %${p}.ib, 0 + jnz %${p}.div0, @${p}.null, @${p}.chk +@${p}.null + %${p} =l copy ${js_null} + jmp @${p}.done +@${p}.chk + %${p}.rem =w rem %${p}.ia, %${p}.ib + %${p}.exact =w ceqw %${p}.rem, 0 + jnz %${p}.exact, @${p}.idiv, @${p}.fdiv +@${p}.idiv + %${p}.q =w div %${p}.ia, %${p}.ib + %${p}.qext =l extuw %${p}.q + %${p} =l shl %${p}.qext, 1 + jmp @${p}.done +@${p}.fdiv + %${p}.da =d swtof %${p}.ia + %${p}.db =d swtof %${p}.ib + %${p}.dr =d div %${p}.da, %${p}.db + %${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.dr) +@${p}.done +` +} + +var mod_int = function(p, ctx, a, b) { + return ` %${p}.ia =w copy 0 + %${p}.tmp =l sar ${a}, 1 + %${p}.ia =w copy %${p}.tmp + %${p}.ib =w copy 0 + %${p}.tmp2 =l sar ${b}, 1 + %${p}.ib =w copy %${p}.tmp2 + %${p}.div0 =w ceqw %${p}.ib, 0 + jnz %${p}.div0, @${p}.null, @${p}.do_mod +@${p}.null + %${p} =l copy ${js_null} + jmp @${p}.done +@${p}.do_mod + %${p}.r =w rem %${p}.ia, %${p}.ib + %${p}.rext =l extuw %${p}.r + %${p} =l shl %${p}.rext, 1 +@${p}.done +` +} + +var neg_int = function(p, ctx, v) { + return ` %${p}.sl =l sar ${v}, 1 + %${p}.iw =w copy %${p}.sl + %${p}.is_min =w ceqw %${p}.iw, ${int32_min} + jnz %${p}.is_min, @${p}.ov, @${p}.ok +@${p}.ov + %${p}.fd =d swtof %${p}.iw + %${p}.fdn =d neg %${p}.fd + %${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fdn) + jmp @${p}.done +@${p}.ok + %${p}.ni =w sub 0, %${p}.iw + %${p}.niext =l extuw %${p}.ni + %${p} =l shl %${p}.niext, 1 +@${p}.done +` +} + +// --- Arithmetic (float path) --- +var add_float = function(p, ctx, a, b) { + return ` %${p} =l call $qbe_float_add(l ${ctx}, l ${a}, l ${b}) +` +} + +var sub_float = function(p, ctx, a, b) { + return ` %${p} =l call $qbe_float_sub(l ${ctx}, l ${a}, l ${b}) +` +} + +var mul_float = function(p, ctx, a, b) { + return ` %${p} =l call $qbe_float_mul(l ${ctx}, l ${a}, l ${b}) +` +} + +var div_float = function(p, ctx, a, b) { + return ` %${p} =l call $qbe_float_div(l ${ctx}, l ${a}, l ${b}) +` +} + +var mod_float = function(p, ctx, a, b) { + return ` %${p} =l call $qbe_float_mod(l ${ctx}, l ${a}, l ${b}) +` +} + +var neg_float = function(p, ctx, v) { + return ` %${p} =l call $qbe_float_neg(l ${ctx}, l ${v}) +` +} + +// --- Text concat --- +var concat = function(p, ctx, a, b) { + return ` %${p} =l call $JS_ConcatString(l ${ctx}, l ${a}, l ${b}) +` +} + +// --- Comparisons (int path) --- +var cmp_int = function(p, a, b, qbe_op) { + return ` %${p}.ia =l sar ${a}, 1 + %${p}.ib =l sar ${b}, 1 + %${p}.iaw =w copy %${p}.ia + %${p}.ibw =w copy %${p}.ib + %${p}.cr =w ${qbe_op} %${p}.iaw, %${p}.ibw + %${p}.crext =l extuw %${p}.cr + %${p}.sh =l shl %${p}.crext, 5 + %${p} =l or %${p}.sh, 3 +` +} + +var eq_int = function(p, ctx, a, b) { return cmp_int(p, a, b, "ceqw") } +var ne_int = function(p, ctx, a, b) { return cmp_int(p, a, b, "cnew") } +var lt_int = function(p, ctx, a, b) { return cmp_int(p, a, b, "csltw") } +var le_int = function(p, ctx, a, b) { return cmp_int(p, a, b, "cslew") } +var gt_int = function(p, ctx, a, b) { return cmp_int(p, a, b, "csgtw") } +var ge_int = function(p, ctx, a, b) { return cmp_int(p, a, b, "csgew") } + +// --- Comparisons (float path) --- +var cmp_float = function(p, ctx, a, b, op_id) { + return ` %${p}.fcr =w call $qbe_float_cmp(l ${ctx}, w ${op_id}, l ${a}, l ${b}) + %${p}.fcrext =l extuw %${p}.fcr + %${p}.fsh =l shl %${p}.fcrext, 5 + %${p} =l or %${p}.fsh, 3 +` +} + +var eq_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 0) } +var ne_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 1) } +var lt_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 2) } +var le_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 3) } +var gt_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 4) } +var ge_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 5) } + +// --- Comparisons (text path) --- +var cmp_text = function(p, ctx, a, b, qbe_op, eq_only) { + return ` %${p}.scmp =w call $js_string_compare_value(l ${ctx}, l ${a}, l ${b}, w ${eq_only}) + %${p}.tcr =w ${qbe_op} %${p}.scmp, 0 + %${p}.tcrext =l extuw %${p}.tcr + %${p}.tsh =l shl %${p}.tcrext, 5 + %${p} =l or %${p}.tsh, 3 +` +} + +var eq_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "ceqw", 1) } +var ne_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "cnew", 1) } +var lt_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "csltw", 0) } +var le_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "cslew", 0) } +var gt_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "csgtw", 0) } +var ge_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "csgew", 0) } + +// --- Comparisons (bool path) --- +var eq_bool = function(p, a, b) { + return ` %${p}.cr =w ceql ${a}, ${b} + %${p}.crext =l extuw %${p}.cr + %${p}.sh =l shl %${p}.crext, 5 + %${p} =l or %${p}.sh, 3 +` +} + +var ne_bool = function(p, a, b) { + return ` %${p}.cr =w cnel ${a}, ${b} + %${p}.crext =l extuw %${p}.cr + %${p}.sh =l shl %${p}.crext, 5 + %${p} =l or %${p}.sh, 3 +` +} + +// --- Type guard: is_identical --- +var is_identical = function(p, a, b) { + return ` %${p}.cr =w ceql ${a}, ${b} + %${p}.crext =l extuw %${p}.cr + %${p}.sh =l shl %${p}.crext, 5 + %${p} =l or %${p}.sh, 3 +` +} + // ============================================================ // Module export // ============================================================ @@ -760,5 +1011,47 @@ return { bxor: bxor, shl: shl, shr: shr, - ushr: ushr + ushr: ushr, + // decomposed arithmetic (int path) + add_int: add_int, + sub_int: sub_int, + mul_int: mul_int, + div_int: div_int, + mod_int: mod_int, + neg_int: neg_int, + // decomposed arithmetic (float path) + add_float: add_float, + sub_float: sub_float, + mul_float: mul_float, + div_float: div_float, + mod_float: mod_float, + neg_float: neg_float, + // text concat + concat: concat, + // decomposed comparisons (int) + eq_int: eq_int, + ne_int: ne_int, + lt_int: lt_int, + le_int: le_int, + gt_int: gt_int, + ge_int: ge_int, + // decomposed comparisons (float) + eq_float: eq_float, + ne_float: ne_float, + lt_float: lt_float, + le_float: le_float, + gt_float: gt_float, + ge_float: ge_float, + // decomposed comparisons (text) + eq_text: eq_text, + ne_text: ne_text, + lt_text: lt_text, + le_text: le_text, + gt_text: gt_text, + ge_text: ge_text, + // decomposed comparisons (bool) + eq_bool: eq_bool, + ne_bool: ne_bool, + // type guard + is_identical: is_identical } diff --git a/source/mcode.c b/source/mcode.c index 26edb270..96aabbb5 100644 --- a/source/mcode.c +++ b/source/mcode.c @@ -486,6 +486,220 @@ JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj, frame->slots[dest] = JS_NewFloat64(ctx, -d); } } + /* ---- Compiler-internal type guards ---- */ + else if (strcmp(op, "is_int") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_IsInt(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; + } + else if (strcmp(op, "is_num") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_IsNumber(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; + } + else if (strcmp(op, "is_text") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_IsText(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; + } + else if (strcmp(op, "is_null") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_IsNull(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; + } + else if (strcmp(op, "is_bool") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = (JS_VALUE_GET_TAG(frame->slots[(int)a2->valuedouble]) == JS_TAG_BOOL) ? JS_TRUE : JS_FALSE; + } + else if (strcmp(op, "is_identical") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = (frame->slots[(int)a2->valuedouble] == frame->slots[(int)a3->valuedouble]) ? JS_TRUE : JS_FALSE; + } + + /* ---- Specialized arithmetic (int) ---- */ + else if (strcmp(op, "add_int") == 0) { + int dest = (int)a1->valuedouble; + int64_t r = (int64_t)JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) + (int64_t)JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = (r >= INT32_MIN && r <= INT32_MAX) ? JS_NewInt32(ctx, (int32_t)r) : JS_NewFloat64(ctx, (double)r); + } + else if (strcmp(op, "sub_int") == 0) { + int dest = (int)a1->valuedouble; + int64_t r = (int64_t)JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) - (int64_t)JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = (r >= INT32_MIN && r <= INT32_MAX) ? JS_NewInt32(ctx, (int32_t)r) : JS_NewFloat64(ctx, (double)r); + } + else if (strcmp(op, "mul_int") == 0) { + int dest = (int)a1->valuedouble; + int64_t r = (int64_t)JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) * (int64_t)JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = (r >= INT32_MIN && r <= INT32_MAX) ? JS_NewInt32(ctx, (int32_t)r) : JS_NewFloat64(ctx, (double)r); + } + else if (strcmp(op, "div_int") == 0) { + int dest = (int)a1->valuedouble; + int32_t ia = JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]); + int32_t ib = JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble]); + if (ib == 0) frame->slots[dest] = JS_NULL; + else if (ia % ib == 0) frame->slots[dest] = JS_NewInt32(ctx, ia / ib); + else frame->slots[dest] = JS_NewFloat64(ctx, (double)ia / (double)ib); + } + else if (strcmp(op, "mod_int") == 0) { + int dest = (int)a1->valuedouble; + int32_t ib = JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble]); + if (ib == 0) frame->slots[dest] = JS_NULL; + else frame->slots[dest] = JS_NewInt32(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) % ib); + } + else if (strcmp(op, "neg_int") == 0) { + int dest = (int)a1->valuedouble; + int32_t i = JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]); + if (i == INT32_MIN) frame->slots[dest] = JS_NewFloat64(ctx, -(double)i); + else frame->slots[dest] = JS_NewInt32(ctx, -i); + } + + /* ---- Specialized arithmetic (float) ---- */ + else if (strcmp(op, "add_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = JS_NewFloat64(ctx, a + b); + } + else if (strcmp(op, "sub_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = JS_NewFloat64(ctx, a - b); + } + else if (strcmp(op, "mul_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = JS_NewFloat64(ctx, a * b); + } + else if (strcmp(op, "div_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + if (b == 0.0) frame->slots[dest] = JS_NULL; + else frame->slots[dest] = JS_NewFloat64(ctx, a / b); + } + else if (strcmp(op, "mod_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + if (b == 0.0) frame->slots[dest] = JS_NULL; + else frame->slots[dest] = JS_NewFloat64(ctx, fmod(a, b)); + } + else if (strcmp(op, "neg_float") == 0) { + int dest = (int)a1->valuedouble; + double d; + JS_ToFloat64(ctx, &d, frame->slots[(int)a2->valuedouble]); + frame->slots[dest] = JS_NewFloat64(ctx, -d); + } + + /* ---- Specialized comparisons (int) ---- */ + else if (strcmp(op, "eq_int") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) == JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble])); + } + else if (strcmp(op, "ne_int") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) != JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble])); + } + else if (strcmp(op, "lt_int") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) < JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble])); + } + else if (strcmp(op, "le_int") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) <= JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble])); + } + else if (strcmp(op, "gt_int") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) > JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble])); + } + else if (strcmp(op, "ge_int") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) >= JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble])); + } + + /* ---- Specialized comparisons (float) ---- */ + else if (strcmp(op, "eq_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = JS_NewBool(ctx, a == b); + } + else if (strcmp(op, "ne_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = JS_NewBool(ctx, a != b); + } + else if (strcmp(op, "lt_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = JS_NewBool(ctx, a < b); + } + else if (strcmp(op, "le_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = JS_NewBool(ctx, a <= b); + } + else if (strcmp(op, "gt_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = JS_NewBool(ctx, a > b); + } + else if (strcmp(op, "ge_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = JS_NewBool(ctx, a >= b); + } + + /* ---- Specialized comparisons (text) ---- */ + else if (strcmp(op, "eq_text") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble], TRUE) == 0); + } + else if (strcmp(op, "ne_text") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble], TRUE) != 0); + } + else if (strcmp(op, "lt_text") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble], FALSE) < 0); + } + else if (strcmp(op, "le_text") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble], FALSE) <= 0); + } + else if (strcmp(op, "gt_text") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble], FALSE) > 0); + } + else if (strcmp(op, "ge_text") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble], FALSE) >= 0); + } + + /* ---- Specialized comparisons (bool) ---- */ + else if (strcmp(op, "eq_bool") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = (frame->slots[(int)a2->valuedouble] == frame->slots[(int)a3->valuedouble]) ? JS_TRUE : JS_FALSE; + } + else if (strcmp(op, "ne_bool") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = (frame->slots[(int)a2->valuedouble] != frame->slots[(int)a3->valuedouble]) ? JS_TRUE : JS_FALSE; + } + else if (strcmp(op, "abs") == 0) { int dest = (int)a1->valuedouble; JSValue v = frame->slots[(int)a2->valuedouble]; From ad863fb89b28b88a11920545c574f67fbeaf96f4 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Tue, 10 Feb 2026 08:12:27 -0600 Subject: [PATCH 3/4] postfix/prefix operators handled correctly --- mcode.mach | Bin 38080 -> 33031 bytes parse.cm | 64 +++++++++++++----- parse.mach | Bin 50120 -> 52835 bytes vm_suite.ce | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 238 insertions(+), 17 deletions(-) diff --git a/mcode.mach b/mcode.mach index bde182bcec98d95895dbaab519fdcdd9c4b1d5bc..e3fdf4a94ba77753b945105b065cd848240c6adf 100644 GIT binary patch literal 33031 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 literal 38080 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 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 02f25e6f0560ed349e8b3ae76fac9db3bceb20b6..76d89a78d3533b7f1fce071e8273498f51540dfc 100644 GIT binary patch literal 52835 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 Zr@{pB3)=~AJK-6!NW%`?S|}3re*m2JxFY}n 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 // ============================================================================ From ded5f7d74bda9d7553626469ad51353555e1d807 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Tue, 10 Feb 2026 09:13:10 -0600 Subject: [PATCH 4/4] cell shop env var --- internal/bootstrap.cm | 3 +- internal/engine.cm | 15 ++------- source/cell.c | 73 ++++++++++++++++++++++++++++++++----------- 3 files changed, 59 insertions(+), 32 deletions(-) diff --git a/internal/bootstrap.cm b/internal/bootstrap.cm index ac532e0a..75c61076 100644 --- a/internal/bootstrap.cm +++ b/internal/bootstrap.cm @@ -1,4 +1,5 @@ -// Hidden vars (os, args, core_path, use_mcode) come from env +// Hidden vars (os, args, shop_path, use_mcode) come from env +var core_path = shop_path + '/packages/core' // args[0] = script name, args[1..] = user args var load_internal = os.load_internal function use_embed(name) { diff --git a/internal/engine.cm b/internal/engine.cm index d49c9589..bd4dbf76 100644 --- a/internal/engine.cm +++ b/internal/engine.cm @@ -1,4 +1,4 @@ -// Hidden vars (os, actorsym, init, core_path) come from env +// Hidden vars (os, actorsym, init, shop_path) come from env var ACTORDATA = actorsym var SYSYM = '__SYSTEM__' @@ -52,21 +52,10 @@ function ends_with(str, suffix) { var js = use_embed('js') var fd = use_embed('fd') -// Get the shop path from HOME environment -var home = os.getenv('HOME') || os.getenv('USERPROFILE') -if (!home) { - os.print('Could not determine home directory\n') - os.exit(1) -} -var shop_path = home + '/.cell' +// shop_path comes from hidden env (set by cell.c) var packages_path = shop_path + '/packages' var core_path = packages_path + '/core' -if (!fd.is_dir(core_path)) { - os.print('Cell shop not found at ' + shop_path + '. Run "cell install" to set up.\n') - os.exit(1) -} - var use_cache = {} use_cache['core/os'] = os diff --git a/source/cell.c b/source/cell.c index 4a421748..881b2d9a 100644 --- a/source/cell.c +++ b/source/cell.c @@ -26,6 +26,7 @@ int run_c_test_suite(JSContext *ctx); static int run_test_suite(size_t heap_size); cell_rt *root_cell = NULL; +static char *shop_path = NULL; static char *core_path = NULL; static JSRuntime *g_runtime = NULL; @@ -38,31 +39,52 @@ static const char* get_home_dir(void) { return home; } -// Find and verify the cell shop at ~/.cell -int find_cell_shop(void) +// Find and verify the cell shop +// Precedence: override (--shop flag) > CELL_SHOP env var > ~/.cell +int find_cell_shop(const char *override_path) { - const char *home = get_home_dir(); - if (!home) { - printf("ERROR: Could not determine home directory. Set HOME environment variable.\n"); - return 0; + if (override_path) { + shop_path = strdup(override_path); + } else { + const char *env = getenv("CELL_SHOP"); + if (env) { + shop_path = strdup(env); + } else { + const char *home = get_home_dir(); + if (!home) { + printf("ERROR: Could not determine home directory. Set HOME environment variable.\n"); + return 0; + } + size_t path_len = strlen(home) + strlen("/" CELL_SHOP_DIR) + 1; + shop_path = malloc(path_len); + if (!shop_path) { + printf("ERROR: Could not allocate memory for shop path\n"); + return 0; + } + snprintf(shop_path, path_len, "%s/" CELL_SHOP_DIR, home); + } } - // Build path to ~/.cell/core - size_t path_len = strlen(home) + strlen("/" CELL_SHOP_DIR "/" CELL_CORE_DIR) + 1; - core_path = malloc(path_len); + // Derive core_path from shop_path + size_t core_len = strlen(shop_path) + strlen("/" CELL_CORE_DIR) + 1; + core_path = malloc(core_len); if (!core_path) { printf("ERROR: Could not allocate memory for core path\n"); + free(shop_path); + shop_path = NULL; return 0; } - snprintf(core_path, path_len, "%s/" CELL_SHOP_DIR "/" CELL_CORE_DIR, home); + snprintf(core_path, core_len, "%s/" CELL_CORE_DIR, shop_path); // Check if the core directory exists struct stat st; if (stat(core_path, &st) != 0 || !S_ISDIR(st.st_mode)) { - printf("ERROR: Cell shop not found at %s/" CELL_SHOP_DIR "\n", home); + printf("ERROR: Cell shop not found at %s\n", shop_path); printf("Run 'cell install' to set up the cell environment.\n"); free(core_path); + free(shop_path); core_path = NULL; + shop_path = NULL; return 0; } @@ -183,8 +205,8 @@ void script_startup(cell_rt *prt) JS_SetPropertyStr(js, hidden_env, "init", JS_NULL); } - if (core_path) { - JS_SetPropertyStr(js, hidden_env, "core_path", JS_NewString(js, core_path)); + if (shop_path) { + JS_SetPropertyStr(js, hidden_env, "shop_path", JS_NewString(js, shop_path)); } // Stone the environment @@ -255,6 +277,7 @@ static void print_usage(const char *prog) printf("Usage: %s [options]