use new parser information

This commit is contained in:
2026-02-06 03:44:44 -06:00
parent 9212003401
commit 017a57b1eb

View File

@@ -523,11 +523,10 @@ typedef enum MachOpcode {
MACH_GETINDEX, /* R(A) = R(B)[R(C)] — computed property */
MACH_SETINDEX, /* R(A)[R(B)] = R(C) — computed property */
/* Global/environment access (ABx) */
MACH_GETGLOBAL, /* R(A) = global[K(Bx)] post-link */
MACH_SETGLOBAL, /* global[K(Bx)] = R(A) — post-link */
MACH_GETNAME, /* R(A) = resolve(K(Bx)) — pre-link, patched to GETGLOBAL */
MACH_SETNAME, /* resolve(K(Bx)) = R(A) — pre-link, patched to SETGLOBAL */
/* Unbound variable access (ABx) */
MACH_GETNAME, /* R(A) = resolve(K(Bx))compiler placeholder, patched by link */
MACH_GETINTRINSIC, /* R(A) = global[K(Bx)] — post-link, intrinsic/built-in */
MACH_GETENV, /* R(A) = env[K(Bx)] — post-link, module environment */
/* Closure access (ABC) */
MACH_GETUP, /* R(A) = outer_frame[B].slots[C] */
@@ -592,10 +591,9 @@ static const char *mach_opcode_names[MACH_OP_COUNT] = {
[MACH_SETFIELD] = "setfield",
[MACH_GETINDEX] = "getindex",
[MACH_SETINDEX] = "setindex",
[MACH_GETGLOBAL] = "getglobal",
[MACH_SETGLOBAL] = "setglobal",
[MACH_GETNAME] = "getname",
[MACH_SETNAME] = "setname",
[MACH_GETINTRINSIC] = "getintrinsic",
[MACH_GETENV] = "getenv",
[MACH_GETUP] = "getup",
[MACH_SETUP] = "setup",
[MACH_JMP] = "jmp",
@@ -30706,6 +30704,10 @@ typedef struct MachCompState {
/* Parent for nested function compilation */
struct MachCompState *parent;
/* AST semantic annotations */
int function_nr; /* current function number (0=program body) */
cJSON *scopes; /* pointer to AST "scopes" array (not owned) */
/* Error tracking */
int has_error;
} MachCompState;
@@ -30825,38 +30827,34 @@ static int mach_add_function(MachCompState *cs, JSCodeRegister *fn) {
return cs->func_count++;
}
/* Scan AST statements for var/def declarations (hoisting) */
static void mach_scan_vars(MachCompState *cs, cJSON *stmts) {
int count = cJSON_GetArraySize(stmts);
/* Find the scope record for a given function_nr in the scopes array */
static cJSON *mach_find_scope_record(cJSON *scopes, int function_nr) {
if (!scopes) return NULL;
int count = cJSON_GetArraySize(scopes);
for (int i = 0; i < count; i++) {
cJSON *stmt = cJSON_GetArrayItem(stmts, i);
const char *kind = cJSON_GetStringValue(cJSON_GetObjectItem(stmt, "kind"));
if (!kind) continue;
cJSON *scope = cJSON_GetArrayItem(scopes, i);
cJSON *fn_nr = cJSON_GetObjectItem(scope, "function_nr");
if (fn_nr && (int)cJSON_GetNumberValue(fn_nr) == function_nr)
return scope;
}
return NULL;
}
if (strcmp(kind, "var") == 0 || strcmp(kind, "def") == 0) {
cJSON *left = cJSON_GetObjectItem(stmt, "left");
const char *name = cJSON_GetStringValue(cJSON_GetObjectItem(left, "name"));
if (name && mach_find_var(cs, name) < 0) {
int slot = mach_reserve_reg(cs);
mach_add_var(cs, name, slot, strcmp(kind, "def") == 0);
}
}
/* Also handle function declarations at top level */
if (strcmp(kind, "call") == 0) {
cJSON *expr = cJSON_GetObjectItem(stmt, "expression");
if (expr) {
const char *ek = cJSON_GetStringValue(cJSON_GetObjectItem(expr, "kind"));
if (ek && strcmp(ek, "function") == 0) {
cJSON *fname = cJSON_GetObjectItem(expr, "name");
if (fname && cJSON_IsString(fname)) {
const char *fn = cJSON_GetStringValue(fname);
if (fn && mach_find_var(cs, fn) < 0) {
int slot = mach_reserve_reg(cs);
mach_add_var(cs, fn, slot, 1);
}
}
}
}
/* Scan AST scope record for variable declarations */
static void mach_scan_scope(MachCompState *cs) {
cJSON *scope = mach_find_scope_record(cs->scopes, cs->function_nr);
cJSON *vars = scope ? cJSON_GetObjectItem(scope, "vars") : NULL;
if (!vars) return;
int vcount = cJSON_GetArraySize(vars);
for (int j = 0; j < vcount; j++) {
cJSON *v = cJSON_GetArrayItem(vars, j);
const char *make = cJSON_GetStringValue(cJSON_GetObjectItem(v, "make"));
if (!make || strcmp(make, "input") == 0) continue;
const char *name = cJSON_GetStringValue(cJSON_GetObjectItem(v, "name"));
if (name && mach_find_var(cs, name) < 0) {
int is_const = (strcmp(make, "def") == 0 || strcmp(make, "function") == 0);
int slot = mach_reserve_reg(cs);
mach_add_var(cs, name, slot, is_const);
}
}
}
@@ -30940,16 +30938,29 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
if (strcmp(kind, "name") == 0) {
const char *name = cJSON_GetStringValue(cJSON_GetObjectItem(node, "name"));
if (name) {
int slot = mach_find_var(cs, name);
if (slot >= 0) {
/* Local variable — just return its register */
if (dest >= 0 && dest != slot) {
mach_emit(cs, MACH_ABC(MACH_MOVE, dest, slot, 0));
return dest;
cJSON *level_node = cJSON_GetObjectItem(node, "level");
int level = level_node ? (int)cJSON_GetNumberValue(level_node) : -1;
if (level == 0) {
/* Local variable */
int slot = mach_find_var(cs, name);
if (slot >= 0) {
if (dest >= 0 && dest != slot) {
mach_emit(cs, MACH_ABC(MACH_MOVE, dest, slot, 0));
return dest;
}
return slot;
}
return slot;
} else if (level > 0) {
/* Closure variable — walk parent compiler states for slot */
MachCompState *target = cs;
for (int i = 0; i < level; i++) target = target->parent;
int slot = mach_find_var(target, name);
if (dest < 0) dest = mach_reserve_reg(cs);
mach_emit(cs, MACH_ABC(MACH_GETUP, dest, level, slot));
return dest;
}
/* Unbound — emit GETNAME (will be patched to GETGLOBAL at link time) */
/* Unbound or fallback — emit placeholder, patched at link time */
if (dest < 0) dest = mach_reserve_reg(cs);
int ki = mach_cpool_add_str(cs, name);
mach_emit(cs, MACH_ABx(MACH_GETNAME, dest, ki));
@@ -31127,22 +31138,35 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
if (lk && strcmp(lk, "name") == 0) {
const char *name = cJSON_GetStringValue(cJSON_GetObjectItem(left, "name"));
int slot = name ? mach_find_var(cs, name) : -1;
if (slot >= 0) {
int r = mach_compile_expr(cs, right, slot);
if (r != slot)
mach_emit(cs, MACH_ABC(MACH_MOVE, slot, r, 0));
if (dest >= 0 && dest != slot)
mach_emit(cs, MACH_ABC(MACH_MOVE, dest, slot, 0));
return slot;
cJSON *level_node = cJSON_GetObjectItem(left, "level");
int level = level_node ? (int)cJSON_GetNumberValue(level_node) : -1;
if (level == 0) {
/* Local assignment */
int slot = name ? mach_find_var(cs, name) : -1;
if (slot >= 0) {
int r = mach_compile_expr(cs, right, slot);
if (r != slot)
mach_emit(cs, MACH_ABC(MACH_MOVE, slot, r, 0));
if (dest >= 0 && dest != slot)
mach_emit(cs, MACH_ABC(MACH_MOVE, dest, slot, 0));
return slot;
}
} else if (level > 0) {
/* Closure assignment — walk parent states for slot */
MachCompState *target = cs;
for (int i = 0; i < level; i++) target = target->parent;
int slot = mach_find_var(target, name);
if (dest < 0) dest = mach_reserve_reg(cs);
int r = mach_compile_expr(cs, right, dest);
if (r != dest)
mach_emit(cs, MACH_ABC(MACH_MOVE, dest, r, 0));
mach_emit(cs, MACH_ABC(MACH_SETUP, dest, level, slot));
return dest;
}
/* Global assignment */
/* Unbound (level -1) — error, AST parser should have rejected this */
if (dest < 0) dest = mach_reserve_reg(cs);
int r = mach_compile_expr(cs, right, dest);
if (r != dest)
mach_emit(cs, MACH_ABC(MACH_MOVE, dest, r, 0));
int ki = mach_cpool_add_str(cs, name);
mach_emit(cs, MACH_ABx(MACH_SETNAME, dest, ki));
mach_emit(cs, MACH_ABx(MACH_LOADNULL, dest, 0));
return dest;
}
@@ -31308,8 +31332,13 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
MachCompState child = {0};
child.ctx = cs->ctx;
child.parent = cs;
child.scopes = cs->scopes;
child.freereg = 1; /* slot 0 = this */
/* Read function_nr from AST node */
cJSON *fn_nr_node = cJSON_GetObjectItem(node, "function_nr");
child.function_nr = fn_nr_node ? (int)cJSON_GetNumberValue(fn_nr_node) : 0;
/* Register parameters */
cJSON *params = cJSON_GetObjectItem(node, "params");
if (!params) params = cJSON_GetObjectItem(node, "parameters");
@@ -31325,15 +31354,14 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
}
}
/* Scan for var/def */
/* Scan scope record for var/def declarations */
mach_scan_scope(&child);
/* Compile body */
cJSON *body = cJSON_GetObjectItem(node, "body");
if (body) {
cJSON *stmts = cJSON_GetObjectItem(body, "statements");
if (!stmts) stmts = body; /* body might be the statements array directly */
if (cJSON_IsArray(stmts))
mach_scan_vars(&child, stmts);
/* Compile body */
if (cJSON_IsArray(stmts)) {
int count = cJSON_GetArraySize(stmts);
for (int i = 0; i < count; i++) {
@@ -31346,10 +31374,12 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
mach_emit(&child, MACH_ABC(MACH_RETNIL, 0, 0, 0));
/* Build JSCodeRegister for the child function */
cJSON *fn_scope = mach_find_scope_record(cs->scopes, child.function_nr);
cJSON *fn_ncs = fn_scope ? cJSON_GetObjectItem(fn_scope, "nr_close_slots") : NULL;
JSCodeRegister *fn_code = js_mallocz_rt(sizeof(JSCodeRegister));
fn_code->arity = nparams;
fn_code->nr_slots = child.maxreg;
fn_code->nr_close_slots = 0;
fn_code->nr_close_slots = fn_ncs ? (int)cJSON_GetNumberValue(fn_ncs) : 0;
fn_code->entry_point = 0;
fn_code->instr_count = child.code_count;
fn_code->instructions = child.code;
@@ -31615,23 +31645,23 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
}
}
/* ---- Link pass: resolve GETNAME/SETNAME to GETGLOBAL/SETGLOBAL ---- */
/* ---- Link pass: resolve GETNAME to GETINTRINSIC or GETENV ---- */
static void mach_link_names(MachCompState *cs, JSValue env) {
for (int i = 0; i < cs->code_count; i++) {
MachInstr32 instr = cs->code[i];
int op = MACH_GET_OP(instr);
if (op == MACH_GETNAME) {
int a = MACH_GET_A(instr);
int bx = MACH_GET_Bx(instr);
/* Patch to GETGLOBAL — the VM will look up K(Bx) in global_obj */
cs->code[i] = MACH_ABx(MACH_GETGLOBAL, a, bx);
} else if (op == MACH_SETNAME) {
int a = MACH_GET_A(instr);
int bx = MACH_GET_Bx(instr);
cs->code[i] = MACH_ABx(MACH_SETGLOBAL, a, bx);
static void mach_link_code(JSContext *ctx, JSCodeRegister *code, JSValue env) {
for (uint32_t i = 0; i < code->instr_count; i++) {
MachInstr32 instr = code->instructions[i];
if (MACH_GET_OP(instr) != MACH_GETNAME) continue;
int a = MACH_GET_A(instr);
int bx = MACH_GET_Bx(instr);
int in_env = 0;
if (!JS_IsNull(env) && (uint32_t)bx < code->cpool_count) {
JSValue val = JS_GetProperty(ctx, env, code->cpool[bx]);
in_env = !JS_IsNull(val) && !JS_IsException(val);
}
code->instructions[i] = MACH_ABx(in_env ? MACH_GETENV : MACH_GETINTRINSIC, a, bx);
}
for (uint32_t i = 0; i < code->func_count; i++)
if (code->functions[i]) mach_link_code(ctx, code->functions[i], env);
}
/* ---- Top-level compiler ---- */
@@ -31640,8 +31670,12 @@ static JSCodeRegister *mach_compile_program(MachCompState *cs, cJSON *ast, JSVal
cJSON *stmts = cJSON_GetObjectItem(ast, "statements");
if (!stmts || !cJSON_IsArray(stmts)) return NULL;
/* Scan for var/def declarations (hoisting) */
mach_scan_vars(cs, stmts);
/* Read scopes array from AST */
cs->scopes = cJSON_GetObjectItem(ast, "scopes");
cs->function_nr = 0;
/* Scan scope record for declarations */
mach_scan_scope(cs);
/* Compile each statement */
int count = cJSON_GetArraySize(stmts);
@@ -31652,14 +31686,15 @@ static JSCodeRegister *mach_compile_program(MachCompState *cs, cJSON *ast, JSVal
/* Implicit return null */
mach_emit(cs, MACH_ABC(MACH_RETNIL, 0, 0, 0));
/* Link: resolve GETNAME/SETNAME */
mach_link_names(cs, env);
/* nr_close_slots from scope record */
cJSON *prog_scope = mach_find_scope_record(cs->scopes, 0);
cJSON *ncs_node = prog_scope ? cJSON_GetObjectItem(prog_scope, "nr_close_slots") : NULL;
/* Build JSCodeRegister */
JSCodeRegister *code = js_mallocz_rt(sizeof(JSCodeRegister));
code->arity = 0;
code->nr_slots = cs->maxreg;
code->nr_close_slots = 0;
code->nr_close_slots = ncs_node ? (int)cJSON_GetNumberValue(ncs_node) : 0;
code->entry_point = 0;
code->instr_count = cs->code_count;
code->instructions = cs->code;
@@ -31684,6 +31719,9 @@ JSCodeRegister *JS_CompileMach(JSContext *ctx, const char *ast_json, JSValue env
JSCodeRegister *code = mach_compile_program(&cs, ast, env);
/* Link: resolve GETNAME to GETINTRINSIC/GETENV */
if (code) mach_link_code(ctx, code, env);
/* Free var table (code/cpool/functions are owned by JSCodeRegister now) */
for (int i = 0; i < cs.var_count; i++)
sys_free(cs.vars[i].name);
@@ -32100,32 +32138,26 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
break;
}
case MACH_GETGLOBAL: {
case MACH_GETINTRINSIC: {
int bx = MACH_GET_Bx(instr);
JSValue key = code->cpool[bx];
JSValue val = JS_GetProperty(ctx, ctx->global_obj, key);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (JS_IsNull(val) || JS_IsException(val)) {
/* Try env too */
if (!JS_IsNull(env)) {
val = JS_GetProperty(ctx, env, key);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
}
}
frame->slots[a] = val;
break;
}
case MACH_SETGLOBAL: {
case MACH_GETENV: {
int bx = MACH_GET_Bx(instr);
JSValue key = code->cpool[bx];
JS_SetProperty(ctx, ctx->global_obj, key, frame->slots[a]);
JSValue val = JS_GetProperty(ctx, env, key);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
frame->slots[a] = val;
break;
}
case MACH_GETNAME: {
/* Runtime fallback: try env then global */
/* Runtime fallback: try env then global (should not appear in linked code) */
int bx = MACH_GET_Bx(instr);
JSValue key = code->cpool[bx];
JSValue val = JS_NULL;
@@ -32141,30 +32173,28 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
break;
}
case MACH_SETNAME: {
int bx = MACH_GET_Bx(instr);
JSValue key = code->cpool[bx];
JS_SetProperty(ctx, ctx->global_obj, key, frame->slots[a]);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
break;
}
case MACH_GETUP: {
/* R(A) = outer_frame[B].slots[C] */
/* R(A) = outer_frame[B].slots[C] — walk lexical scope chain */
int depth = b;
JSFrameRegister *target = frame;
for (int d = 0; d < depth && !JS_IsNull(target->caller); d++)
target = (JSFrameRegister *)JS_VALUE_GET_PTR(target->caller);
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
JSFrameRegister *target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.reg.outer_frame);
for (int d = 1; d < depth; d++) {
fn = JS_VALUE_GET_FUNCTION(target->function);
target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.reg.outer_frame);
}
frame->slots[a] = target->slots[c];
break;
}
case MACH_SETUP: {
/* outer_frame[B].slots[C] = R(A) */
/* outer_frame[B].slots[C] = R(A) — walk lexical scope chain */
int depth = b;
JSFrameRegister *target = frame;
for (int d = 0; d < depth && !JS_IsNull(target->caller); d++)
target = (JSFrameRegister *)JS_VALUE_GET_PTR(target->caller);
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
JSFrameRegister *target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.reg.outer_frame);
for (int d = 1; d < depth; d++) {
fn = JS_VALUE_GET_FUNCTION(target->function);
target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.reg.outer_frame);
}
target->slots[c] = frame->slots[a];
break;
}
@@ -32507,11 +32537,10 @@ static void dump_register_code(JSContext *ctx, JSCodeRegister *code, int indent)
printf("r%d, r%d, r%d", a, b, c);
break;
/* ABx: global/name access */
case MACH_GETGLOBAL:
case MACH_SETGLOBAL:
/* ABx: name/intrinsic/env access */
case MACH_GETNAME:
case MACH_SETNAME: {
case MACH_GETINTRINSIC:
case MACH_GETENV: {
int bx = MACH_GET_Bx(instr);
printf("r%d, #%d", a, bx);
if ((uint32_t)bx < code->cpool_count) {