Files
cell/mcode.cm
2026-02-09 17:01:39 -06:00

1627 lines
43 KiB
Plaintext

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",
"<!": "lt", ">!": "gt", "<=!": "le", ">=!": "ge",
"=!": "eq", "!=!": "ne",
"&!": "bitand", "|!": "bitor", "^!": "bitxor",
"<<!": "shl", ">>!": "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 = "<anonymous>"
}
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 : "<eval>"
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