var json = use("json") var mcode = function(ast) { // Translation tables var binop_map = { "+": "add", "-": "subtract", "*": "multiply", "/": "divide", "%": "remainder", "&": "bitand", "|": "bitor", "^": "bitxor", "<<": "shl", ">>": "shr", ">>>": "ushr", "==": "eq", "===": "eq", "!=": "ne", "!==": "ne", "<": "lt", "<=": "le", ">": "gt", ">=": "ge", "**": "pow", "in": "in" } var functino_map = { "+!": "add", "-!": "subtract", "*!": "multiply", "/!": "divide", "%!": "modulo", "**!": "pow", "!": "gt", "<=!": "le", ">=!": "ge", "=!": "eq", "!=!": "ne", "&!": "bitand", "|!": "bitor", "^!": "bitxor", "<>!": "shr", ">>>!": "ushr", "&&!": "and", "||!": "or", "~!": "bitnot", "[]!": "load" } var binop_sym = { add: "+", subtract: "-", multiply: "*", divide: "/", remainder: "%", pow: "**", lt: "<", le: "<=", gt: ">", ge: ">=" } var compound_map = { "+=": "add", "-=": "subtract", "*=": "multiply", "/=": "divide", "%=": "remainder", "&=": "bitand", "|=": "bitor", "^=": "bitxor", "<<=": "shl", ">>=": "shr", ">>>=": "ushr" } var sensory_ops = { is_array: "is_array", is_function: "is_func", is_object: "is_record", is_stone: "is_stone", is_integer: "is_int", is_text: "is_text", is_number: "is_num", is_logical: "is_bool", is_null: "is_null", is_blob: "is_blob", is_data: "is_data", is_true: "is_true", is_false: "is_false", is_fit: "is_fit", is_character: "is_char", is_digit: "is_digit", is_letter: "is_letter", is_lower: "is_lower", is_upper: "is_upper", is_whitespace: "is_ws", is_actor: "is_actor", length: "length" } // Numeric intrinsic lowering maps (Tier 1 direct mcode). var intrinsic_num_unary_ops = { abs: "abs", sign: "sign", fraction: "fraction", integer: "integer", whole: "integer", neg: "negate" } var intrinsic_num_binary_ops = { modulo: "modulo", remainder: "remainder", max: "max", min: "min" } var intrinsic_num_place_ops = { floor: "floor", ceiling: "ceiling", round: "round", trunc: "trunc" } // Compiler state var s_instructions = null var s_data = null var s_functions = null var s_vars = null var s_this_slot = 0 var s_nr_args = 0 var s_nr_close_slots = 0 var s_nr_local_slots = 0 var s_next_temp_slot = 0 var s_max_slot = 0 var s_label_counter = 0 var s_func_counter = 0 var s_loop_break = null var s_loop_continue = null var s_label_map = {} var s_pending_label = null var s_is_arrow = false var s_function_nr = 0 var s_scopes = null var s_intrinsic_cache = null var s_cur_line = 0 var s_cur_col = 0 var s_filename = null var s_has_disruption = false var s_slot_types = {} var s_num_err_label = null var s_num_err_emitted = false // Shared closure vars for binop helpers (avoids >4 param functions) var _bp_dest = 0 var _bp_left = 0 var _bp_right = 0 var _bp_ln = null var _bp_rn = null var _bp_op_sym = null // State save/restore for nested function compilation var save_state = function() { return { instructions: s_instructions, vars: s_vars, this_slot: s_this_slot, nr_args: s_nr_args, nr_close_slots: s_nr_close_slots, nr_local_slots: s_nr_local_slots, next_temp_slot: s_next_temp_slot, max_slot: s_max_slot, loop_break: s_loop_break, loop_continue: s_loop_continue, label_map: s_label_map, is_arrow: s_is_arrow, function_nr: s_function_nr, intrinsic_cache: s_intrinsic_cache, cur_line: s_cur_line, cur_col: s_cur_col, has_disruption: s_has_disruption, slot_types: s_slot_types, num_err_label: s_num_err_label, num_err_emitted: s_num_err_emitted } } var restore_state = function(saved) { s_instructions = saved.instructions s_vars = saved.vars s_this_slot = saved.this_slot s_nr_args = saved.nr_args s_nr_close_slots = saved.nr_close_slots s_nr_local_slots = saved.nr_local_slots s_next_temp_slot = saved.next_temp_slot s_max_slot = saved.max_slot s_loop_break = saved.loop_break s_loop_continue = saved.loop_continue s_label_map = saved.label_map s_is_arrow = saved.is_arrow s_function_nr = saved.function_nr s_intrinsic_cache = saved.intrinsic_cache s_cur_line = saved.cur_line s_cur_col = saved.cur_col s_has_disruption = saved.has_disruption s_slot_types = saved.slot_types s_num_err_label = saved.num_err_label s_num_err_emitted = saved.num_err_emitted } // Slot allocation var alloc_slot = function() { var slot = s_next_temp_slot s_next_temp_slot = s_next_temp_slot + 1 if (slot > s_max_slot) { s_max_slot = slot } return slot } // Variable tracking var add_var = function(name, slot, is_const) { push(s_vars, {name: name, slot: slot, is_const: is_const, is_closure: false}) } var find_var = function(name) { var _i = 0 while (_i < length(s_vars)) { if (s_vars[_i].name == name) { return s_vars[_i].slot } _i = _i + 1 } return -1 } // Intrinsic cache var find_intrinsic = function(name) { var _i = 0 while (_i < length(s_intrinsic_cache)) { if (s_intrinsic_cache[_i].name == name) { return s_intrinsic_cache[_i].slot } _i = _i + 1 } return -1 } // Scope helpers var find_scope_record = function(fn_nr) { if (s_scopes == null) { return null } var _i = 0 var scope = null while (_i < length(s_scopes)) { scope = s_scopes[_i] if (scope.function_nr == fn_nr) { return scope } _i = _i + 1 } return null } // Label generation var gen_label = function(prefix) { var label = prefix + "_" + text(s_label_counter) s_label_counter = s_label_counter + 1 return label } // Position tracking var set_pos = function(node) { if (node.from_row != null) { s_cur_line = node.from_row + 1 } if (node.from_column != null) { s_cur_col = node.from_column + 1 } } // Instruction emission var add_instr = function(instr) { push(instr, s_cur_line) push(instr, s_cur_col) push(s_instructions, instr) } var emit_label = function(label) { push(s_instructions, label) } var emit_0 = function(op) { add_instr([op]) } var emit_1 = function(op, a) { add_instr([op, a]) } var emit_2 = function(op, a, b) { add_instr([op, a, b]) } var emit_3 = function(op, a, b, c) { add_instr([op, a, b, c]) } var emit_4 = function(op, a, b, c) { // 4th operand passed via closure - use emit_4_full instead add_instr([op, a, b, c]) } var emit_4_full = function(op, abcd) { var instr = [op, abcd[0], abcd[1], abcd[2], abcd[3]] add_instr(instr) } var emit_const_num = function(dest, val) { add_instr(["access", dest, val]) } var emit_const_str = function(dest, val) { add_instr(["access", dest, val]) } var emit_const_bool = function(dest, val) { if (val) { emit_1("true", dest) } else { emit_1("false", dest) } } var emit_const_null = function(dest) { emit_1("null", dest) } var emit_log_error = function(msg) { var log_slot = alloc_slot() add_instr(["access", log_slot, {kind: "name", name: "log", make: "intrinsic"}]) var name_slot = alloc_slot() emit_const_str(name_slot, "error") var msg_slot = alloc_slot() emit_const_str(msg_slot, msg) var args_arr = alloc_slot() add_instr(["array", args_arr, 0]) emit_2("push", args_arr, msg_slot) var result = alloc_slot() var frame_slot = alloc_slot() emit_3("frame", frame_slot, log_slot, 2) var null_slot = alloc_slot() emit_1("null", null_slot) emit_3("setarg", frame_slot, 0, null_slot) emit_3("setarg", frame_slot, 1, name_slot) emit_3("setarg", frame_slot, 2, args_arr) emit_2("invoke", frame_slot, result) } var emit_jump = function(label) { add_instr(["jump", label]) } var emit_jump_cond = function(op, slot, label) { 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" } // Slot-type tracking helpers var slot_is_num = function(slot) { var t = s_slot_types[text(slot)] return t == "num" || t == "int" } var slot_is_text = function(slot) { return s_slot_types[text(slot)] == "text" } var mark_slot = function(slot, typ) { s_slot_types[text(slot)] = typ } var propagate_slot = function(dest, src) { s_slot_types[text(dest)] = s_slot_types[text(src)] } // emit_add_decomposed: emit type-dispatched add (text → concat, num → add) // reads _bp_dest, _bp_left, _bp_right, _bp_ln, _bp_rn from closure var emit_add_decomposed = function() { var left_is_num = is_known_number(_bp_ln) || slot_is_num(_bp_left) var left_is_text = is_known_text(_bp_ln) || slot_is_text(_bp_left) var right_is_num = is_known_number(_bp_rn) || slot_is_num(_bp_right) var right_is_text = is_known_text(_bp_rn) || slot_is_text(_bp_right) // Both known text → concat if (left_is_text && right_is_text) { emit_3("concat", _bp_dest, _bp_left, _bp_right) mark_slot(_bp_dest, "text") return null } // Both known number → add if (left_is_num && right_is_num) { emit_3("add", _bp_dest, _bp_left, _bp_right) mark_slot(_bp_dest, "num") return null } // One known number, other unknown → emit_numeric_binop (guard on unknown side) if (left_is_num || right_is_num) { emit_numeric_binop("add") mark_slot(_bp_dest, "num") return null } // Unknown types: emit full dispatch var t0 = alloc_slot() var t1 = alloc_slot() var done = gen_label("add_done") var check_num = gen_label("add_cn") // Check text path first (since add doubles as concat) emit_2("is_text", t0, _bp_left) emit_jump_cond("jump_false", t0, check_num) emit_2("is_text", t1, _bp_right) emit_jump_cond("jump_false", t1, check_num) emit_3("concat", _bp_dest, _bp_left, _bp_right) emit_jump(done) // Numeric path var err = gen_label("add_err") emit_label(check_num) emit_2("is_num", t0, _bp_left) emit_jump_cond("jump_false", t0, err) emit_2("is_num", t1, _bp_right) emit_jump_cond("jump_false", t1, err) emit_3("add", _bp_dest, _bp_left, _bp_right) emit_jump(done) emit_label(err) emit_log_error("cannot apply '+': operands must both be text or both be numbers") emit_0("disrupt") emit_label(done) return null } // emit_numeric_binop: emit type-guarded numeric binary op // reads _bp_dest, _bp_left, _bp_right, _bp_ln, _bp_rn from closure var emit_numeric_binop = function(op_str) { var left_known = is_known_number(_bp_ln) || slot_is_num(_bp_left) var right_known = is_known_number(_bp_rn) || slot_is_num(_bp_right) var t0 = null var done = null if (left_known && right_known) { emit_3(op_str, _bp_dest, _bp_left, _bp_right) mark_slot(_bp_dest, "num") return null } if (s_num_err_label == null) { s_num_err_label = gen_label("num_err") } t0 = alloc_slot() if (!left_known) { emit_2("is_num", t0, _bp_left) emit_jump_cond("jump_false", t0, s_num_err_label) mark_slot(_bp_left, "num") } if (!right_known) { emit_2("is_num", t0, _bp_right) emit_jump_cond("jump_false", t0, s_num_err_label) mark_slot(_bp_right, "num") } emit_3(op_str, _bp_dest, _bp_left, _bp_right) if (!s_num_err_emitted) { done = gen_label("num_done") emit_jump(done) emit_label(s_num_err_label) emit_log_error("operands must be numbers") emit_0("disrupt") emit_label(done) s_num_err_emitted = true } mark_slot(_bp_dest, "num") return null } // emit_eq_decomposed: VM eq handles all types (int fast path, text memcmp, identity, mixed→false) var emit_eq_decomposed = function() { emit_3("eq", _bp_dest, _bp_left, _bp_right) return null } // emit_ne_decomposed: VM ne handles all types (int fast path, text memcmp, identity, mixed→true) var emit_ne_decomposed = function() { emit_3("ne", _bp_dest, _bp_left, _bp_right) return null } // emit_relational: VM lt/le/gt/ge handle numbers and text, disrupt on mismatch var emit_relational = function(op_str) { emit_3(op_str, _bp_dest, _bp_left, _bp_right) return null } // emit_neg_decomposed: emit type-guarded negate var emit_neg_decomposed = function(dest, src, src_node) { var t0 = null var done = null if (is_known_number(src_node) || slot_is_num(src)) { emit_2("negate", dest, src) mark_slot(dest, "num") return null } if (s_num_err_label == null) { s_num_err_label = gen_label("num_err") } t0 = alloc_slot() emit_2("is_num", t0, src) emit_jump_cond("jump_false", t0, s_num_err_label) mark_slot(src, "num") emit_2("negate", dest, src) if (!s_num_err_emitted) { done = gen_label("num_done") emit_jump(done) emit_label(s_num_err_label) emit_log_error("operands must be numbers") emit_0("disrupt") emit_label(done) s_num_err_emitted = true } mark_slot(dest, "num") return null } // Central router: maps op string to decomposition helper // Sets _bp_* closure vars then calls helper with reduced args var emit_binop = function(op_str, dest, left, right) { _bp_dest = dest _bp_left = left _bp_right = right _bp_op_sym = binop_sym[op_str] || op_str if (op_str == "add") { emit_add_decomposed() } else if (op_str == "eq") { emit_eq_decomposed() } else if (op_str == "ne") { emit_ne_decomposed() } else if (op_str == "lt" || op_str == "le" || op_str == "gt" || op_str == "ge") { emit_relational(op_str) } else if (op_str == "subtract" || op_str == "multiply" || op_str == "divide" || op_str == "modulo" || op_str == "remainder" || op_str == "pow") { emit_numeric_binop(op_str) } else { // Passthrough for bitwise, in, etc. emit_3(op_str, dest, left, right) } return null } var emit_get_prop = function(dest, obj, prop) { add_instr(["load_field", dest, obj, prop]) } var emit_set_prop = function(obj, prop, val) { add_instr(["store_field", obj, val, prop]) } var emit_get_elem = function(dest, obj, idx, access_kind) { if (access_kind == "index") { emit_3("load_index", dest, obj, idx) } else if (access_kind == "field") { emit_3("load_field", dest, obj, idx) } else { emit_3("load_dynamic", dest, obj, idx) } } var emit_set_elem = function(obj, idx, val, access_kind) { if (access_kind == "index") { emit_3("store_index", obj, val, idx) } else if (access_kind == "field") { emit_3("store_field", obj, val, idx) } else { emit_3("store_dynamic", obj, val, idx) } } var emit_call = function(dest, func_slot, args) { var argc = length(args) var frame_slot = alloc_slot() emit_3("frame", frame_slot, func_slot, argc) var arg_idx = 1 var _i = 0 while (_i < argc) { emit_3("setarg", frame_slot, arg_idx, args[_i]) arg_idx = arg_idx + 1 _i = _i + 1 } emit_2("invoke", frame_slot, dest) } var emit_call_method = function(dest, obj, prop, args) { var argc = length(args) var check = alloc_slot() var record_path = gen_label("record_path") var done_label = gen_label("call_done") var _i = 0 var arg_idx = 0 // Check if obj is a proxy function (arity 2) emit_2("is_proxy", check, obj) emit_jump_cond("jump_false", check, record_path) // Function proxy path: call obj(prop_name, [args...]) with this=null var null_slot = alloc_slot() emit_const_null(null_slot) var name_str = alloc_slot() emit_const_str(name_str, prop) var args_arr = alloc_slot() add_instr(["array", args_arr, 0]) _i = 0 while (_i < argc) { emit_2("push", args_arr, args[_i]) _i = _i + 1 } var pf = alloc_slot() emit_3("frame", pf, obj, 2) emit_3("setarg", pf, 0, null_slot) emit_3("setarg", pf, 1, name_str) emit_3("setarg", pf, 2, args_arr) emit_2("invoke", pf, dest) emit_jump(done_label) // Record path: load method, call with this=obj emit_label(record_path) var method_slot = alloc_slot() add_instr(["load_field", method_slot, obj, prop]) var frame_slot = alloc_slot() emit_3("frame", frame_slot, method_slot, argc) emit_3("setarg", frame_slot, 0, obj) arg_idx = 1 _i = 0 while (_i < argc) { emit_3("setarg", frame_slot, arg_idx, args[_i]) arg_idx = arg_idx + 1 _i = _i + 1 } emit_2("invoke", frame_slot, dest) emit_label(done_label) } var emit_call_method_dyn = function(dest, obj, key_reg, args) { var argc = length(args) var check = alloc_slot() var record_path = gen_label("dyn_record_path") var done_label = gen_label("dyn_call_done") var _i = 0 var arg_idx = 0 // Check if obj is a proxy function (arity 2) emit_2("is_proxy", check, obj) emit_jump_cond("jump_false", check, record_path) // Function proxy path (dynamic key): must be text var key_ok = alloc_slot() var error_path = gen_label("dyn_error") emit_2("is_text", key_ok, key_reg) emit_jump_cond("jump_false", key_ok, error_path) var null_slot = alloc_slot() emit_const_null(null_slot) var args_arr = alloc_slot() add_instr(["array", args_arr, 0]) _i = 0 while (_i < argc) { emit_2("push", args_arr, args[_i]) _i = _i + 1 } var pf = alloc_slot() emit_3("frame", pf, obj, 2) emit_3("setarg", pf, 0, null_slot) emit_3("setarg", pf, 1, key_reg) emit_3("setarg", pf, 2, args_arr) emit_2("invoke", pf, dest) emit_jump(done_label) // Error path: non-text key on function disrupts emit_label(error_path) emit_log_error("cannot access: key must be text") emit_0("disrupt") emit_jump(done_label) // Record path: load method dynamically, call with this=obj emit_label(record_path) var method_slot = alloc_slot() emit_3("load_dynamic", method_slot, obj, key_reg) var frame_slot = alloc_slot() emit_3("frame", frame_slot, method_slot, argc) emit_3("setarg", frame_slot, 0, obj) arg_idx = 1 _i = 0 while (_i < argc) { emit_3("setarg", frame_slot, arg_idx, args[_i]) arg_idx = arg_idx + 1 _i = _i + 1 } emit_2("invoke", frame_slot, dest) emit_label(done_label) } var emit_go_call = function(func_slot, args) { var argc = length(args) var frame_slot = alloc_slot() emit_3("goframe", frame_slot, func_slot, argc) var null_slot = alloc_slot() emit_1("null", null_slot) emit_3("setarg", frame_slot, 0, null_slot) var arg_idx = 1 var _i = 0 while (_i < argc) { emit_3("setarg", frame_slot, arg_idx, args[_i]) arg_idx = arg_idx + 1 _i = _i + 1 } emit_1("goinvoke", frame_slot) } var emit_go_call_method = function(obj, prop, args) { var func_slot = alloc_slot() emit_get_prop(func_slot, obj, prop) var argc = length(args) var frame_slot = alloc_slot() emit_3("goframe", frame_slot, func_slot, argc) emit_3("setarg", frame_slot, 0, obj) var arg_idx = 1 var _i = 0 while (_i < argc) { emit_3("setarg", frame_slot, arg_idx, args[_i]) arg_idx = arg_idx + 1 _i = _i + 1 } emit_1("goinvoke", frame_slot) } // Pre-load intrinsics from AST intrinsics array var load_intrinsics = function(intrinsics) { if (intrinsics == null) { return null } var _i = 0 var name = null var slot = 0 var lit = null while (_i < length(intrinsics)) { name = intrinsics[_i] if (name == null || length(s_intrinsic_cache) >= 64) { _i = _i + 1 continue } if (find_intrinsic(name) >= 0) { _i = _i + 1 continue } slot = alloc_slot() lit = {kind: "name", name: name, make: "intrinsic"} add_instr(["access", slot, lit]) push(s_intrinsic_cache, {name: name, slot: slot}) _i = _i + 1 } } // Intrinsic numeric helpers: // preserve native intrinsic behavior for bad argument types by returning null. var emit_intrinsic_num_unary = function(op, arg_slot) { var dest = alloc_slot() var t = alloc_slot() var bad = gen_label(op + "_arg_bad") var done = gen_label(op + "_arg_done") emit_2("is_num", t, arg_slot) emit_jump_cond("jump_false", t, bad) emit_2(op, dest, arg_slot) emit_jump(done) emit_label(bad) emit_1("null", dest) emit_label(done) return dest } var emit_intrinsic_num_binary = function(op, left_slot, right_slot) { var dest = alloc_slot() var t0 = alloc_slot() var t1 = alloc_slot() var bad = gen_label(op + "_arg_bad") var done = gen_label(op + "_arg_done") emit_2("is_num", t0, left_slot) emit_jump_cond("jump_false", t0, bad) emit_2("is_num", t1, right_slot) emit_jump_cond("jump_false", t1, bad) emit_3(op, dest, left_slot, right_slot) emit_jump(done) emit_label(bad) emit_1("null", dest) emit_label(done) return dest } var emit_intrinsic_num_place = function(op, value_slot, place_slot) { var dest = alloc_slot() var t = alloc_slot() var bad = gen_label(op + "_arg_bad") var done = gen_label(op + "_arg_done") emit_2("is_num", t, value_slot) emit_jump_cond("jump_false", t, bad) emit_3(op, dest, value_slot, place_slot) emit_jump(done) emit_label(bad) emit_1("null", dest) emit_label(done) return dest } // Scan scope record for variable declarations // Closure locals are assigned first so returned frames can be shortened var scan_scope = function() { var scope = find_scope_record(s_function_nr) if (scope == null) { return null } var keys = sort(array(scope)) var _i = 0 var name = null var v = null var make = null var is_const = false var slot = 0 // Pass 1: closure locals first _i = 0 while (_i < length(keys)) { name = keys[_i] if (name == "function_nr" || name == "nr_close_slots") { _i = _i + 1 continue } v = scope[name] make = v.make if (make == null || make == "input") { _i = _i + 1 continue } if (v.closure == true && find_var(name) < 0) { is_const = (make == "def" || make == "function") slot = 1 + s_nr_args + s_nr_local_slots s_nr_local_slots = s_nr_local_slots + 1 s_nr_close_slots = s_nr_close_slots + 1 add_var(name, slot, is_const) s_vars[length(s_vars) - 1].is_closure = true } _i = _i + 1 } // Pass 2: non-closure locals _i = 0 while (_i < length(keys)) { name = keys[_i] if (name == "function_nr" || name == "nr_close_slots") { _i = _i + 1 continue } v = scope[name] make = v.make if (make == null || make == "input") { _i = _i + 1 continue } if (v.closure != true && find_var(name) < 0) { is_const = (make == "def" || make == "function") slot = 1 + s_nr_args + s_nr_local_slots s_nr_local_slots = s_nr_local_slots + 1 add_var(name, slot, is_const) } _i = _i + 1 } } // Find variable in a parent's saved state var find_var_in_saved = function(saved, name) { var _i = 0 while (_i < length(saved.vars)) { if (saved.vars[_i].name == name) { return saved.vars[_i].slot } _i = _i + 1 } return -1 } // --- Inline expansion toggle flags --- var inline_arrfor = true var inline_filter = true var inline_every = true var inline_some = true var inline_reduce = true var inline_map = false var inline_find = true // --- Helper: emit arity-dispatched callback invocation --- // ctx = {fn, fn_arity, result, null_s, frame, zero, one, az, ao, prefix, // known_arity (optional — compile-time arity of callback literal)} // args = [slot_for_arg1, slot_for_arg2] — data args (not this) // max_args = 1 or 2 — how many data args to support var emit_arity_call = function(ctx, args, max_args) { var call_one = null var call_two = null var call_done = null var ka = ctx.known_arity // When callback arity is known at compile time, emit only the matching // call path. This avoids dead branches where parameters are nulled, // which confuse the type checker after inlining (e.g. push on null). if (ka != null) { if (ka >= max_args) { ka = max_args } if (ka == 0) { emit_3("frame", ctx.frame, ctx.fn, 0) emit_3("setarg", ctx.frame, 0, ctx.null_s) emit_2("invoke", ctx.frame, ctx.result) } else if (ka == 1 || max_args < 2) { emit_3("frame", ctx.frame, ctx.fn, 1) emit_3("setarg", ctx.frame, 0, ctx.null_s) emit_3("setarg", ctx.frame, 1, args[0]) emit_2("invoke", ctx.frame, ctx.result) } else { emit_3("frame", ctx.frame, ctx.fn, 2) emit_3("setarg", ctx.frame, 0, ctx.null_s) emit_3("setarg", ctx.frame, 1, args[0]) emit_3("setarg", ctx.frame, 2, args[1]) emit_2("invoke", ctx.frame, ctx.result) } return null } call_one = gen_label(ctx.prefix + "_c1") call_two = gen_label(ctx.prefix + "_c2") call_done = gen_label(ctx.prefix + "_cd") emit_3("eq", ctx.az, ctx.fn_arity, ctx.zero) emit_jump_cond("jump_false", ctx.az, call_one) emit_3("frame", ctx.frame, ctx.fn, 0) emit_3("setarg", ctx.frame, 0, ctx.null_s) emit_2("invoke", ctx.frame, ctx.result) emit_jump(call_done) emit_label(call_one) if (max_args >= 2) { emit_3("eq", ctx.ao, ctx.fn_arity, ctx.one) emit_jump_cond("jump_false", ctx.ao, call_two) } emit_3("frame", ctx.frame, ctx.fn, 1) emit_3("setarg", ctx.frame, 0, ctx.null_s) emit_3("setarg", ctx.frame, 1, args[0]) emit_2("invoke", ctx.frame, ctx.result) if (max_args < 2) { emit_label(call_done) return null } emit_jump(call_done) emit_label(call_two) emit_3("frame", ctx.frame, ctx.fn, 2) emit_3("setarg", ctx.frame, 0, ctx.null_s) emit_3("setarg", ctx.frame, 1, args[0]) emit_3("setarg", ctx.frame, 2, args[1]) emit_2("invoke", ctx.frame, ctx.result) emit_label(call_done) return null } // --- Helper: forward loop scaffolding --- // L = {arr, len, i, check, item, one, loop_label, done_label} // body_fn(L) — called between element load and increment var emit_forward_loop = function(L, body_fn) { emit_2("int", L.i, 0) emit_label(L.loop_label) emit_3("lt", L.check, L.i, L.len) emit_jump_cond("jump_false", L.check, L.done_label) emit_3("load_index", L.item, L.arr, L.i) body_fn(L) emit_3("add", L.i, L.i, L.one) emit_jump(L.loop_label) emit_label(L.done_label) return null } // --- Helper: reverse loop scaffolding --- var emit_reverse_loop = function(L, body_fn) { var zero = alloc_slot() emit_2("int", zero, 0) emit_3("subtract", L.i, L.len, L.one) emit_label(L.loop_label) emit_3("ge", L.check, L.i, zero) emit_jump_cond("jump_false", L.check, L.done_label) emit_3("load_index", L.item, L.arr, L.i) body_fn(L) emit_3("subtract", L.i, L.i, L.one) emit_jump(L.loop_label) emit_label(L.done_label) return null } // --- Helper: emit a reduce loop body --- // r = {acc, i, arr, fn, len, fn_arity, known_arity}; emits loop updating acc in-place. // Caller must emit the done_label after calling this. var emit_reduce_loop = function(r, forward, done_label) { var acc = r.acc var i = r.i var arr_slot = r.arr var fn_slot = r.fn var len = r.len var fn_arity = r.fn_arity var check = alloc_slot() var item = alloc_slot() var null_s = alloc_slot() var one = alloc_slot() var zero = alloc_slot() var az = alloc_slot() var ao = alloc_slot() var f = alloc_slot() var loop_label = gen_label("reduce_loop") var ctx = {fn: fn_slot, fn_arity: fn_arity, result: acc, null_s: null_s, frame: f, zero: zero, one: one, az: az, ao: ao, prefix: "reduce", known_arity: r.known_arity} emit_2("int", one, 1) emit_2("int", zero, 0) emit_1("null", null_s) emit_label(loop_label) if (forward) { emit_3("lt", check, i, len) } else { emit_3("ge", check, i, zero) } emit_jump_cond("jump_false", check, done_label) emit_3("load_index", item, arr_slot, i) emit_arity_call(ctx, [acc, item], 2) if (forward) { emit_3("add", i, i, one) } else { emit_3("subtract", i, i, one) } emit_jump(loop_label) } // --- Inline expansion: arrfor(arr, fn[, rev[, exit]]) --- var expand_inline_arrfor = function(dest, args, nargs) { var arr_slot = args.arr var fn_slot = args.fn var len = alloc_slot() var i = alloc_slot() var check = alloc_slot() var item = alloc_slot() var fn_arity = alloc_slot() var az = alloc_slot() var ao = alloc_slot() var null_s = alloc_slot() var zero = alloc_slot() var one = alloc_slot() var f = alloc_slot() var val = alloc_slot() var eq_check = alloc_slot() var early_exit = gen_label("arrfor_exit") var done_final = gen_label("arrfor_final") var rev_label = gen_label("arrfor_rev") var final_label = gen_label("arrfor_fwd_done") var fwd_L = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one, loop_label: gen_label("arrfor_fwd"), done_label: gen_label("arrfor_fwd_d")} var rev_L = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one, loop_label: gen_label("arrfor_rev_l"), done_label: gen_label("arrfor_rev_d")} var ctx = {fn: fn_slot, fn_arity: fn_arity, result: val, null_s: null_s, frame: f, zero: zero, one: one, az: az, ao: ao, prefix: "arrfor"} var body_fn = function(L) { emit_arity_call(ctx, [L.item, L.i], 2) if (nargs >= 4 && args.exit >= 0) { emit_3("eq", eq_check, val, args.exit) emit_jump_cond("jump_true", eq_check, early_exit) } return null } emit_2("length", len, arr_slot) emit_2("int", zero, 0) emit_2("int", one, 1) emit_1("null", null_s) emit_2("length", fn_arity, fn_slot) if (nargs <= 2) { emit_forward_loop(fwd_L, body_fn) } else { emit_jump_cond("wary_true", args.rev, rev_label) emit_forward_loop(fwd_L, body_fn) emit_jump(final_label) emit_label(rev_label) emit_reverse_loop(rev_L, body_fn) emit_label(final_label) } emit_1("null", dest) emit_jump(done_final) if (nargs >= 4 && args.exit >= 0) { emit_label(early_exit) emit_2("move", dest, val) } emit_label(done_final) return dest } // --- Inline expansion: every(arr, fn) --- var expand_inline_every = function(dest, arr_slot, fn_slot) { var len = alloc_slot() var i = alloc_slot() var check = alloc_slot() var item = alloc_slot() var fn_arity = alloc_slot() var arity_is_zero = alloc_slot() var null_s = alloc_slot() var zero = alloc_slot() var one = alloc_slot() var f = alloc_slot() var val = alloc_slot() var loop_label = gen_label("every_loop") var call_one_label = gen_label("every_call_one") var call_done_label = gen_label("every_call_done") var ret_true = gen_label("every_true") var ret_false = gen_label("every_false") var done_label = gen_label("every_done") emit_2("length", len, arr_slot) emit_2("int", i, 0) emit_2("int", zero, 0) emit_2("int", one, 1) emit_1("null", null_s) emit_2("length", fn_arity, fn_slot) emit_label(loop_label) emit_3("lt", check, i, len) emit_jump_cond("jump_false", check, ret_true) emit_3("load_index", item, arr_slot, i) emit_3("eq", arity_is_zero, fn_arity, zero) emit_jump_cond("jump_false", arity_is_zero, call_one_label) emit_3("frame", f, fn_slot, 0) emit_3("setarg", f, 0, null_s) emit_2("invoke", f, val) emit_jump(call_done_label) emit_label(call_one_label) emit_3("frame", f, fn_slot, 1) emit_3("setarg", f, 0, null_s) emit_3("setarg", f, 1, item) emit_2("invoke", f, val) emit_label(call_done_label) emit_jump_cond("wary_false", val, ret_false) emit_3("add", i, i, one) emit_jump(loop_label) emit_label(ret_true) emit_1("true", dest) emit_jump(done_label) emit_label(ret_false) emit_1("false", dest) emit_label(done_label) return dest } // --- Inline expansion: some(arr, fn) --- var expand_inline_some = function(dest, arr_slot, fn_slot) { var len = alloc_slot() var i = alloc_slot() var check = alloc_slot() var item = alloc_slot() var fn_arity = alloc_slot() var arity_is_zero = alloc_slot() var null_s = alloc_slot() var zero = alloc_slot() var one = alloc_slot() var f = alloc_slot() var val = alloc_slot() var loop_label = gen_label("some_loop") var call_one_label = gen_label("some_call_one") var call_done_label = gen_label("some_call_done") var ret_true = gen_label("some_true") var ret_false = gen_label("some_false") var done_label = gen_label("some_done") emit_2("length", len, arr_slot) emit_2("int", i, 0) emit_2("int", zero, 0) emit_2("int", one, 1) emit_1("null", null_s) emit_2("length", fn_arity, fn_slot) emit_label(loop_label) emit_3("lt", check, i, len) emit_jump_cond("jump_false", check, ret_false) emit_3("load_index", item, arr_slot, i) emit_3("eq", arity_is_zero, fn_arity, zero) emit_jump_cond("jump_false", arity_is_zero, call_one_label) emit_3("frame", f, fn_slot, 0) emit_3("setarg", f, 0, null_s) emit_2("invoke", f, val) emit_jump(call_done_label) emit_label(call_one_label) emit_3("frame", f, fn_slot, 1) emit_3("setarg", f, 0, null_s) emit_3("setarg", f, 1, item) emit_2("invoke", f, val) emit_label(call_done_label) emit_jump_cond("wary_true", val, ret_true) emit_3("add", i, i, one) emit_jump(loop_label) emit_label(ret_true) emit_1("true", dest) emit_jump(done_label) emit_label(ret_false) emit_1("false", dest) emit_label(done_label) return dest } // --- Inline expansion: filter(arr, fn) --- var expand_inline_filter = function(dest, arr_slot, fn_slot) { var result = alloc_slot() var len = alloc_slot() var i = alloc_slot() var check = alloc_slot() var item = alloc_slot() var fn_arity = alloc_slot() var az = alloc_slot() var ao = alloc_slot() var null_s = alloc_slot() var zero = alloc_slot() var one = alloc_slot() var f = alloc_slot() var val = alloc_slot() var skip = gen_label("filter_skip") var ctx = {fn: fn_slot, fn_arity: fn_arity, result: val, null_s: null_s, frame: f, zero: zero, one: one, az: az, ao: ao, prefix: "filter"} var L = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one, loop_label: gen_label("filter_loop"), done_label: gen_label("filter_done")} add_instr(["array", result, 0]) emit_2("length", len, arr_slot) emit_2("int", zero, 0) emit_2("int", one, 1) emit_1("null", null_s) emit_2("length", fn_arity, fn_slot) emit_forward_loop(L, function(L) { emit_arity_call(ctx, [L.item, L.i], 2) emit_jump_cond("wary_false", val, skip) emit_2("push", result, L.item) emit_label(skip) return null }) emit_2("move", dest, result) return dest } // --- Inline expansion: find(arr, target[, rev[, from]]) --- var expand_inline_find = function(dest, args, nargs) { var arr_slot = args.arr var target = args.target var len = alloc_slot() var i = alloc_slot() var check = alloc_slot() var item = alloc_slot() var fn_arity = alloc_slot() var az = alloc_slot() var ao = alloc_slot() var null_s = alloc_slot() var zero = alloc_slot() var one = alloc_slot() var f = alloc_slot() var val = alloc_slot() var is_fn = alloc_slot() var eq_check = alloc_slot() var fn_mode_label = gen_label("find_fn") var found_label = gen_label("find_found") var not_found_label = gen_label("find_nf") var final_label = gen_label("find_final") var vrev = gen_label("find_vrev") var vdone = gen_label("find_vdone") var frev = gen_label("find_frev") var fdone = gen_label("find_fdone") var vL = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one, loop_label: gen_label("find_vl"), done_label: gen_label("find_vd")} var vrL = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one, loop_label: gen_label("find_vrl"), done_label: gen_label("find_vrd")} var fL = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one, loop_label: gen_label("find_fl"), done_label: gen_label("find_fd")} var ffL = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one, loop_label: gen_label("find_ffl"), done_label: gen_label("find_ffd")} var frL = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one, loop_label: gen_label("find_frl"), done_label: gen_label("find_frd")} var ctx = {fn: target, fn_arity: fn_arity, result: val, null_s: null_s, frame: f, zero: zero, one: one, az: az, ao: ao, prefix: "find"} var val_body = function(L) { emit_3("eq", eq_check, L.item, target) emit_jump_cond("jump_true", eq_check, found_label) return null } var fn_body = function(L) { emit_arity_call(ctx, [L.item, L.i], 2) emit_jump_cond("wary_true", val, found_label) return null } emit_2("length", len, arr_slot) emit_2("int", zero, 0) emit_2("int", one, 1) emit_1("null", null_s) emit_2("is_func", is_fn, target) emit_jump_cond("jump_true", is_fn, fn_mode_label) // === Value mode === if (nargs <= 2) { emit_forward_loop(vL, val_body) } else { emit_jump_cond("wary_true", args.rev, vrev) if (nargs >= 4 && args.from >= 0) { emit_2("move", i, args.from) } if (nargs >= 4 && args.from >= 0) { emit_label(vL.loop_label) emit_3("lt", vL.check, vL.i, vL.len) emit_jump_cond("jump_false", vL.check, vL.done_label) emit_3("load_index", vL.item, vL.arr, vL.i) val_body(vL) emit_3("add", vL.i, vL.i, vL.one) emit_jump(vL.loop_label) emit_label(vL.done_label) } else { emit_forward_loop(vL, val_body) } emit_jump(vdone) emit_label(vrev) emit_reverse_loop(vrL, val_body) emit_label(vdone) } emit_jump(not_found_label) // === Function mode === emit_label(fn_mode_label) emit_2("length", fn_arity, target) if (nargs <= 2) { emit_forward_loop(fL, fn_body) } else { emit_jump_cond("wary_true", args.rev, frev) if (nargs >= 4 && args.from >= 0) { emit_2("move", i, args.from) } if (nargs >= 4 && args.from >= 0) { emit_label(ffL.loop_label) emit_3("lt", ffL.check, ffL.i, ffL.len) emit_jump_cond("jump_false", ffL.check, ffL.done_label) emit_3("load_index", ffL.item, ffL.arr, ffL.i) fn_body(ffL) emit_3("add", ffL.i, ffL.i, ffL.one) emit_jump(ffL.loop_label) emit_label(ffL.done_label) } else { emit_forward_loop(ffL, fn_body) } emit_jump(fdone) emit_label(frev) emit_reverse_loop(frL, fn_body) emit_label(fdone) } emit_label(not_found_label) emit_1("null", dest) emit_jump(final_label) emit_label(found_label) emit_2("move", dest, i) emit_label(final_label) return dest } // --- Inline expansion: array(arr, fn) → map --- var expand_inline_map = function(dest, arr_slot, fn_slot) { var result = alloc_slot() var len = alloc_slot() var i = alloc_slot() var check = alloc_slot() var item = alloc_slot() var fn_arity = alloc_slot() var arity_is_zero = alloc_slot() var arity_is_one = alloc_slot() var null_s = alloc_slot() var zero = alloc_slot() var one = alloc_slot() var f = alloc_slot() var val = alloc_slot() var loop_label = gen_label("map_loop") var call_one_label = gen_label("map_call_one") var call_two_label = gen_label("map_call_two") var call_done_label = gen_label("map_call_done") var done_label = gen_label("map_done") add_instr(["array", result, 0]) emit_2("length", len, arr_slot) emit_2("int", i, 0) emit_2("int", zero, 0) emit_2("int", one, 1) emit_1("null", null_s) emit_2("length", fn_arity, fn_slot) emit_label(loop_label) emit_3("lt", check, i, len) emit_jump_cond("jump_false", check, done_label) emit_3("load_index", item, arr_slot, i) emit_3("eq", arity_is_zero, fn_arity, zero) emit_jump_cond("jump_false", arity_is_zero, call_one_label) emit_3("frame", f, fn_slot, 0) emit_3("setarg", f, 0, null_s) emit_2("invoke", f, val) emit_jump(call_done_label) emit_label(call_one_label) emit_3("eq", arity_is_one, fn_arity, one) emit_jump_cond("jump_false", arity_is_one, call_two_label) emit_3("frame", f, fn_slot, 1) emit_3("setarg", f, 0, null_s) emit_3("setarg", f, 1, item) emit_2("invoke", f, val) emit_jump(call_done_label) emit_label(call_two_label) emit_3("frame", f, fn_slot, 2) emit_3("setarg", f, 0, null_s) emit_3("setarg", f, 1, item) emit_3("setarg", f, 2, i) emit_2("invoke", f, val) emit_label(call_done_label) emit_2("push", result, val) emit_3("add", i, i, one) emit_jump(loop_label) emit_label(done_label) emit_2("move", dest, result) return dest } // --- Inline expansion: array(arr, intrinsic_op) → map with direct opcode --- var expand_inline_map_intrinsic = function(dest, arr_slot, opcode) { var result = alloc_slot() var len = alloc_slot() var i = alloc_slot() var check = alloc_slot() var item = alloc_slot() var val = alloc_slot() var zero = alloc_slot() var one = alloc_slot() var loop_label = gen_label("mapi_loop") var done_label = gen_label("mapi_done") add_instr(["array", result, 0]) emit_2("length", len, arr_slot) emit_2("int", i, 0) emit_2("int", zero, 0) emit_2("int", one, 1) emit_label(loop_label) emit_3("lt", check, i, len) emit_jump_cond("jump_false", check, done_label) emit_3("load_index", item, arr_slot, i) emit_2(opcode, val, item) emit_2("push", result, val) emit_3("add", i, i, one) emit_jump(loop_label) emit_label(done_label) emit_2("move", dest, result) return dest } // --- Inline expansion: reduce(arr, fn[, initial[, reverse]]) --- var expand_inline_reduce = function(dest, args, nargs) { var arr_slot = args.arr var fn_slot = args.fn var init_slot = args.init var rev_slot = args.rev var fn_arity = alloc_slot() var len = alloc_slot() var acc = alloc_slot() var i = alloc_slot() var check = alloc_slot() var zero = alloc_slot() var one = alloc_slot() var final_label = gen_label("reduce_final") var has_init = null var no_init_rev = null var init_rev = null var null_label = null var d1 = null var d2 = null var d3 = null var d4 = null var r = null emit_2("length", len, arr_slot) emit_2("length", fn_arity, fn_slot) emit_2("int", zero, 0) emit_2("int", one, 1) r = {acc: acc, i: i, arr: arr_slot, fn: fn_slot, len: len, fn_arity: fn_arity, known_arity: args.fn_known_arity} if (nargs == 2) { null_label = gen_label("reduce_null") d1 = gen_label("reduce_d1") emit_3("lt", check, zero, len) emit_jump_cond("jump_false", check, null_label) emit_3("load_index", acc, arr_slot, zero) emit_2("move", i, one) emit_reduce_loop(r, true, d1) emit_label(d1) emit_2("move", dest, acc) emit_jump(final_label) emit_label(null_label) emit_1("null", dest) emit_label(final_label) } else if (nargs == 3) { has_init = gen_label("reduce_has_init") null_label = gen_label("reduce_null") d1 = gen_label("reduce_d1") d2 = gen_label("reduce_d2") emit_2("is_null", check, init_slot) emit_jump_cond("jump_false", check, has_init) // No initial, forward emit_3("lt", check, zero, len) emit_jump_cond("jump_false", check, null_label) emit_3("load_index", acc, arr_slot, zero) emit_2("move", i, one) emit_reduce_loop(r, true, d1) emit_label(d1) emit_2("move", dest, acc) emit_jump(final_label) emit_label(null_label) emit_1("null", dest) emit_jump(final_label) // Has initial, forward emit_label(has_init) emit_2("move", acc, init_slot) emit_2("int", i, 0) emit_reduce_loop(r, true, d2) emit_label(d2) emit_2("move", dest, acc) emit_label(final_label) } else { // nargs == 4: full branching has_init = gen_label("reduce_has_init") no_init_rev = gen_label("reduce_no_init_rev") init_rev = gen_label("reduce_init_rev") null_label = gen_label("reduce_null") d1 = gen_label("reduce_d1") d2 = gen_label("reduce_d2") d3 = gen_label("reduce_d3") d4 = gen_label("reduce_d4") emit_2("is_null", check, init_slot) emit_jump_cond("jump_false", check, has_init) // No initial emit_3("lt", check, zero, len) emit_jump_cond("jump_false", check, null_label) emit_jump_cond("wary_true", rev_slot, no_init_rev) // No initial, forward emit_3("load_index", acc, arr_slot, zero) emit_2("move", i, one) emit_reduce_loop(r, true, d1) emit_label(d1) emit_2("move", dest, acc) emit_jump(final_label) // No initial, reverse emit_label(no_init_rev) emit_3("subtract", i, len, one) emit_3("load_index", acc, arr_slot, i) emit_3("subtract", i, i, one) emit_reduce_loop(r, false, d2) emit_label(d2) emit_2("move", dest, acc) emit_jump(final_label) emit_label(null_label) emit_1("null", dest) emit_jump(final_label) // Has initial emit_label(has_init) emit_jump_cond("wary_true", rev_slot, init_rev) // Has initial, forward emit_2("move", acc, init_slot) emit_2("int", i, 0) emit_reduce_loop(r, true, d3) emit_label(d3) emit_2("move", dest, acc) emit_jump(final_label) // Has initial, reverse emit_label(init_rev) emit_2("move", acc, init_slot) emit_3("subtract", i, len, one) emit_reduce_loop(r, false, d4) emit_label(d4) emit_2("move", dest, acc) emit_label(final_label) } return dest } // Forward declarations via var var gen_expr = null var gen_statement = null var gen_function = null // Emit an access instruction for an intrinsic name var emit_access_intrinsic = function(dest, name) { var lit = {kind: "name", name: name, make: "intrinsic"} add_instr(["access", dest, lit]) } // Binary expression compilation var gen_binary = function(node, target) { var kind = node.kind var left = node.left var right = node.right var end_label = null var left_slot = 0 var right_slot = 0 var dest = 0 var op = null if (kind == "&&") { end_label = gen_label("and_end") left_slot = gen_expr(left, -1) dest = alloc_slot() emit_2("move", dest, left_slot) emit_jump_cond("wary_false", dest, end_label) right_slot = gen_expr(right, -1) emit_2("move", dest, right_slot) emit_label(end_label) return dest } if (kind == "||") { end_label = gen_label("or_end") left_slot = gen_expr(left, -1) dest = alloc_slot() emit_2("move", dest, left_slot) emit_jump_cond("wary_true", dest, end_label) right_slot = gen_expr(right, -1) emit_2("move", dest, right_slot) emit_label(end_label) return dest } if (kind == "??") { end_label = gen_label("nullish_end") left_slot = gen_expr(left, -1) dest = alloc_slot() emit_2("move", dest, left_slot) emit_jump_cond("jump_not_null", dest, end_label) right_slot = gen_expr(right, -1) emit_2("move", dest, right_slot) emit_label(end_label) return dest } // Comma operator if (kind == ",") { gen_expr(left, -1) return gen_expr(right, -1) } // Standard binary ops left_slot = gen_expr(left, -1) right_slot = gen_expr(right, -1) // Use target slot for ops without multi-type dispatch (add has text+num paths). // Exception: allow + to write directly to target when target == left_slot // (self-assign pattern like s = s + x) since concat/add reads before writing. dest = (target >= 0 && (kind != "+" || target == left_slot)) ? target : alloc_slot() op = binop_map[kind] if (op == null) { op = "add" } _bp_ln = left _bp_rn = right emit_binop(op, dest, left_slot, right_slot) return dest } // Compound assignment helper // parent_vars is an array of saved states for parent chain lookup var parent_states = [] var gen_compound_assign = function(node, op) { var left = node.left var right = node.right var left_kind = left.kind var name = null var level = 0 var left_slot = 0 var local = 0 var _lv = 0 var pstate = null var pslot = 0 var right_slot = 0 var dest = 0 var obj = null var prop = null var obj_slot = 0 var old_val = 0 var idx_expr = null var idx_slot = 0 if (left_kind == "name") { name = left.name level = left.level if (level == null) { level = -1 } left_slot = alloc_slot() if (level == 0 || level == -1) { local = find_var(name) if (local >= 0) { emit_2("move", left_slot, local) level = 0 } } if (level > 0) { _lv = level - 1 pstate = parent_states[length(parent_states) - 1 - _lv] pslot = find_var_in_saved(pstate, name) emit_3("get", left_slot, pslot, level) } else if (level == -1) { emit_access_intrinsic(left_slot, name) } right_slot = gen_expr(right, -1) dest = alloc_slot() _bp_ln = null _bp_rn = right emit_binop(op, dest, left_slot, right_slot) if (level == 0) { local = find_var(name) if (local >= 0) { emit_2("move", local, dest) propagate_slot(local, dest) } } else if (level > 0) { _lv = level - 1 pstate = parent_states[length(parent_states) - 1 - _lv] pslot = find_var_in_saved(pstate, name) emit_3("put", dest, pslot, level) } return dest } else if (left_kind == ".") { obj = left.left prop = left.right obj_slot = gen_expr(obj, -1) old_val = alloc_slot() emit_get_prop(old_val, obj_slot, prop) right_slot = gen_expr(right, -1) dest = alloc_slot() _bp_ln = null _bp_rn = right emit_binop(op, dest, old_val, right_slot) emit_set_prop(obj_slot, prop, dest) return dest } else if (left_kind == "[") { obj = left.left idx_expr = left.right obj_slot = gen_expr(obj, -1) idx_slot = gen_expr(idx_expr, -1) old_val = alloc_slot() emit_get_elem(old_val, obj_slot, idx_slot, left.access_kind) right_slot = gen_expr(right, -1) dest = alloc_slot() _bp_ln = null _bp_rn = right emit_binop(op, dest, old_val, right_slot) emit_set_elem(obj_slot, idx_slot, dest, left.access_kind) return dest } return -1 } // Assignment compilation var gen_assign = function(node) { var kind = node.kind var left = node.left var right = node.right var cop = compound_map[kind] var arr_expr = null var arr_slot = 0 var val_slot = 0 var left_kind = null var name = null var level = 0 var slot = 0 var _lv = 0 var pstate = null var pslot = 0 var obj = null var prop = null var obj_slot = 0 var idx_expr = null var idx_slot = 0 var guard_t = 0 var guard_err = null var guard_done = null if (cop != null) { return gen_compound_assign(node, cop) } // Push syntax: arr[] = val (guarded) if (node.push == true) { arr_expr = left.left arr_slot = gen_expr(arr_expr, -1) val_slot = gen_expr(right, -1) guard_t = alloc_slot() guard_err = gen_label("push_err") guard_done = gen_label("push_done") emit_2("is_array", guard_t, arr_slot) emit_jump_cond("jump_false", guard_t, guard_err) emit_2("push", arr_slot, val_slot) emit_jump(guard_done) emit_label(guard_err) emit_log_error("cannot push: target must be an array") emit_0("disrupt") emit_label(guard_done) return val_slot } left_kind = left.kind // For local name assignments, try to write directly to the var's slot if (left_kind == "name") { name = left.name level = left.level if (level == null) { level = -1 } if (level == 0 || level == -1) { slot = find_var(name) if (slot >= 0) { val_slot = gen_expr(right, slot) if (val_slot != slot) { emit_2("move", slot, val_slot) propagate_slot(slot, val_slot) } return val_slot } val_slot = gen_expr(right, -1) } else { val_slot = gen_expr(right, -1) if (level > 0) { _lv = level - 1 pstate = parent_states[length(parent_states) - 1 - _lv] pslot = find_var_in_saved(pstate, name) emit_3("put", val_slot, pslot, level) } } return val_slot } val_slot = gen_expr(right, -1) if (left_kind == ".") { obj = left.left prop = left.right obj_slot = gen_expr(obj, -1) emit_set_prop(obj_slot, prop, val_slot) } else if (left_kind == "[") { obj = left.left idx_expr = left.right obj_slot = gen_expr(obj, -1) idx_slot = gen_expr(idx_expr, -1) emit_set_elem(obj_slot, idx_slot, val_slot, left.access_kind) } return val_slot } // Expression compilation gen_expr = function(expr, target) { var kind = null var slot = 0 var val = null var list = null var nexpr = 0 var expr_slots = null var _i = 0 var arr_slot = 0 var arr_instr = null var fmt_func_slot = 0 var fmt = null var fmt_str_slot = 0 var result_slot = 0 var pattern = null var flags = null var name = null var level = 0 var cached = 0 var dest = 0 var _lv = 0 var pstate = null var parent_slot = 0 var obj = null var prop = null var obj_slot = 0 var idx = null var idx_slot = 0 var callee = null var args_list = null var callee_kind = null var fname = null var mop = null var nargs = 0 var a0 = 0 var a1 = 0 var a2 = 0 var a3 = 0 var d = 0 var top = null var arg_slots = null var key_expr = null var key_slot = 0 var func_slot = 0 var operand_slot = 0 var operand = null var postfix = false 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 var pslot = 0 var idx_expr = null var cond = null var then_expr = null var else_expr = null var else_label = null var end_label = null var cond_slot = 0 var then_slot = 0 var else_slot = 0 var count = 0 var elem_slots = null var instr = null var pair = null var key = null var val_slot = 0 var key_kind = null var kname = null var func = null var func_id = 0 var guard_t = 0 var guard_err = null var guard_done = null var cb_known = null var cb_p = null if (expr == null) { return -1 } set_pos(expr) kind = expr.kind if (kind == null) { return -1 } // Literals if (kind == "number") { slot = target >= 0 ? target : alloc_slot() emit_const_num(slot, expr.number) mark_slot(slot, is_integer(expr.number) ? "int" : "num") return slot } if (kind == "text") { slot = target >= 0 ? target : alloc_slot() val = expr.value if (val == null) { val = "" } emit_const_str(slot, val) mark_slot(slot, "text") return slot } // Template literal if (kind == "text literal") { list = expr.list nexpr = list != null ? length(list) : 0 expr_slots = [] _i = 0 while (_i < nexpr) { push(expr_slots, gen_expr(list[_i], -1)) _i = _i + 1 } // Create array from expression results arr_slot = alloc_slot() add_instr(["array", arr_slot, 0]) _i = 0 while (_i < nexpr) { emit_2("push", arr_slot, expr_slots[_i]) _i = _i + 1 } // Load format intrinsic fmt_func_slot = find_intrinsic("format") if (fmt_func_slot < 0) { fmt_func_slot = alloc_slot() emit_access_intrinsic(fmt_func_slot, "format") } // Load format string fmt = expr.value if (fmt == null) { fmt = "" } fmt_str_slot = alloc_slot() emit_const_str(fmt_str_slot, fmt) // Call format(fmt_str, array) result_slot = target >= 0 ? target : alloc_slot() emit_call(result_slot, fmt_func_slot, [fmt_str_slot, arr_slot]) mark_slot(result_slot, "text") return result_slot } if (kind == "regexp") { slot = target >= 0 ? target : alloc_slot() pattern = expr.pattern if (pattern == null) { pattern = "" } flags = expr.flags if (flags == null) { flags = "" } add_instr(["regexp", slot, pattern, flags]) return slot } if (kind == "true") { slot = target >= 0 ? target : alloc_slot() emit_const_bool(slot, true) mark_slot(slot, "bool") return slot } if (kind == "false") { slot = target >= 0 ? target : alloc_slot() emit_const_bool(slot, false) mark_slot(slot, "bool") return slot } if (kind == "null") { slot = target >= 0 ? target : alloc_slot() emit_const_null(slot) mark_slot(slot, null) return slot } if (kind == "this") { return s_this_slot } // Variable reference if (kind == "name") { name = expr.name level = expr.level if (level == null) { level = -1 } if (level == 0 || level == -1) { slot = find_var(name) if (slot >= 0) { return slot } } else if (level > 0) { _lv = level - 1 pstate = parent_states[length(parent_states) - 1 - _lv] parent_slot = find_var_in_saved(pstate, name) dest = alloc_slot() emit_3("get", dest, parent_slot, level) return dest } // Unbound — check intrinsic cache cached = find_intrinsic(name) if (cached >= 0) { return cached } dest = alloc_slot() emit_access_intrinsic(dest, name) return dest } // Property access if (kind == ".") { obj = expr.left prop = expr.right obj_slot = gen_expr(obj, -1) slot = alloc_slot() emit_get_prop(slot, obj_slot, prop) return slot } // Element access if (kind == "[") { obj = expr.left idx = expr.right obj_slot = gen_expr(obj, -1) idx_slot = gen_expr(idx, -1) slot = alloc_slot() emit_get_elem(slot, obj_slot, idx_slot, expr.access_kind) return slot } // Function call if (kind == "(") { callee = expr.expression args_list = expr.list callee_kind = callee.kind // Functino: inline operator call if (callee_kind == "name" && callee.make == "functino") { fname = callee.name mop = functino_map[fname] nargs = args_list != null ? length(args_list) : 0 if (fname == "~!") { a0 = gen_expr(args_list[0], -1) d = alloc_slot() emit_2(mop, d, a0) return d } if (fname == "[]!") { a0 = gen_expr(args_list[0], -1) a1 = gen_expr(args_list[1], -1) d = alloc_slot() emit_get_elem(d, a0, a1) return d } if ((fname == "=!" || fname == "!=!") && nargs == 3) { a0 = gen_expr(args_list[0], -1) a1 = gen_expr(args_list[1], -1) a2 = gen_expr(args_list[2], -1) d = alloc_slot() top = fname == "=!" ? "eq_tol" : "ne_tol" emit_4_full(top, [d, a0, a1, a2]) return d } if (fname == "&&!") { a0 = gen_expr(args_list[0], -1) a1 = gen_expr(args_list[1], -1) d = alloc_slot() emit_3("and", d, a0, a1) return d } if (fname == "||!") { a0 = gen_expr(args_list[0], -1) a1 = gen_expr(args_list[1], -1) d = alloc_slot() emit_3("or", d, a0, a1) return d } // Standard 2-arg binary functino a0 = gen_expr(args_list[0], -1) a1 = gen_expr(args_list[1], -1) d = alloc_slot() _bp_ln = args_list[0] _bp_rn = args_list[1] emit_binop(mop, d, a0, a1) return d } // Tier 1 intrinsic inlining: emit direct opcodes instead of frame/invoke if (callee_kind == "name" && callee.intrinsic == true) { fname = callee.name nargs = args_list != null ? length(args_list) : 0 mop = intrinsic_num_unary_ops[fname] if (mop != null && nargs == 1) { a0 = gen_expr(args_list[0], -1) return emit_intrinsic_num_unary(mop, a0) } mop = intrinsic_num_binary_ops[fname] if (mop != null && nargs == 2) { a0 = gen_expr(args_list[0], -1) a1 = gen_expr(args_list[1], -1) return emit_intrinsic_num_binary(mop, a0, a1) } mop = intrinsic_num_place_ops[fname] if (mop != null && (nargs == 1 || nargs == 2)) { a0 = gen_expr(args_list[0], -1) if (nargs == 2) { a1 = gen_expr(args_list[1], -1) } else { a1 = alloc_slot() emit_1("null", a1) } return emit_intrinsic_num_place(mop, a0, a1) } // 1-arg type check intrinsics → direct opcode if (nargs == 1 && sensory_ops[fname] != null) { a0 = gen_expr(args_list[0], -1) d = alloc_slot() emit_2(sensory_ops[fname], d, a0) return d } // 2-arg push: push(arr, val) → guarded direct opcode if (nargs == 2 && fname == "push") { a0 = gen_expr(args_list[0], -1) a1 = gen_expr(args_list[1], -1) guard_t = alloc_slot() guard_err = gen_label("push_err") guard_done = gen_label("push_done") emit_2("is_array", guard_t, a0) emit_jump_cond("jump_false", guard_t, guard_err) emit_2("push", a0, a1) emit_jump(guard_done) emit_label(guard_err) emit_log_error("cannot push: target must be an array") emit_0("disrupt") emit_label(guard_done) return a1 } // apply(fn, arr) → direct opcode if (nargs == 2 && fname == "apply") { a0 = gen_expr(args_list[0], -1) a1 = gen_expr(args_list[1], -1) d = alloc_slot() emit_3("apply", d, a0, a1) return d } // Callback intrinsics → inline mcode loops if (fname == "arrfor" && nargs >= 2 && nargs <= 4 && inline_arrfor) { a0 = gen_expr(args_list[0], -1) a1 = gen_expr(args_list[1], -1) a2 = nargs >= 3 ? gen_expr(args_list[2], -1) : -1 a3 = nargs >= 4 ? gen_expr(args_list[3], -1) : -1 d = alloc_slot() return expand_inline_arrfor(d, {arr: a0, fn: a1, rev: a2, exit: a3}, nargs) } if (nargs == 2 && fname == "every" && inline_every) { a0 = gen_expr(args_list[0], -1) a1 = gen_expr(args_list[1], -1) d = alloc_slot() return expand_inline_every(d, a0, a1) } if (nargs == 2 && fname == "some" && inline_some) { a0 = gen_expr(args_list[0], -1) a1 = gen_expr(args_list[1], -1) d = alloc_slot() return expand_inline_some(d, a0, a1) } if (nargs == 2 && fname == "filter" && inline_filter) { a0 = gen_expr(args_list[0], -1) a1 = gen_expr(args_list[1], -1) d = alloc_slot() return expand_inline_filter(d, a0, a1) } if (fname == "find" && nargs >= 2 && nargs <= 4 && inline_find) { a0 = gen_expr(args_list[0], -1) a1 = gen_expr(args_list[1], -1) a2 = nargs >= 3 ? gen_expr(args_list[2], -1) : -1 a3 = nargs >= 4 ? gen_expr(args_list[3], -1) : -1 d = alloc_slot() return expand_inline_find(d, {arr: a0, target: a1, rev: a2, from: a3}, nargs) } if (fname == "reduce" && nargs >= 2 && nargs <= 4 && inline_reduce) { a0 = gen_expr(args_list[0], -1) a1 = gen_expr(args_list[1], -1) a2 = nargs >= 3 ? gen_expr(args_list[2], -1) : -1 a3 = nargs >= 4 ? gen_expr(args_list[3], -1) : -1 d = alloc_slot() cb_known = null if (args_list[1].kind == "function") { cb_p = args_list[1].list if (cb_p == null) cb_p = args_list[1].parameters cb_known = cb_p != null ? length(cb_p) : 0 } return expand_inline_reduce(d, {arr: a0, fn: a1, init: a2, rev: a3, fn_known_arity: cb_known}, nargs) } // array(arr, fn) inline expansion removed — array() is too complex to inline } // Collect arg slots arg_slots = [] _i = 0 nargs = args_list != null ? length(args_list) : 0 while (_i < nargs) { push(arg_slots, gen_expr(args_list[_i], -1)) _i = _i + 1 } dest = alloc_slot() if (callee_kind == ".") { obj = callee.left prop = callee.right obj_slot = gen_expr(obj, -1) emit_call_method(dest, obj_slot, prop, arg_slots) } else if (callee_kind == "[") { obj = callee.left key_expr = callee.right obj_slot = gen_expr(obj, -1) key_slot = gen_expr(key_expr, -1) emit_call_method_dyn(dest, obj_slot, key_slot, arg_slots) } else { func_slot = gen_expr(callee, -1) emit_call(dest, func_slot, arg_slots) } return dest } // Unary operators if (kind == "!") { operand_slot = gen_expr(expr.expression, -1) slot = alloc_slot() emit_2("not", slot, operand_slot) return slot } if (kind == "~") { operand_slot = gen_expr(expr.expression, -1) slot = alloc_slot() emit_2("bitnot", slot, operand_slot) return slot } if (kind == "-unary") { operand_slot = gen_expr(expr.expression, -1) slot = alloc_slot() emit_neg_decomposed(slot, operand_slot, expr.expression) return slot } if (kind == "+unary") { return gen_expr(expr.expression, -1) } // Increment/Decrement if (kind == "++" || kind == "--") { operand = expr.expression postfix = expr.postfix == true arith_op = kind == "++" ? "add" : "subtract" 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 level = operand.level if (level == null) { level = -1 } old_slot = alloc_slot() if (level == 0) { local = find_var(name) if (local >= 0) { emit_2("move", old_slot, local) } } else if (level > 0) { _lv = level - 1 pstate = parent_states[length(parent_states) - 1 - _lv] pslot = find_var_in_saved(pstate, name) emit_3("get", old_slot, pslot, level) } else { emit_access_intrinsic(old_slot, name) } new_slot = alloc_slot() _bp_ln = null _bp_rn = one_node emit_binop(arith_op, new_slot, old_slot, one_slot) if (level == 0) { local = find_var(name) if (local >= 0) { emit_2("move", local, new_slot) } } else if (level > 0) { _lv = level - 1 pstate = parent_states[length(parent_states) - 1 - _lv] pslot = find_var_in_saved(pstate, name) emit_3("put", new_slot, pslot, level) } return postfix ? old_slot : new_slot } else if (operand_kind == ".") { obj = operand.left prop = operand.right obj_slot = gen_expr(obj, -1) old_slot = alloc_slot() emit_get_prop(old_slot, obj_slot, prop) new_slot = alloc_slot() _bp_ln = null _bp_rn = one_node emit_binop(arith_op, new_slot, old_slot, one_slot) emit_set_prop(obj_slot, prop, new_slot) return postfix ? old_slot : new_slot } else if (operand_kind == "[") { obj = operand.left idx_expr = operand.right obj_slot = gen_expr(obj, -1) idx_slot = gen_expr(idx_expr, -1) old_slot = alloc_slot() emit_get_elem(old_slot, obj_slot, idx_slot, operand.access_kind) new_slot = alloc_slot() _bp_ln = null _bp_rn = one_node emit_binop(arith_op, new_slot, old_slot, one_slot) emit_set_elem(obj_slot, idx_slot, new_slot, operand.access_kind) return postfix ? old_slot : new_slot } } // Delete operator if (kind == "delete") { operand = expr.expression operand_kind = operand.kind slot = alloc_slot() if (operand_kind == ".") { obj = operand.left prop = operand.right obj_slot = gen_expr(obj, -1) add_instr(["delete", slot, obj_slot, prop]) } else if (operand_kind == "[") { obj = operand.left idx = operand.right obj_slot = gen_expr(obj, -1) idx_slot = gen_expr(idx, -1) emit_3("delete", slot, obj_slot, idx_slot) } else { emit_const_bool(slot, true) } return slot } // Ternary if (kind == "then") { cond = expr.expression then_expr = expr.then else_expr = expr["else"] else_label = gen_label("tern_else") end_label = gen_label("tern_end") cond_slot = gen_expr(cond, -1) emit_jump_cond("wary_false", cond_slot, else_label) dest = alloc_slot() then_slot = gen_expr(then_expr, -1) emit_2("move", dest, then_slot) emit_jump(end_label) emit_label(else_label) else_slot = gen_expr(else_expr, -1) emit_2("move", dest, else_slot) emit_label(end_label) return dest } // Array literal if (kind == "array") { list = expr.list count = length(list) elem_slots = [] _i = 0 while (_i < count) { push(elem_slots, gen_expr(list[_i], -1)) _i = _i + 1 } dest = alloc_slot() add_instr(["array", dest, count]) _i = 0 while (_i < count) { emit_2("push", dest, elem_slots[_i]) _i = _i + 1 } return dest } // Object literal if (kind == "record") { list = expr.list dest = alloc_slot() push(s_instructions, ["record", dest, length(list)]) _i = 0 while (_i < length(list)) { pair = list[_i] key = pair.left val = pair.right val_slot = gen_expr(val, -1) key_kind = key.kind if (key_kind == "name") { emit_set_prop(dest, key.name, val_slot) } else if (key_kind == "text") { kname = key.value if (kname == null) { kname = "" } emit_set_prop(dest, kname, val_slot) } else { key_slot = gen_expr(key, -1) emit_set_elem(dest, key_slot, val_slot) } _i = _i + 1 } return dest } // Function expression if (kind == "function") { func = gen_function(expr) func_id = s_func_counter s_func_counter = s_func_counter + 1 push(s_functions, func) dest = alloc_slot() emit_2("function", dest, func_id) return dest } // Assignment operators if (kind == "assign" || kind == "+=" || kind == "-=" || kind == "*=" || kind == "/=" || kind == "%=" || kind == "**=" || kind == "&=" || kind == "|=" || kind == "^=" || kind == "<<=" || kind == ">>=" || kind == ">>>=" || kind == "&&=" || kind == "||=" || kind == "??=") { return gen_assign(expr) } // Binary operators (fallback) return gen_binary(expr, target) } // Statement compilation gen_statement = function(stmt) { var kind = null var left = null var right = null var name = null var local_slot = 0 var arr_expr = null var arr_slot = 0 var val_slot = 0 var list = null var _i = 0 var stmts = null var cond = null var then_stmts = null var else_stmts = null var else_label = null var end_label = null var cond_slot = 0 var start_label = null var old_break = null var old_continue = null var cond_label = null var init = null var test = null var update = null var update_label = null var init_kind = null var test_slot = 0 var expr = null var slot = 0 var null_slot = 0 var call_expr = null var callee = null var args_list = null var arg_slots = null var nargs = 0 var callee_kind = null var obj_node = null var prop = null var obj_slot = 0 var func_slot = 0 var cases = null var switch_val = 0 var default_label = null var case_labels = null var case_node = null var case_kind = null var case_label = null var case_expr = null var case_val = 0 var cmp_slot = 0 var case_stmts = null var _j = 0 var func = null var func_id = 0 var dest = 0 var guard_t = 0 var guard_err = null var guard_done = null var last_instr = null if (stmt == null) { return null } set_pos(stmt) kind = stmt.kind if (kind == null) { return null } if (kind == "var" || kind == "def") { left = stmt.left right = stmt.right name = left.name local_slot = find_var(name) // Pop: var val = arr[] (guarded) if (stmt.pop == true && right != null) { arr_expr = right.left arr_slot = gen_expr(arr_expr, -1) if (local_slot >= 0) { guard_t = alloc_slot() guard_err = gen_label("pop_err") guard_done = gen_label("pop_done") emit_2("is_array", guard_t, arr_slot) emit_jump_cond("jump_false", guard_t, guard_err) emit_2("pop", local_slot, arr_slot) emit_jump(guard_done) emit_label(guard_err) emit_log_error("cannot pop: target must be an array") emit_0("disrupt") emit_label(guard_done) } return null } if (right != null) { val_slot = gen_expr(right, local_slot) if (local_slot >= 0 && val_slot != local_slot) { emit_2("move", local_slot, val_slot) } } else if (local_slot >= 0) { emit_const_null(local_slot) } return null } if (kind == "var_list" || kind == "def_list") { list = stmt.list _i = 0 while (_i < length(list)) { gen_statement(list[_i]) _i = _i + 1 } return null } if (kind == "block") { stmts = stmt.statements _i = 0 while (_i < length(stmts)) { gen_statement(stmts[_i]) _i = _i + 1 } return null } if (kind == "if") { cond = stmt.expression then_stmts = stmt.then else_stmts = stmt["else"] if (else_stmts == null) { else_stmts = stmt.list } else_label = gen_label("if_else") end_label = gen_label("if_end") cond_slot = gen_expr(cond, -1) emit_jump_cond("wary_false", cond_slot, else_label) _i = 0 while (_i < length(then_stmts)) { gen_statement(then_stmts[_i]) _i = _i + 1 } emit_jump(end_label) emit_label(else_label) if (else_stmts != null) { _i = 0 while (_i < length(else_stmts)) { gen_statement(else_stmts[_i]) _i = _i + 1 } } emit_label(end_label) return null } if (kind == "label") { s_pending_label = stmt.name gen_statement(stmt.statement) s_pending_label = null return null } if (kind == "while") { cond = stmt.expression stmts = stmt.statements start_label = gen_label("while_start") end_label = gen_label("while_end") old_break = s_loop_break old_continue = s_loop_continue s_loop_break = end_label s_loop_continue = start_label if (s_pending_label != null) { s_label_map[s_pending_label] = {break_target: end_label, continue_target: start_label} s_pending_label = null } emit_label(start_label) cond_slot = gen_expr(cond, -1) emit_jump_cond("wary_false", cond_slot, end_label) _i = 0 while (_i < length(stmts)) { gen_statement(stmts[_i]) _i = _i + 1 } emit_jump(start_label) emit_label(end_label) s_loop_break = old_break s_loop_continue = old_continue return null } if (kind == "do") { cond = stmt.expression stmts = stmt.statements start_label = gen_label("do_start") cond_label = gen_label("do_cond") end_label = gen_label("do_end") old_break = s_loop_break old_continue = s_loop_continue s_loop_break = end_label s_loop_continue = cond_label if (s_pending_label != null) { s_label_map[s_pending_label] = {break_target: end_label, continue_target: cond_label} s_pending_label = null } emit_label(start_label) _i = 0 while (_i < length(stmts)) { gen_statement(stmts[_i]) _i = _i + 1 } emit_label(cond_label) cond_slot = gen_expr(cond, -1) emit_jump_cond("wary_true", cond_slot, start_label) emit_label(end_label) s_loop_break = old_break s_loop_continue = old_continue return null } if (kind == "for") { init = stmt.init test = stmt.test update = stmt.update stmts = stmt.statements start_label = gen_label("for_start") update_label = gen_label("for_update") end_label = gen_label("for_end") old_break = s_loop_break old_continue = s_loop_continue s_loop_break = end_label s_loop_continue = update_label if (s_pending_label != null) { s_label_map[s_pending_label] = {break_target: end_label, continue_target: update_label} s_pending_label = null } if (init != null) { init_kind = init.kind if (init_kind == "var" || init_kind == "def") { gen_statement(init) } else { gen_expr(init, -1) } } emit_label(start_label) if (test != null) { test_slot = gen_expr(test, -1) emit_jump_cond("wary_false", test_slot, end_label) } _i = 0 while (_i < length(stmts)) { gen_statement(stmts[_i]) _i = _i + 1 } emit_label(update_label) if (update != null) { gen_expr(update, -1) } emit_jump(start_label) emit_label(end_label) s_loop_break = old_break s_loop_continue = old_continue return null } if (kind == "return") { expr = stmt.expression if (expr != null) { slot = gen_expr(expr, -1) // Mark tail calls: rename last invoke to tail_invoke if (stmt.tail == true && !s_has_disruption) { last_instr = s_instructions[length(s_instructions) - 1] if (is_array(last_instr) && last_instr[0] == "invoke") { last_instr[0] = "tail_invoke" } } emit_1("return", slot) } else { null_slot = alloc_slot() emit_1("null", null_slot) emit_1("return", null_slot) } return null } if (kind == "go") { call_expr = stmt.expression if (call_expr == null || call_expr.kind != "(") { return null } callee = call_expr.expression args_list = call_expr.list arg_slots = [] _i = 0 nargs = args_list != null ? length(args_list) : 0 while (_i < nargs) { push(arg_slots, gen_expr(args_list[_i], -1)) _i = _i + 1 } callee_kind = callee.kind if (callee_kind == ".") { obj_node = callee.left prop = callee.right obj_slot = gen_expr(obj_node, -1) emit_go_call_method(obj_slot, prop, arg_slots) } else { func_slot = gen_expr(callee, -1) emit_go_call(func_slot, arg_slots) } return null } if (kind == "disrupt") { emit_0("disrupt") return null } if (kind == "break") { if (stmt.name != null && s_label_map[stmt.name] != null) { emit_jump(s_label_map[stmt.name].break_target) } else if (s_loop_break != null) { emit_jump(s_loop_break) } return null } if (kind == "continue") { if (stmt.name != null && s_label_map[stmt.name] != null) { emit_jump(s_label_map[stmt.name].continue_target) } else if (s_loop_continue != null) { emit_jump(s_loop_continue) } return null } if (kind == "switch") { expr = stmt.expression cases = stmt.cases switch_val = gen_expr(expr, -1) end_label = gen_label("switch_end") default_label = null old_break = s_loop_break s_loop_break = end_label // Phase 1: emit comparisons, collect labels case_labels = [] _i = 0 while (_i < length(cases)) { case_node = cases[_i] case_kind = case_node.kind if (case_kind == "default") { default_label = gen_label("switch_default") push(case_labels, default_label) } else { case_label = gen_label("switch_case") case_expr = case_node.expression case_val = gen_expr(case_expr, -1) cmp_slot = alloc_slot() _bp_ln = null _bp_rn = case_expr emit_binop("eq", cmp_slot, switch_val, case_val) emit_jump_cond("jump_true", cmp_slot, case_label) push(case_labels, case_label) } _i = _i + 1 } if (default_label != null) { emit_jump(default_label) } else { emit_jump(end_label) } // Phase 2: emit case bodies _i = 0 while (_i < length(cases)) { emit_label(case_labels[_i]) case_stmts = cases[_i].statements _j = 0 while (_j < length(case_stmts)) { gen_statement(case_stmts[_j]) _j = _j + 1 } _i = _i + 1 } emit_label(end_label) s_loop_break = old_break return null } if (kind == "function") { name = stmt.name if (name != null) { func = gen_function(stmt) func_id = s_func_counter s_func_counter = s_func_counter + 1 push(s_functions, func) local_slot = find_var(name) dest = alloc_slot() emit_2("function", dest, func_id) if (local_slot >= 0) { emit_2("move", local_slot, dest) } } return null } if (kind == "call") { gen_expr(stmt.expression, -1) return null } gen_expr(stmt, -1) return null } // Function compilation gen_function = function(func_node) { var saved = save_state() var is_arrow = func_node.arrow == true var fn_nr_node = func_node.function_nr var params = func_node.list var nr_params = 0 var param_slot = 1 var _i = 0 var param = null var param_name = null var ps = 1 var default_expr = null var end_label = null var default_slot = 0 var hoisted = null var fn = null var fname = null var compiled = null var func_id = 0 var local_slot = 0 var dest = 0 var stmts = null var body = null var null_slot = 0 var disruption_start = 0 var disrupt_clause = func_node.disruption var null_slot2 = null var fn_name = func_node.name var result = null var saved_label = 0 var saved_func = 0 var captured_this = 0 push(parent_states, saved) s_instructions = [] s_vars = [] s_intrinsic_cache = [] s_slot_types = {} s_num_err_label = null s_num_err_emitted = false s_loop_break = null s_loop_continue = null s_label_map = {} s_is_arrow = is_arrow s_has_disruption = disrupt_clause != null && is_array(disrupt_clause) s_function_nr = fn_nr_node != null ? fn_nr_node : 0 // Parameters if (params == null) { params = func_node.parameters } nr_params = params != null ? length(params) : 0 s_nr_args = nr_params s_this_slot = 0 s_nr_close_slots = 0 s_nr_local_slots = 0 param_slot = 1 _i = 0 while (_i < nr_params) { param = params[_i] param_name = param.name if (param_name == null && is_text(param)) { param_name = param } if (param_name != null) { add_var(param_name, param_slot, true) param_slot = param_slot + 1 } _i = _i + 1 } s_next_temp_slot = 1 + s_nr_args s_max_slot = 1 + s_nr_args // Scan scope scan_scope() s_next_temp_slot = 1 + s_nr_args + s_nr_local_slots if (s_next_temp_slot > s_max_slot) { s_max_slot = s_next_temp_slot } // Arrow functions capture the enclosing this via closure if (is_arrow) { captured_this = alloc_slot() emit_3("get", captured_this, saved.this_slot, 1) s_this_slot = captured_this } // Default parameter initialization ps = 1 _i = 0 while (_i < nr_params) { param = params[_i] default_expr = param.expression if (default_expr != null) { end_label = gen_label("default_end") emit_jump_cond("jump_not_null", ps, end_label) default_slot = gen_expr(default_expr, -1) emit_2("move", ps, default_slot) emit_label(end_label) } ps = ps + 1 _i = _i + 1 } // Pre-load intrinsics load_intrinsics(func_node.intrinsics) // Compile hoisted function declarations hoisted = func_node.functions if (hoisted != null) { _i = 0 while (_i < length(hoisted)) { fn = hoisted[_i] fname = fn.name if (fname != null) { compiled = gen_function(fn) func_id = s_func_counter s_func_counter = s_func_counter + 1 push(s_functions, compiled) local_slot = find_var(fname) dest = alloc_slot() emit_2("function", dest, func_id) if (local_slot >= 0) { emit_2("move", local_slot, dest) } } _i = _i + 1 } } // Compile body stmts = func_node.statements if (stmts == null) { body = func_node.body if (body != null) { stmts = body.statements if (stmts == null) { stmts = body } } } if (stmts != null && is_array(stmts)) { _i = 0 while (_i < length(stmts)) { gen_statement(stmts[_i]) _i = _i + 1 } } // Implicit return null null_slot = alloc_slot() emit_1("null", null_slot) emit_1("return", null_slot) // Compile disruption clause if (disrupt_clause != null && is_array(disrupt_clause)) { emit_label(gen_label("disruption")) disruption_start = length(s_instructions) _i = 0 while (_i < length(disrupt_clause)) { gen_statement(disrupt_clause[_i]) _i = _i + 1 } null_slot2 = alloc_slot() emit_1("null", null_slot2) emit_1("return", null_slot2) } // Build result if (fn_name == null) { fn_name = "" } result = { name: fn_name, nr_args: nr_params, nr_close_slots: s_nr_close_slots, nr_slots: s_max_slot + 1, disruption_pc: disruption_start, instructions: s_instructions } if (s_filename != null) { result.filename = s_filename } // Propagate counters back saved_label = s_label_counter saved_func = s_func_counter // Pop parent state pop(parent_states) restore_state(saved) s_label_counter = saved_label s_func_counter = saved_func return result } // Program compilation (top-level entry) var gen_program = function(ast) { var filename = ast.filename var hoisted = ast.functions var _i = 0 var fn = null var name = null var compiled = null var func_id = 0 var local_slot = 0 var dest = 0 var statements = ast.statements var stmt = null var kind = null var null_slot = 0 var result = null s_filename = filename s_instructions = [] s_data = {} s_functions = [] s_vars = [] s_intrinsic_cache = [] s_scopes = ast.scopes s_this_slot = 0 s_nr_args = 0 s_nr_close_slots = 0 s_nr_local_slots = 0 s_next_temp_slot = 1 s_max_slot = 1 s_label_counter = 0 s_func_counter = 0 s_slot_types = {} s_num_err_label = null s_num_err_emitted = false s_loop_break = null s_loop_continue = null s_label_map = {} s_function_nr = 0 // Scan scope scan_scope() s_next_temp_slot = 1 + s_nr_local_slots if (s_next_temp_slot > s_max_slot) { s_max_slot = s_next_temp_slot } // Compile hoisted function declarations from ast.functions if (hoisted != null) { _i = 0 while (_i < length(hoisted)) { fn = hoisted[_i] name = fn.name if (name != null) { compiled = gen_function(fn) func_id = s_func_counter s_func_counter = s_func_counter + 1 push(s_functions, compiled) local_slot = find_var(name) dest = alloc_slot() emit_2("function", dest, func_id) if (local_slot >= 0) { emit_2("move", local_slot, dest) } } _i = _i + 1 } } // Generate main code _i = 0 while (_i < length(statements)) { stmt = statements[_i] kind = stmt.kind if (kind != null) { if (kind == "call") { gen_expr(stmt.expression, -1) } else if (kind == "return" || kind == "disrupt" || kind == "break" || kind == "continue" || kind == "var" || kind == "def" || kind == "var_list" || kind == "def_list" || kind == "function" || kind == "block" || kind == "if" || kind == "while" || kind == "do" || kind == "for" || kind == "switch") { gen_statement(stmt) } else { gen_expr(stmt, -1) } } else { gen_statement(stmt) } _i = _i + 1 } null_slot = alloc_slot() emit_1("null", null_slot) emit_1("return", null_slot) result = {} result.name = filename != null ? filename : "" result.data = s_data result.functions = s_functions result.main = { nr_args: 0, nr_close_slots: 0, nr_slots: s_max_slot + 1, instructions: s_instructions } if (filename != null) { result.filename = filename } return result } return gen_program(ast) } return mcode