var json = use("json") var mcode = function(ast) { // Translation tables var binop_map = { "+": "add", "-": "subtract", "*": "multiply", "/": "divide", "%": "modulo", "&": "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 compound_map = { "+=": "add", "-=": "subtract", "*=": "multiply", "/=": "divide", "%=": "modulo", "&=": "bitand", "|=": "bitor", "^=": "bitxor", "<<=": "shl", ">>=": "shr", ">>>=": "ushr" } // 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_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 // 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 // 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, 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 } } 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_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 } // 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_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" } // emit_add_decomposed: int path -> text path -> float path -> disrupt // reads _bp_dest, _bp_left, _bp_right, _bp_ln, _bp_rn from closure var emit_add_decomposed = function() { var dest = _bp_dest var left = _bp_left var right = _bp_right var t0 = 0 var t1 = 0 var left_is_int = is_known_int(_bp_ln) var left_is_text = is_known_text(_bp_ln) var left_is_num = is_known_number(_bp_ln) var right_is_int = is_known_int(_bp_rn) var right_is_text = is_known_text(_bp_rn) var right_is_num = is_known_number(_bp_rn) 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 // reads _bp_dest, _bp_left, _bp_right, _bp_ln, _bp_rn from closure var emit_numeric_binop = function(int_op, float_op) { var dest = _bp_dest var left = _bp_left var right = _bp_right var t0 = 0 var t1 = 0 var left_is_int = is_known_int(_bp_ln) var left_is_num = is_known_number(_bp_ln) var right_is_int = is_known_int(_bp_rn) var right_is_num = is_known_number(_bp_rn) 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) // reads _bp_dest, _bp_left, _bp_right from closure var emit_eq_decomposed = function() { var dest = _bp_dest var left = _bp_left var right = _bp_right 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) // reads _bp_dest, _bp_left, _bp_right from closure var emit_ne_decomposed = function() { var dest = _bp_dest var left = _bp_left var right = _bp_right 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 // reads _bp_dest, _bp_left, _bp_right, _bp_ln, _bp_rn from closure var emit_relational = function(int_op, float_op, text_op) { var dest = _bp_dest var left = _bp_left var right = _bp_right var t0 = 0 var t1 = 0 var left_is_int = is_known_int(_bp_ln) var left_is_num = is_known_number(_bp_ln) var left_is_text = is_known_text(_bp_ln) var right_is_int = is_known_int(_bp_rn) var right_is_num = is_known_number(_bp_rn) var right_is_text = is_known_text(_bp_rn) 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 // 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 if (op_str == "add") { emit_add_decomposed() } else if (op_str == "subtract") { emit_numeric_binop("sub_int", "sub_float") } else if (op_str == "multiply") { emit_numeric_binop("mul_int", "mul_float") } else if (op_str == "divide") { emit_numeric_binop("div_int", "div_float") } else if (op_str == "modulo") { emit_numeric_binop("mod_int", "mod_float") } else if (op_str == "eq") { emit_eq_decomposed() } else if (op_str == "ne") { emit_ne_decomposed() } else if (op_str == "lt") { emit_relational("lt_int", "lt_float", "lt_text") } else if (op_str == "le") { emit_relational("le_int", "le_float", "le_text") } else if (op_str == "gt") { emit_relational("gt_int", "gt_float", "gt_text") } else if (op_str == "ge") { emit_relational("ge_int", "ge_float", "ge_text") } 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_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 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_2("invoke", frame_slot, dest) } var emit_call_method = function(dest, obj, prop, args) { var method_slot = alloc_slot() add_instr(["load_field", method_slot, obj, prop]) var argc = length(args) var frame_slot = alloc_slot() emit_3("frame", frame_slot, method_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_2("invoke", frame_slot, dest) } var emit_call_method_dyn = function(dest, obj, key_reg, args) { var method_slot = alloc_slot() emit_3("load_dynamic", method_slot, obj, key_reg) var argc = length(args) var frame_slot = alloc_slot() emit_3("frame", frame_slot, method_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_2("invoke", frame_slot, dest) } 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 } } // Scan scope record for variable declarations var scan_scope = function() { var scope = find_scope_record(s_function_nr) if (scope == null) { return null } var keys = array(scope) var _i = 0 var name = null var v = null var make = null var is_const = false var slot = 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 (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) if (v.closure == true) { s_vars[length(s_vars) - 1].is_closure = true } } _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 } // 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) { 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("jump_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("jump_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) dest = 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) } } 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) } else { add_instr(["set_var", name, dest]) } 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 if (cop != null) { return gen_compound_assign(node, cop) } // Push syntax: arr[] = val if (node.push == true) { arr_expr = left.left arr_slot = gen_expr(arr_expr, -1) val_slot = gen_expr(right, -1) emit_2("push", arr_slot, val_slot) return val_slot } val_slot = gen_expr(right, -1) left_kind = left.kind 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) { emit_2("move", slot, val_slot) } else if (level == -1) { add_instr(["set_var", name, val_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", val_slot, pslot, level) } } else 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 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 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) return slot } if (kind == "text") { slot = target >= 0 ? target : alloc_slot() val = expr.value if (val == null) { val = "" } emit_const_str(slot, val) 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() arr_instr = ["array", arr_slot, nexpr] _i = 0 while (_i < nexpr) { push(arr_instr, expr_slots[_i]) _i = _i + 1 } add_instr(arr_instr) // 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]) 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) return slot } if (kind == "false") { slot = target >= 0 ? target : alloc_slot() emit_const_bool(slot, false) return slot } if (kind == "null") { slot = target >= 0 ? target : alloc_slot() emit_const_null(slot) 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 } // 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) push(s_instructions, ["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("jump_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() instr = ["array", dest, count] _i = 0 while (_i < count) { push(instr, elem_slots[_i]) _i = _i + 1 } push(s_instructions, instr) return dest } // Object literal if (kind == "record") { list = expr.list dest = alloc_slot() push(s_instructions, ["record", dest, 0]) _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) } // 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 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[] if (stmt.pop == true && right != null) { arr_expr = right.left arr_slot = gen_expr(arr_expr, -1) if (local_slot >= 0) { emit_2("pop", local_slot, arr_slot) } 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("jump_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 == "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 emit_label(start_label) cond_slot = gen_expr(cond, -1) emit_jump_cond("jump_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 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("jump_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 (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("jump_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) 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 (s_loop_break != null) { emit_jump(s_loop_break) } return null } if (kind == "continue") { 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 fn_scope = null var nr_cs = 0 var result = null var saved_label = 0 var saved_func = 0 push(parent_states, saved) s_instructions = [] s_vars = [] s_intrinsic_cache = [] s_loop_break = null s_loop_continue = null s_is_arrow = is_arrow 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 } // 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)) { 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 = "" } fn_scope = find_scope_record(s_function_nr) if (fn_scope != null && fn_scope.nr_close_slots != null) { nr_cs = fn_scope.nr_close_slots } result = { name: fn_name, nr_args: nr_params, nr_close_slots: nr_cs, 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 last_expr_slot = -1 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_loop_break = null s_loop_continue = null 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") { last_expr_slot = gen_expr(stmt.expression, -1) } else if (kind == "return" || kind == "disrupt" || kind == "break" || kind == "continue") { gen_statement(stmt) last_expr_slot = -1 } else if (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) last_expr_slot = -1 } else { last_expr_slot = gen_expr(stmt, -1) } } else { gen_statement(stmt) } _i = _i + 1 } if (last_expr_slot >= 0) { emit_1("return", last_expr_slot) } else { 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