From 68e2395b927818fef9d4f5ad384e12541112274c Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Mon, 9 Feb 2026 17:01:39 -0600 Subject: [PATCH] mcode generators --- mcode.ce | 10 + mcode.cm | 1626 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1636 insertions(+) create mode 100644 mcode.ce create mode 100644 mcode.cm diff --git a/mcode.ce b/mcode.ce new file mode 100644 index 00000000..ef94d7d8 --- /dev/null +++ b/mcode.ce @@ -0,0 +1,10 @@ +var fd = use("fd") +var tokenize = use("tokenize") +var parse = use("parse") +var mcode = use("mcode") +var filename = args[0] +var src = text(fd.slurp(filename)) +var result = tokenize(src, filename) +var ast = parse(result.tokens, src, filename) +var compiled = mcode(ast) +print(json.encode(compiled)) diff --git a/mcode.cm b/mcode.cm new file mode 100644 index 00000000..6bf8c2ba --- /dev/null +++ b/mcode.cm @@ -0,0 +1,1626 @@ +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 + while (_i < length(s_scopes)) { + var 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 + while (_i < length(intrinsics)) { + var name = intrinsics[_i] + if (name == null || length(s_intrinsic_cache) >= 64) { + _i = _i + 1 + continue + } + if (find_intrinsic(name) >= 0) { + _i = _i + 1 + continue + } + var slot = alloc_slot() + var 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 + while (_i < length(keys)) { + var name = keys[_i] + if (name == "function_nr" || name == "nr_close_slots") { + _i = _i + 1 + continue + } + var v = scope[name] + var make = v.make + if (make == null || make == "input") { + _i = _i + 1 + continue + } + if (find_var(name) < 0) { + var is_const = (make == "def" || make == "function") + var 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 + + if (kind == "&&") { + var end_label = gen_label("and_end") + var left_slot = gen_expr(left, -1) + var dest = alloc_slot() + emit_2("move", dest, left_slot) + emit_jump_cond("jump_false", dest, end_label) + var right_slot = gen_expr(right, -1) + emit_2("move", dest, right_slot) + emit_label(end_label) + return dest + } + + if (kind == "||") { + var end_label = gen_label("or_end") + var left_slot = gen_expr(left, -1) + var dest = alloc_slot() + emit_2("move", dest, left_slot) + emit_jump_cond("jump_true", dest, end_label) + var right_slot = gen_expr(right, -1) + emit_2("move", dest, right_slot) + emit_label(end_label) + return dest + } + + if (kind == "??") { + var end_label = gen_label("nullish_end") + var left_slot = gen_expr(left, -1) + var dest = alloc_slot() + emit_2("move", dest, left_slot) + emit_jump_cond("jump_not_null", dest, end_label) + var 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 + var left_slot = gen_expr(left, -1) + var right_slot = gen_expr(right, -1) + var dest = alloc_slot() + var 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 + + if (left_kind == "name") { + var name = left.name + var level = left.level + if (level == null) { + level = -1 + } + var left_slot = alloc_slot() + if (level == 0 || level == -1) { + var local = find_var(name) + if (local >= 0) { + emit_2("move", left_slot, local) + level = 0 + } + } + if (level > 0) { + var _lv = level - 1 + var pstate = parent_states[length(parent_states) - 1 - _lv] + var pslot = find_var_in_saved(pstate, name) + emit_3("get", left_slot, pslot, level) + } else if (level == -1) { + emit_access_intrinsic(left_slot, name) + } + var right_slot = gen_expr(right, -1) + var dest = alloc_slot() + emit_3(op, dest, left_slot, right_slot) + if (level == 0) { + var local = find_var(name) + if (local >= 0) { + emit_2("move", local, dest) + } + } else if (level > 0) { + var _lv = level - 1 + var pstate = parent_states[length(parent_states) - 1 - _lv] + var 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 == ".") { + var obj = left.left + var prop = left.right + var obj_slot = gen_expr(obj, -1) + var old_val = alloc_slot() + emit_get_prop(old_val, obj_slot, prop) + var right_slot = gen_expr(right, -1) + var dest = alloc_slot() + emit_3(op, dest, old_val, right_slot) + emit_set_prop(obj_slot, prop, dest) + return dest + } else if (left_kind == "[") { + var obj = left.left + var idx_expr = left.right + var obj_slot = gen_expr(obj, -1) + var idx_slot = gen_expr(idx_expr, -1) + var old_val = alloc_slot() + emit_get_elem(old_val, obj_slot, idx_slot) + var right_slot = gen_expr(right, -1) + var 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] + if (cop != null) { + return gen_compound_assign(node, cop) + } + + // Push syntax: arr[] = val + if (node.push == true) { + var arr_expr = left.left + var arr_slot = gen_expr(arr_expr, -1) + var val_slot = gen_expr(right, -1) + emit_2("push", arr_slot, val_slot) + return val_slot + } + + var val_slot = gen_expr(right, -1) + var left_kind = left.kind + + if (left_kind == "name") { + var name = left.name + var level = left.level + if (level == null) { + level = -1 + } + if (level == 0 || level == -1) { + var 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) { + var _lv = level - 1 + var pstate = parent_states[length(parent_states) - 1 - _lv] + var pslot = find_var_in_saved(pstate, name) + emit_3("put", val_slot, pslot, level) + } + } else if (left_kind == ".") { + var obj = left.left + var prop = left.right + var obj_slot = gen_expr(obj, -1) + emit_set_prop(obj_slot, prop, val_slot) + } else if (left_kind == "[") { + var obj = left.left + var idx_expr = left.right + var obj_slot = gen_expr(obj, -1) + var 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) { + if (expr == null) { + return -1 + } + set_pos(expr) + var kind = expr.kind + if (kind == null) { + return -1 + } + + // Literals + if (kind == "number") { + var slot = target >= 0 ? target : alloc_slot() + emit_const_num(slot, expr.number) + return slot + } + if (kind == "text") { + var slot = target >= 0 ? target : alloc_slot() + var val = expr.value + if (val == null) { + val = "" + } + emit_const_str(slot, val) + return slot + } + // Template literal + if (kind == "text literal") { + var list = expr.list + var nexpr = list != null ? length(list) : 0 + var expr_slots = [] + var _i = 0 + while (_i < nexpr) { + push(expr_slots, gen_expr(list[_i], -1)) + _i = _i + 1 + } + // Create array from expression results + var arr_slot = alloc_slot() + var 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 + var 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 + var fmt = expr.value + if (fmt == null) { + fmt = "" + } + var fmt_str_slot = alloc_slot() + emit_const_str(fmt_str_slot, fmt) + // Call format(fmt_str, array) + var 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") { + var slot = target >= 0 ? target : alloc_slot() + var pattern = expr.pattern + if (pattern == null) { + pattern = "" + } + var flags = expr.flags + if (flags == null) { + flags = "" + } + add_instr(["regexp", slot, pattern, flags]) + return slot + } + if (kind == "true") { + var slot = target >= 0 ? target : alloc_slot() + emit_const_bool(slot, true) + return slot + } + if (kind == "false") { + var slot = target >= 0 ? target : alloc_slot() + emit_const_bool(slot, false) + return slot + } + if (kind == "null") { + var slot = target >= 0 ? target : alloc_slot() + emit_const_null(slot) + return slot + } + if (kind == "this") { + return s_this_slot + } + + // Variable reference + if (kind == "name") { + var name = expr.name + var level = expr.level + if (level == null) { + level = -1 + } + if (level == 0 || level == -1) { + var slot = find_var(name) + if (slot >= 0) { + return slot + } + } else if (level > 0) { + var _lv = level - 1 + var pstate = parent_states[length(parent_states) - 1 - _lv] + var parent_slot = find_var_in_saved(pstate, name) + var dest = alloc_slot() + emit_3("get", dest, parent_slot, level) + return dest + } + // Unbound — check intrinsic cache + var cached = find_intrinsic(name) + if (cached >= 0) { + return cached + } + var dest = alloc_slot() + emit_access_intrinsic(dest, name) + return dest + } + + // Property access + if (kind == ".") { + var obj = expr.left + var prop = expr.right + var obj_slot = gen_expr(obj, -1) + var slot = alloc_slot() + emit_get_prop(slot, obj_slot, prop) + return slot + } + + // Element access + if (kind == "[") { + var obj = expr.left + var idx = expr.right + var obj_slot = gen_expr(obj, -1) + var idx_slot = gen_expr(idx, -1) + var slot = alloc_slot() + emit_get_elem(slot, obj_slot, idx_slot) + return slot + } + + // Function call + if (kind == "(") { + var callee = expr.expression + var args_list = expr.list + var callee_kind = callee.kind + + // Functino: inline operator call + if (callee_kind == "name" && callee.make == "functino") { + var fname = callee.name + var mop = functino_map[fname] + var nargs = args_list != null ? length(args_list) : 0 + + if (fname == "~!") { + var a0 = gen_expr(args_list[0], -1) + var d = alloc_slot() + emit_2(mop, d, a0) + return d + } + if (fname == "[]!") { + var a0 = gen_expr(args_list[0], -1) + var a1 = gen_expr(args_list[1], -1) + var d = alloc_slot() + emit_get_elem(d, a0, a1) + return d + } + if ((fname == "=!" || fname == "!=!") && nargs == 3) { + var a0 = gen_expr(args_list[0], -1) + var a1 = gen_expr(args_list[1], -1) + var a2 = gen_expr(args_list[2], -1) + var d = alloc_slot() + var top = fname == "=!" ? "eq_tol" : "ne_tol" + emit_4_full(top, [d, a0, a1, a2]) + return d + } + if (fname == "&&!") { + var a0 = gen_expr(args_list[0], -1) + var a1 = gen_expr(args_list[1], -1) + var d = alloc_slot() + emit_3("and", d, a0, a1) + return d + } + if (fname == "||!") { + var a0 = gen_expr(args_list[0], -1) + var a1 = gen_expr(args_list[1], -1) + var d = alloc_slot() + emit_3("or", d, a0, a1) + return d + } + // Standard 2-arg binary functino + var a0 = gen_expr(args_list[0], -1) + var a1 = gen_expr(args_list[1], -1) + var d = alloc_slot() + emit_3(mop, d, a0, a1) + return d + } + + // Collect arg slots + 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 dest = alloc_slot() + if (callee_kind == ".") { + var obj = callee.left + var prop = callee.right + var obj_slot = gen_expr(obj, -1) + emit_call_method(dest, obj_slot, prop, arg_slots) + } else if (callee_kind == "[") { + var obj = callee.left + var key_expr = callee.right + var obj_slot = gen_expr(obj, -1) + var key_slot = gen_expr(key_expr, -1) + emit_call_method_dyn(dest, obj_slot, key_slot, arg_slots) + } else { + var func_slot = gen_expr(callee, -1) + emit_call(dest, func_slot, arg_slots) + } + return dest + } + + // Unary operators + if (kind == "!") { + var operand_slot = gen_expr(expr.expression, -1) + var slot = alloc_slot() + emit_2("not", slot, operand_slot) + return slot + } + if (kind == "~") { + var operand_slot = gen_expr(expr.expression, -1) + var slot = alloc_slot() + emit_2("bitnot", slot, operand_slot) + return slot + } + if (kind == "-unary") { + var operand_slot = gen_expr(expr.expression, -1) + var 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 == "--") { + var operand = expr.expression + var postfix = expr.postfix == true + var arith_op = kind == "++" ? "add" : "subtract" + var operand_kind = operand.kind + var one_slot = alloc_slot() + emit_2("int", one_slot, 1) + + if (operand_kind == "name") { + var name = operand.name + var level = operand.level + if (level == null) { + level = -1 + } + var old_slot = alloc_slot() + if (level == 0) { + var local = find_var(name) + if (local >= 0) { + emit_2("move", old_slot, local) + } + } else if (level > 0) { + var _lv = level - 1 + var pstate = parent_states[length(parent_states) - 1 - _lv] + var pslot = find_var_in_saved(pstate, name) + emit_3("get", old_slot, pslot, level) + } else { + emit_access_intrinsic(old_slot, name) + } + var new_slot = alloc_slot() + emit_3(arith_op, new_slot, old_slot, one_slot) + if (level == 0) { + var local = find_var(name) + if (local >= 0) { + emit_2("move", local, new_slot) + } + } else if (level > 0) { + var _lv = level - 1 + var pstate = parent_states[length(parent_states) - 1 - _lv] + var pslot = find_var_in_saved(pstate, name) + emit_3("put", new_slot, pslot, level) + } + return postfix ? old_slot : new_slot + } else if (operand_kind == ".") { + var obj = operand.left + var prop = operand.right + var obj_slot = gen_expr(obj, -1) + var old_slot = alloc_slot() + emit_get_prop(old_slot, obj_slot, prop) + var 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 == "[") { + var obj = operand.left + var idx_expr = operand.right + var obj_slot = gen_expr(obj, -1) + var idx_slot = gen_expr(idx_expr, -1) + var old_slot = alloc_slot() + emit_get_elem(old_slot, obj_slot, idx_slot) + var 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") { + var operand = expr.expression + var operand_kind = operand.kind + var slot = alloc_slot() + if (operand_kind == ".") { + var obj = operand.left + var prop = operand.right + var obj_slot = gen_expr(obj, -1) + push(s_instructions, ["delete", slot, obj_slot, prop]) + } else if (operand_kind == "[") { + var obj = operand.left + var idx = operand.right + var obj_slot = gen_expr(obj, -1) + var 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") { + var cond = expr.expression + var then_expr = expr.then + var else_expr = expr["else"] + var else_label = gen_label("tern_else") + var end_label = gen_label("tern_end") + var cond_slot = gen_expr(cond, -1) + emit_jump_cond("jump_false", cond_slot, else_label) + var dest = alloc_slot() + var then_slot = gen_expr(then_expr, -1) + emit_2("move", dest, then_slot) + emit_jump(end_label) + emit_label(else_label) + var else_slot = gen_expr(else_expr, -1) + emit_2("move", dest, else_slot) + emit_label(end_label) + return dest + } + + // Array literal + if (kind == "array") { + var list = expr.list + var count = length(list) + var elem_slots = [] + var _i = 0 + while (_i < count) { + push(elem_slots, gen_expr(list[_i], -1)) + _i = _i + 1 + } + var dest = alloc_slot() + var 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") { + var list = expr.list + var dest = alloc_slot() + push(s_instructions, ["record", dest, 0]) + var _i = 0 + while (_i < length(list)) { + var pair = list[_i] + var key = pair.left + var val = pair.right + var val_slot = gen_expr(val, -1) + var key_kind = key.kind + if (key_kind == "name") { + emit_set_prop(dest, key.name, val_slot) + } else if (key_kind == "text") { + var kname = key.value + if (kname == null) { + kname = "" + } + emit_set_prop(dest, kname, val_slot) + } else { + var key_slot = gen_expr(key, -1) + emit_set_elem(dest, key_slot, val_slot) + } + _i = _i + 1 + } + return dest + } + + // Function expression + if (kind == "function") { + var func = gen_function(expr) + var func_id = s_func_counter + s_func_counter = s_func_counter + 1 + push(s_functions, func) + var 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