This commit is contained in:
2026-02-05 03:10:06 -06:00
parent 058cdfd2e4
commit 9bd764b11b

View File

@@ -28684,6 +28684,17 @@ static cJSON *ast_parse_statement (ASTParseState *s) {
ast_node_end (s, node, s->buf_ptr);
} break;
case TOK_GO: {
node = ast_node (s, "go", start);
ast_next_token (s);
if (s->token_val != ';' && s->token_val != '}' && !s->got_lf) {
cJSON *expr = ast_parse_expr (s);
cJSON_AddItemToObject (node, "expression", expr);
}
if (s->token_val == ';') ast_next_token (s);
ast_node_end (s, node, s->buf_ptr);
} break;
case TOK_THROW: {
node = ast_node (s, "throw", start);
ast_next_token (s);
@@ -28961,6 +28972,7 @@ typedef struct MachGenState {
const char *loop_continue;
int is_arrow;
int has_inner_function; /* Set if function contains nested functions */
/* Error tracking */
cJSON *errors;
@@ -29170,6 +29182,39 @@ static void mach_scan_vars (MachGenState *s, cJSON *node) {
/* NOTE: Do NOT recurse into nested function nodes - they have their own scope */
}
/* Recursively scan for inner function definitions */
static void mach_scan_inner_functions (MachGenState *s, cJSON *node) {
if (!node || s->has_inner_function) return;
const char *kind = cJSON_GetStringValue (cJSON_GetObjectItem (node, "kind"));
if (!kind) return;
/* Found an inner function */
if (strcmp (kind, "function") == 0 || strcmp (kind, "=>") == 0) {
s->has_inner_function = 1;
return;
}
/* Scan child nodes */
cJSON *expr = cJSON_GetObjectItem (node, "expression");
cJSON *left = cJSON_GetObjectItem (node, "left");
cJSON *right = cJSON_GetObjectItem (node, "right");
cJSON *list = cJSON_GetObjectItem (node, "list");
cJSON *stmts = cJSON_GetObjectItem (node, "statements");
cJSON *then_stmts = cJSON_GetObjectItem (node, "then");
cJSON *else_stmts = cJSON_GetObjectItem (node, "else");
if (expr) mach_scan_inner_functions (s, expr);
if (left) mach_scan_inner_functions (s, left);
if (right) mach_scan_inner_functions (s, right);
cJSON *item;
if (list) cJSON_ArrayForEach (item, list) mach_scan_inner_functions (s, item);
if (stmts) cJSON_ArrayForEach (item, stmts) mach_scan_inner_functions (s, item);
if (then_stmts) cJSON_ArrayForEach (item, then_stmts) mach_scan_inner_functions (s, item);
if (else_stmts) cJSON_ArrayForEach (item, else_stmts) mach_scan_inner_functions (s, item);
}
/* Resolve a variable - returns resolution type and slot/depth info */
static MachResolveResult mach_resolve_var (MachGenState *s, const char *name) {
MachResolveResult result = {MACH_VAR_UNBOUND, -1, 0};
@@ -29449,6 +29494,46 @@ static void mach_emit_call_method (MachGenState *s, int dest, int obj, const cha
mach_emit_2 (s, "invoke", frame_slot, dest);
}
/* Emit tail call using goframe/goinvoke sequence */
static void mach_emit_go_call (MachGenState *s, int func_slot, cJSON *args) {
int argc = cJSON_GetArraySize (args);
int frame_slot = mach_alloc_slot (s);
mach_emit_3 (s, "goframe", frame_slot, func_slot, argc);
int null_slot = mach_alloc_slot (s);
mach_emit_1 (s, "null", null_slot);
mach_emit_2 (s, "set_this", frame_slot, null_slot);
int arg_idx = 1;
cJSON *arg;
cJSON_ArrayForEach (arg, args) {
mach_emit_3 (s, "arg", frame_slot, arg_idx++, arg->valueint);
}
mach_emit_1 (s, "goinvoke", frame_slot);
}
/* Emit method tail call using goframe/goinvoke sequence */
static void mach_emit_go_call_method (MachGenState *s, int obj, const char *prop, cJSON *args) {
int func_slot = mach_alloc_slot (s);
mach_emit_get_prop (s, func_slot, obj, prop);
int argc = cJSON_GetArraySize (args);
int frame_slot = mach_alloc_slot (s);
mach_emit_3 (s, "goframe", frame_slot, func_slot, argc);
mach_emit_2 (s, "set_this", frame_slot, obj);
int arg_idx = 1;
cJSON *arg;
cJSON_ArrayForEach (arg, args) {
mach_emit_3 (s, "arg", frame_slot, arg_idx++, arg->valueint);
}
mach_emit_1 (s, "goinvoke", frame_slot);
}
/* Map JS operator to opcode string */
static const char *mach_binop_to_string (const char *kind) {
if (strcmp (kind, "+") == 0) return "add";
@@ -30201,6 +30286,55 @@ static void mach_gen_statement (MachGenState *s, cJSON *stmt) {
return;
}
/* Go (tail call) statement */
if (strcmp (kind, "go") == 0) {
if (s->has_inner_function) {
mach_error (s, stmt, "'go' cannot be used in functions containing inner functions");
return;
}
cJSON *call_expr = cJSON_GetObjectItem (stmt, "expression");
if (!call_expr) {
mach_error (s, stmt, "'go' requires a function call expression");
return;
}
const char *call_kind = cJSON_GetStringValue (cJSON_GetObjectItem (call_expr, "kind"));
if (!call_kind || strcmp (call_kind, "(") != 0) {
mach_error (s, stmt, "'go' requires a function call expression");
return;
}
cJSON *callee = cJSON_GetObjectItem (call_expr, "expression");
cJSON *args_list = cJSON_GetObjectItem (call_expr, "list");
/* Compile arguments first */
cJSON *arg_slots = cJSON_CreateArray ();
cJSON *arg;
cJSON_ArrayForEach (arg, args_list) {
int arg_slot = mach_gen_expr (s, arg);
cJSON_AddItemToArray (arg_slots, cJSON_CreateNumber (arg_slot));
}
const char *callee_kind = cJSON_GetStringValue (cJSON_GetObjectItem (callee, "kind"));
if (callee_kind && strcmp (callee_kind, ".") == 0) {
/* Method tail call: go obj.method(args) */
cJSON *obj_node = cJSON_GetObjectItem (callee, "left");
cJSON *prop_node = cJSON_GetObjectItem (callee, "right");
const char *prop = cJSON_GetStringValue (prop_node);
int obj_slot = mach_gen_expr (s, obj_node);
mach_emit_go_call_method (s, obj_slot, prop, arg_slots);
} else {
/* Regular tail call: go func(args) */
int func_slot = mach_gen_expr (s, callee);
mach_emit_go_call (s, func_slot, arg_slots);
}
cJSON_Delete (arg_slots);
return;
}
/* Throw statement */
if (strcmp (kind, "throw") == 0) {
cJSON *expr = cJSON_GetObjectItem (stmt, "expression");
@@ -30445,6 +30579,11 @@ static cJSON *mach_gen_function (MachGenState *parent, cJSON *func_node) {
mach_scan_vars (&s, stmt);
}
/* Scan for inner function definitions (for 'go' validation) */
cJSON_ArrayForEach (stmt, stmts) {
mach_scan_inner_functions (&s, stmt);
}
/* Adjust temp slot start after all locals are allocated */
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;