From 017a57b1eb7803d5a2c0bb4c0d8bf47c67fffc33 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Fri, 6 Feb 2026 03:44:44 -0600 Subject: [PATCH] use new parser information --- source/quickjs.c | 261 ++++++++++++++++++++++++++--------------------- 1 file changed, 145 insertions(+), 116 deletions(-) diff --git a/source/quickjs.c b/source/quickjs.c index a1419420..987aee2c 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -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) {