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 // 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]) } var emit_get_prop = function(dest, obj, prop) { add_instr(["load", dest, obj, prop]) } var emit_set_prop = function(obj, prop, val) { add_instr(["store", obj, val, prop]) } var emit_get_elem = function(dest, obj, idx) { emit_3("load", dest, obj, idx) } var emit_set_elem = function(obj, idx, val) { emit_3("store", 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 instr = ["callmethod", dest, obj, prop] var _i = 0 while (_i < length(args)) { push(instr, args[_i]) _i = _i + 1 } add_instr(instr) } var emit_call_method_dyn = function(dest, obj, key_reg, args) { var instr = ["callmethod_dyn", dest, obj, key_reg] var _i = 0 while (_i < length(args)) { push(instr, args[_i]) _i = _i + 1 } add_instr(instr) } 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" } emit_3(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() emit_3(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() emit_3(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) right_slot = gen_expr(right, -1) dest = alloc_slot() emit_3(op, dest, old_val, right_slot) emit_set_elem(obj_slot, idx_slot, dest) 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) } 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 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) 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() emit_3(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_2("neg", slot, operand_slot) 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) 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() emit_3(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() emit_3(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) new_slot = alloc_slot() emit_3(arith_op, new_slot, old_slot, one_slot) emit_set_elem(obj_slot, idx_slot, new_slot) 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) { if (stmt == null) { return null } set_pos(stmt) var kind = stmt.kind if (kind == null) { return null } if (kind == "var" || kind == "def") { var left = stmt.left var right = stmt.right var name = left.name var local_slot = find_var(name) // Pop: var val = arr[] if (stmt.pop == true && right != null) { var arr_expr = right.left var arr_slot = gen_expr(arr_expr, -1) if (local_slot >= 0) { emit_2("pop", local_slot, arr_slot) } return null } if (right != null) { var 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") { var list = stmt.list var _i = 0 while (_i < length(list)) { gen_statement(list[_i]) _i = _i + 1 } return null } if (kind == "block") { var stmts = stmt.statements var _i = 0 while (_i < length(stmts)) { gen_statement(stmts[_i]) _i = _i + 1 } return null } if (kind == "if") { var cond = stmt.expression var then_stmts = stmt.then var else_stmts = stmt["else"] if (else_stmts == null) { else_stmts = stmt.list } var else_label = gen_label("if_else") var end_label = gen_label("if_end") var cond_slot = gen_expr(cond, -1) emit_jump_cond("jump_false", cond_slot, else_label) var _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") { var cond = stmt.expression var stmts = stmt.statements var start_label = gen_label("while_start") var end_label = gen_label("while_end") var old_break = s_loop_break var old_continue = s_loop_continue s_loop_break = end_label s_loop_continue = start_label emit_label(start_label) var cond_slot = gen_expr(cond, -1) emit_jump_cond("jump_false", cond_slot, end_label) var _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") { var cond = stmt.expression var stmts = stmt.statements var start_label = gen_label("do_start") var cond_label = gen_label("do_cond") var end_label = gen_label("do_end") var old_break = s_loop_break var old_continue = s_loop_continue s_loop_break = end_label s_loop_continue = cond_label emit_label(start_label) var _i = 0 while (_i < length(stmts)) { gen_statement(stmts[_i]) _i = _i + 1 } emit_label(cond_label) var 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") { var init = stmt.init var test = stmt.test var update = stmt.update var stmts = stmt.statements var start_label = gen_label("for_start") var update_label = gen_label("for_update") var end_label = gen_label("for_end") var old_break = s_loop_break var old_continue = s_loop_continue s_loop_break = end_label s_loop_continue = update_label if (init != null) { var 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) { var test_slot = gen_expr(test, -1) emit_jump_cond("jump_false", test_slot, end_label) } var _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") { var expr = stmt.expression if (expr != null) { var slot = gen_expr(expr, -1) emit_1("return", slot) } else { var null_slot = alloc_slot() emit_1("null", null_slot) emit_1("return", null_slot) } return null } if (kind == "go") { var call_expr = stmt.expression if (call_expr == null || call_expr.kind != "(") { return null } var callee = call_expr.expression var args_list = call_expr.list var arg_slots = [] var _i = 0 var nargs = args_list != null ? length(args_list) : 0 while (_i < nargs) { push(arg_slots, gen_expr(args_list[_i], -1)) _i = _i + 1 } var callee_kind = callee.kind if (callee_kind == ".") { var obj_node = callee.left var prop = callee.right var obj_slot = gen_expr(obj_node, -1) emit_go_call_method(obj_slot, prop, arg_slots) } else { var 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") { var expr = stmt.expression var cases = stmt.cases var switch_val = gen_expr(expr, -1) var end_label = gen_label("switch_end") var default_label = null var old_break = s_loop_break s_loop_break = end_label // Phase 1: emit comparisons, collect labels var case_labels = [] var _i = 0 while (_i < length(cases)) { var case_node = cases[_i] var case_kind = case_node.kind if (case_kind == "default") { default_label = gen_label("switch_default") push(case_labels, default_label) } else { var case_label = gen_label("switch_case") var case_expr = case_node.expression var case_val = gen_expr(case_expr, -1) var cmp_slot = alloc_slot() emit_3("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]) var case_stmts = cases[_i].statements var _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") { var name = stmt.name if (name != null) { var func = gen_function(stmt) var func_id = s_func_counter s_func_counter = s_func_counter + 1 push(s_functions, func) var local_slot = find_var(name) var 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() push(parent_states, saved) s_instructions = [] s_vars = [] s_intrinsic_cache = [] s_loop_break = null s_loop_continue = null var is_arrow = func_node.arrow == true s_is_arrow = is_arrow var fn_nr_node = func_node.function_nr s_function_nr = fn_nr_node != null ? fn_nr_node : 0 // Parameters var params = func_node.list if (params == null) { params = func_node.parameters } var 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 var param_slot = 1 var _i = 0 while (_i < nr_params) { var param = params[_i] var 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 var ps = 1 _i = 0 while (_i < nr_params) { var param = params[_i] var default_expr = param.expression if (default_expr != null) { var end_label = gen_label("default_end") emit_jump_cond("jump_not_null", ps, end_label) var 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 var hoisted = func_node.functions if (hoisted != null) { _i = 0 while (_i < length(hoisted)) { var fn = hoisted[_i] var fname = fn.name if (fname != null) { var compiled = gen_function(fn) var func_id = s_func_counter s_func_counter = s_func_counter + 1 push(s_functions, compiled) var local_slot = find_var(fname) var dest = alloc_slot() emit_2("function", dest, func_id) if (local_slot >= 0) { emit_2("move", local_slot, dest) } } _i = _i + 1 } } // Compile body var stmts = func_node.statements if (stmts == null) { var 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 var null_slot = alloc_slot() emit_1("null", null_slot) emit_1("return", null_slot) // Compile disruption clause var disruption_start = 0 var disrupt_clause = func_node.disruption var null_slot2 = null 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 var fn_name = func_node.name if (fn_name == null) { fn_name = "" } var fn_scope = find_scope_record(s_function_nr) var nr_cs = 0 if (fn_scope != null && fn_scope.nr_close_slots != null) { nr_cs = fn_scope.nr_close_slots } var 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 var saved_label = s_label_counter var 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 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 var hoisted = ast.functions if (hoisted != null) { var _i = 0 while (_i < length(hoisted)) { var fn = hoisted[_i] var name = fn.name if (name != null) { var compiled = gen_function(fn) var func_id = s_func_counter s_func_counter = s_func_counter + 1 push(s_functions, compiled) var local_slot = find_var(name) var 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 var statements = ast.statements var last_expr_slot = -1 var _i = 0 while (_i < length(statements)) { var stmt = statements[_i] var 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 { var null_slot = alloc_slot() emit_1("null", null_slot) emit_1("return", null_slot) } var 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