add go
This commit is contained in:
139
source/quickjs.c
139
source/quickjs.c
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user