From c3dc27eac6c54f5785d5f12d961d44a32121422c Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Wed, 4 Feb 2026 23:45:51 -0600 Subject: [PATCH] machine code --- source/cell.c | 68 +++ source/quickjs.c | 1229 ++++++++++++++++++++++++++++++++++++++++++++++ source/quickjs.h | 4 + 3 files changed, 1301 insertions(+) diff --git a/source/cell.c b/source/cell.c index 0b3a7a91..ee35d204 100644 --- a/source/cell.c +++ b/source/cell.c @@ -397,6 +397,74 @@ int cell_init(int argc, char **argv) return json ? 0 : 1; } + /* Check for --mach flag to output machine code JSON */ + if (argc >= 3 && strcmp(argv[1], "--mach") == 0) { + const char *script_or_file = argv[2]; + char *script = NULL; + char *allocated_script = NULL; + const char *filename = ""; + + struct stat st; + if (stat(script_or_file, &st) == 0 && S_ISREG(st.st_mode)) { + FILE *f = fopen(script_or_file, "r"); + if (!f) { + printf("Failed to open file: %s\n", script_or_file); + return 1; + } + allocated_script = malloc(st.st_size + 1); + if (!allocated_script) { + fclose(f); + printf("Failed to allocate memory for script\n"); + return 1; + } + size_t read_size = fread(allocated_script, 1, st.st_size, f); + fclose(f); + allocated_script[read_size] = '\0'; + script = allocated_script; + filename = script_or_file; + } else { + script = (char *)script_or_file; + } + + JSRuntime *rt = JS_NewRuntime(); + if (!rt) { + printf("Failed to create JS runtime\n"); + free(allocated_script); + return 1; + } + JSContext *ctx = JS_NewContext(rt); + if (!ctx) { + printf("Failed to create JS context\n"); + JS_FreeRuntime(rt); + free(allocated_script); + return 1; + } + + char *ast_json = JS_AST(ctx, script, strlen(script), filename); + if (!ast_json) { + printf("Failed to parse AST\n"); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + free(allocated_script); + return 1; + } + + char *mach_json = JS_Mach(ctx, ast_json); + free(ast_json); + + if (mach_json) { + printf("%s\n", mach_json); + free(mach_json); + } else { + printf("Failed to generate machine code\n"); + } + + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + free(allocated_script); + return mach_json ? 0 : 1; + } + /* Check for -e or --eval flag to run immediate script */ /* Also check for -p flag to print bytecode */ /* -s / --serializers flag provides json, nota, wota in env */ diff --git a/source/quickjs.c b/source/quickjs.c index d2b65aa0..52e62a29 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -28868,3 +28868,1232 @@ char *JS_AST (JSContext *ctx, const char *source, size_t len, const char *filena return json; } + +/* ============================================================ + Register-Based Machine Code Generator + ============================================================ */ + +typedef struct MachGenState { + JSContext *ctx; + cJSON *instructions; + cJSON *data; + cJSON *functions; + int next_slot; + int max_slot; + int nr_args; + int label_counter; + int func_counter; + struct MachGenState *parent; + /* Loop labels for break/continue */ + const char *loop_break; + const char *loop_continue; +} MachGenState; + +static int mach_gen_expr (MachGenState *s, cJSON *expr); +static void mach_gen_statement (MachGenState *s, cJSON *stmt); + +static int mach_alloc_slot (MachGenState *s) { + int slot = s->next_slot++; + if (slot > s->max_slot) s->max_slot = slot; + return slot; +} + +static char *mach_gen_label (MachGenState *s, const char *prefix) { + char *label = sys_malloc (64); + snprintf (label, 64, "%s_%d", prefix, s->label_counter++); + return label; +} + +static void mach_emit_label (MachGenState *s, const char *label) { + cJSON *item = cJSON_CreateString (label); + cJSON_AddItemToArray (s->instructions, item); +} + +static void mach_emit_0 (MachGenState *s, const char *op) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString (op)); + cJSON_AddItemToArray (s->instructions, instr); +} + +static void mach_emit_1 (MachGenState *s, const char *op, int a) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString (op)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (a)); + cJSON_AddItemToArray (s->instructions, instr); +} + +static void mach_emit_2 (MachGenState *s, const char *op, int a, int b) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString (op)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (a)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (b)); + cJSON_AddItemToArray (s->instructions, instr); +} + +static void mach_emit_3 (MachGenState *s, const char *op, int a, int b, int c) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString (op)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (a)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (b)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (c)); + cJSON_AddItemToArray (s->instructions, instr); +} + +static void mach_emit_const_num (MachGenState *s, int dest, double val) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("const")); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (val)); + cJSON_AddItemToArray (s->instructions, instr); +} + +static void mach_emit_const_str (MachGenState *s, int dest, const char *val) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("const")); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); + cJSON_AddItemToArray (instr, cJSON_CreateString (val)); + cJSON_AddItemToArray (s->instructions, instr); +} + +static void mach_emit_const_bool (MachGenState *s, int dest, int val) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("const")); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); + cJSON_AddItemToArray (instr, val ? cJSON_CreateTrue () : cJSON_CreateFalse ()); + cJSON_AddItemToArray (s->instructions, instr); +} + +static void mach_emit_const_null (MachGenState *s, int dest) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("const")); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); + cJSON_AddItemToArray (instr, cJSON_CreateNull ()); + cJSON_AddItemToArray (s->instructions, instr); +} + +static void mach_emit_jump (MachGenState *s, const char *label) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("jump")); + cJSON_AddItemToArray (instr, cJSON_CreateString (label)); + cJSON_AddItemToArray (s->instructions, instr); +} + +static void mach_emit_jump_cond (MachGenState *s, const char *op, int slot, const char *label) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString (op)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (slot)); + cJSON_AddItemToArray (instr, cJSON_CreateString (label)); + cJSON_AddItemToArray (s->instructions, instr); +} + +static void mach_emit_get_var (MachGenState *s, int dest, const char *name) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("get_var")); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); + cJSON_AddItemToArray (instr, cJSON_CreateString (name)); + cJSON_AddItemToArray (s->instructions, instr); +} + +static void mach_emit_set_var (MachGenState *s, const char *name, int slot) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("set_var")); + cJSON_AddItemToArray (instr, cJSON_CreateString (name)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (slot)); + cJSON_AddItemToArray (s->instructions, instr); +} + +static void mach_emit_get_prop (MachGenState *s, int dest, int obj, const char *prop) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("get_prop")); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj)); + cJSON_AddItemToArray (instr, cJSON_CreateString (prop)); + cJSON_AddItemToArray (s->instructions, instr); +} + +static void mach_emit_set_prop (MachGenState *s, int obj, const char *prop, int val) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("set_prop")); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj)); + cJSON_AddItemToArray (instr, cJSON_CreateString (prop)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (val)); + cJSON_AddItemToArray (s->instructions, instr); +} + +static void mach_emit_get_elem (MachGenState *s, int dest, int obj, int idx) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("get_elem")); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (idx)); + cJSON_AddItemToArray (s->instructions, instr); +} + +static void mach_emit_set_elem (MachGenState *s, int obj, int idx, int val) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("set_elem")); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (idx)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (val)); + cJSON_AddItemToArray (s->instructions, instr); +} + +static void mach_emit_call (MachGenState *s, int dest, int func_slot, cJSON *args) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("call")); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (func_slot)); + cJSON *arg; + cJSON_ArrayForEach (arg, args) { + cJSON_AddItemToArray (instr, cJSON_CreateNumber (arg->valueint)); + } + cJSON_AddItemToArray (s->instructions, instr); +} + +static void mach_emit_call_method (MachGenState *s, int dest, int obj, const char *prop, cJSON *args) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("call_method")); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj)); + cJSON_AddItemToArray (instr, cJSON_CreateString (prop)); + cJSON *arg; + cJSON_ArrayForEach (arg, args) { + cJSON_AddItemToArray (instr, cJSON_CreateNumber (arg->valueint)); + } + cJSON_AddItemToArray (s->instructions, instr); +} + +/* Generate code for binary operators */ +static int mach_gen_binary (MachGenState *s, cJSON *node) { + const char *kind = cJSON_GetStringValue (cJSON_GetObjectItem (node, "kind")); + cJSON *left = cJSON_GetObjectItem (node, "left"); + cJSON *right = cJSON_GetObjectItem (node, "right"); + + /* Handle short-circuit operators */ + if (strcmp (kind, "&&") == 0) { + char *end_label = mach_gen_label (s, "and_end"); + int left_slot = mach_gen_expr (s, left); + int dest = mach_alloc_slot (s); + mach_emit_2 (s, "move", dest, left_slot); + mach_emit_jump_cond (s, "jump_false", dest, end_label); + int right_slot = mach_gen_expr (s, right); + mach_emit_2 (s, "move", dest, right_slot); + mach_emit_label (s, end_label); + sys_free (end_label); + return dest; + } + + if (strcmp (kind, "||") == 0) { + char *end_label = mach_gen_label (s, "or_end"); + int left_slot = mach_gen_expr (s, left); + int dest = mach_alloc_slot (s); + mach_emit_2 (s, "move", dest, left_slot); + mach_emit_jump_cond (s, "jump_true", dest, end_label); + int right_slot = mach_gen_expr (s, right); + mach_emit_2 (s, "move", dest, right_slot); + mach_emit_label (s, end_label); + sys_free (end_label); + return dest; + } + + if (strcmp (kind, "??") == 0) { + char *end_label = mach_gen_label (s, "nullish_end"); + int left_slot = mach_gen_expr (s, left); + int dest = mach_alloc_slot (s); + mach_emit_2 (s, "move", dest, left_slot); + mach_emit_jump_cond (s, "jump_not_null", dest, end_label); + int right_slot = mach_gen_expr (s, right); + mach_emit_2 (s, "move", dest, right_slot); + mach_emit_label (s, end_label); + sys_free (end_label); + return dest; + } + + /* Regular binary operators */ + int left_slot = mach_gen_expr (s, left); + int right_slot = mach_gen_expr (s, right); + int dest = mach_alloc_slot (s); + + const char *op = NULL; + if (strcmp (kind, "+") == 0) op = "add"; + else if (strcmp (kind, "-") == 0) op = "sub"; + else if (strcmp (kind, "*") == 0) op = "mul"; + else if (strcmp (kind, "/") == 0) op = "div"; + else if (strcmp (kind, "%") == 0) op = "mod"; + else if (strcmp (kind, "**") == 0) op = "pow"; + else if (strcmp (kind, "&") == 0) op = "bitand"; + else if (strcmp (kind, "|") == 0) op = "bitor"; + else if (strcmp (kind, "^") == 0) op = "bitxor"; + else if (strcmp (kind, "<<") == 0) op = "shl"; + else if (strcmp (kind, ">>") == 0) op = "shr"; + else if (strcmp (kind, ">>>") == 0) op = "ushr"; + else if (strcmp (kind, "==") == 0 || strcmp (kind, "===") == 0) op = "eq"; + else if (strcmp (kind, "!=") == 0 || strcmp (kind, "!==") == 0) op = "ne"; + else if (strcmp (kind, "<") == 0) op = "lt"; + else if (strcmp (kind, "<=") == 0) op = "le"; + else if (strcmp (kind, ">") == 0) op = "gt"; + else if (strcmp (kind, ">=") == 0) op = "ge"; + else if (strcmp (kind, "in") == 0) op = "in"; + else op = kind; + + mach_emit_3 (s, op, dest, left_slot, right_slot); + return dest; +} + +/* Generate code for assignment with operator */ +static int mach_gen_compound_assign (MachGenState *s, cJSON *node, const char *op) { + cJSON *left = cJSON_GetObjectItem (node, "left"); + cJSON *right = cJSON_GetObjectItem (node, "right"); + + const char *left_kind = cJSON_GetStringValue (cJSON_GetObjectItem (left, "kind")); + + if (strcmp (left_kind, "name") == 0) { + const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (left, "name")); + int left_slot = mach_alloc_slot (s); + mach_emit_get_var (s, left_slot, name); + int right_slot = mach_gen_expr (s, right); + int dest = mach_alloc_slot (s); + mach_emit_3 (s, op, dest, left_slot, right_slot); + mach_emit_set_var (s, name, dest); + return dest; + } else if (strcmp (left_kind, ".") == 0) { + cJSON *obj = cJSON_GetObjectItem (left, "left"); + const char *prop = cJSON_GetStringValue (cJSON_GetObjectItem (left, "right")); + int obj_slot = mach_gen_expr (s, obj); + int old_val = mach_alloc_slot (s); + mach_emit_get_prop (s, old_val, obj_slot, prop); + int right_slot = mach_gen_expr (s, right); + int dest = mach_alloc_slot (s); + mach_emit_3 (s, op, dest, old_val, right_slot); + mach_emit_set_prop (s, obj_slot, prop, dest); + return dest; + } else if (strcmp (left_kind, "[") == 0) { + cJSON *obj = cJSON_GetObjectItem (left, "left"); + cJSON *idx_expr = cJSON_GetObjectItem (left, "right"); + int obj_slot = mach_gen_expr (s, obj); + int idx_slot = mach_gen_expr (s, idx_expr); + int old_val = mach_alloc_slot (s); + mach_emit_get_elem (s, old_val, obj_slot, idx_slot); + int right_slot = mach_gen_expr (s, right); + int dest = mach_alloc_slot (s); + mach_emit_3 (s, op, dest, old_val, right_slot); + mach_emit_set_elem (s, obj_slot, idx_slot, dest); + return dest; + } + + return -1; +} + +/* Generate code for assignment */ +static int mach_gen_assign (MachGenState *s, cJSON *node) { + const char *kind = cJSON_GetStringValue (cJSON_GetObjectItem (node, "kind")); + cJSON *left = cJSON_GetObjectItem (node, "left"); + cJSON *right = cJSON_GetObjectItem (node, "right"); + + /* Handle compound assignments */ + if (strcmp (kind, "+=") == 0) return mach_gen_compound_assign (s, node, "add"); + if (strcmp (kind, "-=") == 0) return mach_gen_compound_assign (s, node, "sub"); + if (strcmp (kind, "*=") == 0) return mach_gen_compound_assign (s, node, "mul"); + if (strcmp (kind, "/=") == 0) return mach_gen_compound_assign (s, node, "div"); + if (strcmp (kind, "%=") == 0) return mach_gen_compound_assign (s, node, "mod"); + if (strcmp (kind, "**=") == 0) return mach_gen_compound_assign (s, node, "pow"); + if (strcmp (kind, "&=") == 0) return mach_gen_compound_assign (s, node, "bitand"); + if (strcmp (kind, "|=") == 0) return mach_gen_compound_assign (s, node, "bitor"); + if (strcmp (kind, "^=") == 0) return mach_gen_compound_assign (s, node, "bitxor"); + if (strcmp (kind, "<<=") == 0) return mach_gen_compound_assign (s, node, "shl"); + if (strcmp (kind, ">>=") == 0) return mach_gen_compound_assign (s, node, "shr"); + if (strcmp (kind, ">>>=") == 0) return mach_gen_compound_assign (s, node, "ushr"); + + /* Simple assignment */ + int val_slot = mach_gen_expr (s, right); + + const char *left_kind = cJSON_GetStringValue (cJSON_GetObjectItem (left, "kind")); + + if (strcmp (left_kind, "name") == 0) { + const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (left, "name")); + mach_emit_set_var (s, name, val_slot); + } else if (strcmp (left_kind, ".") == 0) { + cJSON *obj = cJSON_GetObjectItem (left, "left"); + const char *prop = cJSON_GetStringValue (cJSON_GetObjectItem (left, "right")); + int obj_slot = mach_gen_expr (s, obj); + mach_emit_set_prop (s, obj_slot, prop, val_slot); + } else if (strcmp (left_kind, "[") == 0) { + cJSON *obj = cJSON_GetObjectItem (left, "left"); + cJSON *idx_expr = cJSON_GetObjectItem (left, "right"); + int obj_slot = mach_gen_expr (s, obj); + int idx_slot = mach_gen_expr (s, idx_expr); + mach_emit_set_elem (s, obj_slot, idx_slot, val_slot); + } + + return val_slot; +} + +/* Forward declaration for function generation */ +static cJSON *mach_gen_function (MachGenState *s, cJSON *func_node); + +/* Generate code for an expression, returns the slot containing the result */ +static int mach_gen_expr (MachGenState *s, cJSON *expr) { + if (!expr) return -1; + + const char *kind = cJSON_GetStringValue (cJSON_GetObjectItem (expr, "kind")); + if (!kind) return -1; + + /* Literals */ + if (strcmp (kind, "number") == 0) { + int slot = mach_alloc_slot (s); + double val = cJSON_GetNumberValue (cJSON_GetObjectItem (expr, "number")); + mach_emit_const_num (s, slot, val); + return slot; + } + + if (strcmp (kind, "text") == 0) { + int slot = mach_alloc_slot (s); + const char *val = cJSON_GetStringValue (cJSON_GetObjectItem (expr, "value")); + mach_emit_const_str (s, slot, val ? val : ""); + return slot; + } + + if (strcmp (kind, "true") == 0) { + int slot = mach_alloc_slot (s); + mach_emit_const_bool (s, slot, 1); + return slot; + } + + if (strcmp (kind, "false") == 0) { + int slot = mach_alloc_slot (s); + mach_emit_const_bool (s, slot, 0); + return slot; + } + + if (strcmp (kind, "null") == 0) { + int slot = mach_alloc_slot (s); + mach_emit_const_null (s, slot); + return slot; + } + + if (strcmp (kind, "this") == 0) { + int slot = mach_alloc_slot (s); + mach_emit_1 (s, "this", slot); + return slot; + } + + /* Variable reference */ + if (strcmp (kind, "name") == 0) { + const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (expr, "name")); + int slot = mach_alloc_slot (s); + mach_emit_get_var (s, slot, name); + return slot; + } + + /* Property access */ + if (strcmp (kind, ".") == 0) { + cJSON *obj = cJSON_GetObjectItem (expr, "left"); + const char *prop = cJSON_GetStringValue (cJSON_GetObjectItem (expr, "right")); + int obj_slot = mach_gen_expr (s, obj); + int slot = mach_alloc_slot (s); + mach_emit_get_prop (s, slot, obj_slot, prop); + return slot; + } + + /* Element access */ + if (strcmp (kind, "[") == 0) { + cJSON *obj = cJSON_GetObjectItem (expr, "left"); + cJSON *idx = cJSON_GetObjectItem (expr, "right"); + int obj_slot = mach_gen_expr (s, obj); + int idx_slot = mach_gen_expr (s, idx); + int slot = mach_alloc_slot (s); + mach_emit_get_elem (s, slot, obj_slot, idx_slot); + return slot; + } + + /* Function call */ + if (strcmp (kind, "(") == 0) { + cJSON *callee = cJSON_GetObjectItem (expr, "expression"); + cJSON *args_list = cJSON_GetObjectItem (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")); + + int dest = mach_alloc_slot (s); + + if (strcmp (callee_kind, ".") == 0) { + /* Method call */ + cJSON *obj = cJSON_GetObjectItem (callee, "left"); + const char *prop = cJSON_GetStringValue (cJSON_GetObjectItem (callee, "right")); + int obj_slot = mach_gen_expr (s, obj); + mach_emit_call_method (s, dest, obj_slot, prop, arg_slots); + } else { + /* Regular call */ + int func_slot = mach_gen_expr (s, callee); + mach_emit_call (s, dest, func_slot, arg_slots); + } + + cJSON_Delete (arg_slots); + return dest; + } + + /* Unary operators */ + if (strcmp (kind, "!") == 0) { + cJSON *operand = cJSON_GetObjectItem (expr, "expression"); + int operand_slot = mach_gen_expr (s, operand); + int slot = mach_alloc_slot (s); + mach_emit_2 (s, "not", slot, operand_slot); + return slot; + } + + if (strcmp (kind, "~") == 0) { + cJSON *operand = cJSON_GetObjectItem (expr, "expression"); + int operand_slot = mach_gen_expr (s, operand); + int slot = mach_alloc_slot (s); + mach_emit_2 (s, "bitnot", slot, operand_slot); + return slot; + } + + if (strcmp (kind, "-unary") == 0) { + cJSON *operand = cJSON_GetObjectItem (expr, "expression"); + int operand_slot = mach_gen_expr (s, operand); + int slot = mach_alloc_slot (s); + mach_emit_2 (s, "neg", slot, operand_slot); + return slot; + } + + if (strcmp (kind, "+unary") == 0) { + /* Unary + is essentially a no-op for numbers */ + cJSON *operand = cJSON_GetObjectItem (expr, "expression"); + return mach_gen_expr (s, operand); + } + + /* Increment/Decrement */ + if (strcmp (kind, "++") == 0 || strcmp (kind, "--") == 0) { + cJSON *operand = cJSON_GetObjectItem (expr, "expression"); + cJSON *is_postfix = cJSON_GetObjectItem (expr, "postfix"); + int postfix = is_postfix && cJSON_IsTrue (is_postfix); + const char *op = (strcmp (kind, "++") == 0) ? "inc" : "dec"; + + const char *operand_kind = cJSON_GetStringValue (cJSON_GetObjectItem (operand, "kind")); + + if (strcmp (operand_kind, "name") == 0) { + const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (operand, "name")); + int old_slot = mach_alloc_slot (s); + mach_emit_get_var (s, old_slot, name); + int new_slot = mach_alloc_slot (s); + mach_emit_2 (s, op, new_slot, old_slot); + mach_emit_set_var (s, name, new_slot); + return postfix ? old_slot : new_slot; + } else if (strcmp (operand_kind, ".") == 0) { + cJSON *obj = cJSON_GetObjectItem (operand, "left"); + const char *prop = cJSON_GetStringValue (cJSON_GetObjectItem (operand, "right")); + int obj_slot = mach_gen_expr (s, obj); + int old_slot = mach_alloc_slot (s); + mach_emit_get_prop (s, old_slot, obj_slot, prop); + int new_slot = mach_alloc_slot (s); + mach_emit_2 (s, op, new_slot, old_slot); + mach_emit_set_prop (s, obj_slot, prop, new_slot); + return postfix ? old_slot : new_slot; + } else if (strcmp (operand_kind, "[") == 0) { + cJSON *obj = cJSON_GetObjectItem (operand, "left"); + cJSON *idx_expr = cJSON_GetObjectItem (operand, "right"); + int obj_slot = mach_gen_expr (s, obj); + int idx_slot = mach_gen_expr (s, idx_expr); + int old_slot = mach_alloc_slot (s); + mach_emit_get_elem (s, old_slot, obj_slot, idx_slot); + int new_slot = mach_alloc_slot (s); + mach_emit_2 (s, op, new_slot, old_slot); + mach_emit_set_elem (s, obj_slot, idx_slot, new_slot); + return postfix ? old_slot : new_slot; + } + } + + /* Delete operator */ + if (strcmp (kind, "delete") == 0) { + cJSON *operand = cJSON_GetObjectItem (expr, "expression"); + const char *operand_kind = cJSON_GetStringValue (cJSON_GetObjectItem (operand, "kind")); + int slot = mach_alloc_slot (s); + + if (strcmp (operand_kind, ".") == 0) { + cJSON *obj = cJSON_GetObjectItem (operand, "left"); + const char *prop = cJSON_GetStringValue (cJSON_GetObjectItem (operand, "right")); + int obj_slot = mach_gen_expr (s, obj); + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("delete_prop")); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (slot)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj_slot)); + cJSON_AddItemToArray (instr, cJSON_CreateString (prop)); + cJSON_AddItemToArray (s->instructions, instr); + } else if (strcmp (operand_kind, "[") == 0) { + cJSON *obj = cJSON_GetObjectItem (operand, "left"); + cJSON *idx = cJSON_GetObjectItem (operand, "right"); + int obj_slot = mach_gen_expr (s, obj); + int idx_slot = mach_gen_expr (s, idx); + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("delete_elem")); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (slot)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj_slot)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (idx_slot)); + cJSON_AddItemToArray (s->instructions, instr); + } else { + mach_emit_const_bool (s, slot, 1); + } + return slot; + } + + /* Void operator */ + if (strcmp (kind, "void") == 0) { + cJSON *operand = cJSON_GetObjectItem (expr, "expression"); + mach_gen_expr (s, operand); + int slot = mach_alloc_slot (s); + mach_emit_const_null (s, slot); + return slot; + } + + /* Ternary */ + if (strcmp (kind, "then") == 0) { + cJSON *cond = cJSON_GetObjectItem (expr, "expression"); + cJSON *then_expr = cJSON_GetObjectItem (expr, "then"); + cJSON *else_expr = cJSON_GetObjectItem (expr, "else"); + + char *else_label = mach_gen_label (s, "tern_else"); + char *end_label = mach_gen_label (s, "tern_end"); + + int cond_slot = mach_gen_expr (s, cond); + mach_emit_jump_cond (s, "jump_false", cond_slot, else_label); + + int dest = mach_alloc_slot (s); + int then_slot = mach_gen_expr (s, then_expr); + mach_emit_2 (s, "move", dest, then_slot); + mach_emit_jump (s, end_label); + + mach_emit_label (s, else_label); + int else_slot = mach_gen_expr (s, else_expr); + mach_emit_2 (s, "move", dest, else_slot); + + mach_emit_label (s, end_label); + + sys_free (else_label); + sys_free (end_label); + return dest; + } + + /* Array literal */ + if (strcmp (kind, "array") == 0) { + cJSON *list = cJSON_GetObjectItem (expr, "list"); + int count = cJSON_GetArraySize (list); + + /* Compile each element */ + cJSON *elem_slots = cJSON_CreateArray (); + cJSON *elem; + cJSON_ArrayForEach (elem, list) { + int slot = mach_gen_expr (s, elem); + cJSON_AddItemToArray (elem_slots, cJSON_CreateNumber (slot)); + } + + int dest = mach_alloc_slot (s); + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("array")); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (count)); + cJSON *el; + cJSON_ArrayForEach (el, elem_slots) { + cJSON_AddItemToArray (instr, cJSON_CreateNumber (el->valueint)); + } + cJSON_AddItemToArray (s->instructions, instr); + cJSON_Delete (elem_slots); + return dest; + } + + /* Object literal */ + if (strcmp (kind, "record") == 0) { + cJSON *list = cJSON_GetObjectItem (expr, "list"); + int count = cJSON_GetArraySize (list); + + int dest = mach_alloc_slot (s); + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("record")); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (count)); + + cJSON *pair; + cJSON_ArrayForEach (pair, list) { + cJSON *key = cJSON_GetObjectItem (pair, "left"); + cJSON *val = cJSON_GetObjectItem (pair, "right"); + + const char *key_kind = cJSON_GetStringValue (cJSON_GetObjectItem (key, "kind")); + if (key_kind && strcmp (key_kind, "name") == 0) { + const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (key, "name")); + cJSON_AddItemToArray (instr, cJSON_CreateString (name)); + } else if (key_kind && strcmp (key_kind, "text") == 0) { + const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (key, "value")); + cJSON_AddItemToArray (instr, cJSON_CreateString (name ? name : "")); + } else { + /* Computed property - emit the key slot */ + int key_slot = mach_gen_expr (s, key); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (key_slot)); + } + + int val_slot = mach_gen_expr (s, val); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (val_slot)); + } + cJSON_AddItemToArray (s->instructions, instr); + return dest; + } + + /* Function expression */ + if (strcmp (kind, "function") == 0) { + int func_id = s->func_counter++; + cJSON *func = mach_gen_function (s, expr); + cJSON_AddItemToArray (s->functions, func); + + int dest = mach_alloc_slot (s); + mach_emit_2 (s, "function", dest, func_id); + return dest; + } + + /* New expression */ + if (strcmp (kind, "new") == 0) { + cJSON *callee = cJSON_GetObjectItem (expr, "expression"); + cJSON *args_list = cJSON_GetObjectItem (expr, "list"); + + int func_slot = mach_gen_expr (s, callee); + + cJSON *arg_slots = cJSON_CreateArray (); + if (args_list) { + cJSON *arg; + cJSON_ArrayForEach (arg, args_list) { + int slot = mach_gen_expr (s, arg); + cJSON_AddItemToArray (arg_slots, cJSON_CreateNumber (slot)); + } + } + + int dest = mach_alloc_slot (s); + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("new")); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (func_slot)); + cJSON *el; + cJSON_ArrayForEach (el, arg_slots) { + cJSON_AddItemToArray (instr, cJSON_CreateNumber (el->valueint)); + } + cJSON_AddItemToArray (s->instructions, instr); + cJSON_Delete (arg_slots); + return dest; + } + + /* Assignment operators */ + if (strcmp (kind, "assign") == 0 || strstr (kind, "=") != NULL) { + return mach_gen_assign (s, expr); + } + + /* Binary operators */ + return mach_gen_binary (s, expr); +} + +/* Generate code for a statement */ +static void mach_gen_statement (MachGenState *s, cJSON *stmt) { + if (!stmt) return; + + const char *kind = cJSON_GetStringValue (cJSON_GetObjectItem (stmt, "kind")); + if (!kind) return; + + /* Variable declaration */ + if (strcmp (kind, "var") == 0 || strcmp (kind, "def") == 0) { + cJSON *left = cJSON_GetObjectItem (stmt, "left"); + cJSON *right = cJSON_GetObjectItem (stmt, "right"); + const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (left, "name")); + + if (right) { + int val_slot = mach_gen_expr (s, right); + mach_emit_set_var (s, name, val_slot); + } else { + int slot = mach_alloc_slot (s); + mach_emit_const_null (s, slot); + mach_emit_set_var (s, name, slot); + } + return; + } + + /* Block statement */ + if (strcmp (kind, "block") == 0) { + cJSON *stmts = cJSON_GetObjectItem (stmt, "statements"); + cJSON *child; + cJSON_ArrayForEach (child, stmts) { + mach_gen_statement (s, child); + } + return; + } + + /* If statement */ + if (strcmp (kind, "if") == 0) { + cJSON *cond = cJSON_GetObjectItem (stmt, "expression"); + cJSON *then_stmts = cJSON_GetObjectItem (stmt, "then"); + cJSON *else_stmts = cJSON_GetObjectItem (stmt, "else"); + + char *else_label = mach_gen_label (s, "if_else"); + char *end_label = mach_gen_label (s, "if_end"); + + int cond_slot = mach_gen_expr (s, cond); + mach_emit_jump_cond (s, "jump_false", cond_slot, else_label); + + cJSON *child; + cJSON_ArrayForEach (child, then_stmts) { + mach_gen_statement (s, child); + } + mach_emit_jump (s, end_label); + + mach_emit_label (s, else_label); + if (else_stmts) { + cJSON_ArrayForEach (child, else_stmts) { + mach_gen_statement (s, child); + } + } + + mach_emit_label (s, end_label); + sys_free (else_label); + sys_free (end_label); + return; + } + + /* While loop */ + if (strcmp (kind, "while") == 0) { + cJSON *cond = cJSON_GetObjectItem (stmt, "expression"); + cJSON *stmts = cJSON_GetObjectItem (stmt, "statements"); + + char *start_label = mach_gen_label (s, "while_start"); + char *end_label = mach_gen_label (s, "while_end"); + + const char *old_break = s->loop_break; + const char *old_continue = s->loop_continue; + s->loop_break = end_label; + s->loop_continue = start_label; + + mach_emit_label (s, start_label); + int cond_slot = mach_gen_expr (s, cond); + mach_emit_jump_cond (s, "jump_false", cond_slot, end_label); + + cJSON *child; + cJSON_ArrayForEach (child, stmts) { + mach_gen_statement (s, child); + } + mach_emit_jump (s, start_label); + + mach_emit_label (s, end_label); + + s->loop_break = old_break; + s->loop_continue = old_continue; + sys_free (start_label); + sys_free (end_label); + return; + } + + /* Do-while loop */ + if (strcmp (kind, "do") == 0) { + cJSON *cond = cJSON_GetObjectItem (stmt, "expression"); + cJSON *stmts = cJSON_GetObjectItem (stmt, "statements"); + + char *start_label = mach_gen_label (s, "do_start"); + char *cond_label = mach_gen_label (s, "do_cond"); + char *end_label = mach_gen_label (s, "do_end"); + + const char *old_break = s->loop_break; + const char *old_continue = s->loop_continue; + s->loop_break = end_label; + s->loop_continue = cond_label; + + mach_emit_label (s, start_label); + cJSON *child; + cJSON_ArrayForEach (child, stmts) { + mach_gen_statement (s, child); + } + + mach_emit_label (s, cond_label); + int cond_slot = mach_gen_expr (s, cond); + mach_emit_jump_cond (s, "jump_true", cond_slot, start_label); + + mach_emit_label (s, end_label); + + s->loop_break = old_break; + s->loop_continue = old_continue; + sys_free (start_label); + sys_free (cond_label); + sys_free (end_label); + return; + } + + /* For loop */ + if (strcmp (kind, "for") == 0) { + cJSON *init = cJSON_GetObjectItem (stmt, "init"); + cJSON *test = cJSON_GetObjectItem (stmt, "test"); + cJSON *update = cJSON_GetObjectItem (stmt, "update"); + cJSON *stmts = cJSON_GetObjectItem (stmt, "statements"); + + char *start_label = mach_gen_label (s, "for_start"); + char *update_label = mach_gen_label (s, "for_update"); + char *end_label = mach_gen_label (s, "for_end"); + + const char *old_break = s->loop_break; + const char *old_continue = s->loop_continue; + s->loop_break = end_label; + s->loop_continue = update_label; + + /* Init */ + if (init) { + const char *init_kind = cJSON_GetStringValue (cJSON_GetObjectItem (init, "kind")); + if (init_kind && (strcmp (init_kind, "var") == 0 || strcmp (init_kind, "def") == 0)) { + mach_gen_statement (s, init); + } else { + mach_gen_expr (s, init); + } + } + + mach_emit_label (s, start_label); + + /* Test */ + if (test) { + int test_slot = mach_gen_expr (s, test); + mach_emit_jump_cond (s, "jump_false", test_slot, end_label); + } + + /* Body */ + cJSON *child; + cJSON_ArrayForEach (child, stmts) { + mach_gen_statement (s, child); + } + + mach_emit_label (s, update_label); + + /* Update */ + if (update) { + mach_gen_expr (s, update); + } + mach_emit_jump (s, start_label); + + mach_emit_label (s, end_label); + + s->loop_break = old_break; + s->loop_continue = old_continue; + sys_free (start_label); + sys_free (update_label); + sys_free (end_label); + return; + } + + /* Return statement */ + if (strcmp (kind, "return") == 0) { + cJSON *expr = cJSON_GetObjectItem (stmt, "expression"); + if (expr) { + int slot = mach_gen_expr (s, expr); + mach_emit_1 (s, "return", slot); + } else { + mach_emit_0 (s, "return_undef"); + } + return; + } + + /* Throw statement */ + if (strcmp (kind, "throw") == 0) { + cJSON *expr = cJSON_GetObjectItem (stmt, "expression"); + int slot = mach_gen_expr (s, expr); + mach_emit_1 (s, "throw", slot); + return; + } + + /* Break statement */ + if (strcmp (kind, "break") == 0) { + if (s->loop_break) { + mach_emit_jump (s, s->loop_break); + } + return; + } + + /* Continue statement */ + if (strcmp (kind, "continue") == 0) { + if (s->loop_continue) { + mach_emit_jump (s, s->loop_continue); + } + return; + } + + /* Switch statement */ + if (strcmp (kind, "switch") == 0) { + cJSON *expr = cJSON_GetObjectItem (stmt, "expression"); + cJSON *cases = cJSON_GetObjectItem (stmt, "cases"); + + int switch_val = mach_gen_expr (s, expr); + char *end_label = mach_gen_label (s, "switch_end"); + char *default_label = NULL; + + const char *old_break = s->loop_break; + s->loop_break = end_label; + + /* First pass: generate case tests and jumps */ + cJSON *case_node; + int case_num = 0; + cJSON_ArrayForEach (case_node, cases) { + const char *case_kind = cJSON_GetStringValue (cJSON_GetObjectItem (case_node, "kind")); + if (strcmp (case_kind, "default") == 0) { + default_label = mach_gen_label (s, "switch_default"); + } else { + char *case_label = mach_gen_label (s, "switch_case"); + cJSON *case_expr = cJSON_GetObjectItem (case_node, "expression"); + int case_val = mach_gen_expr (s, case_expr); + int cmp_slot = mach_alloc_slot (s); + mach_emit_3 (s, "eq", cmp_slot, switch_val, case_val); + mach_emit_jump_cond (s, "jump_true", cmp_slot, case_label); + /* Store label in case node for second pass */ + cJSON_AddStringToObject (case_node, "_label", case_label); + sys_free (case_label); + } + case_num++; + } + + /* Jump to default or end */ + if (default_label) { + mach_emit_jump (s, default_label); + } else { + mach_emit_jump (s, end_label); + } + + /* Second pass: generate case bodies */ + cJSON_ArrayForEach (case_node, cases) { + const char *case_kind = cJSON_GetStringValue (cJSON_GetObjectItem (case_node, "kind")); + if (strcmp (case_kind, "default") == 0) { + mach_emit_label (s, default_label); + sys_free (default_label); + } else { + const char *label = cJSON_GetStringValue (cJSON_GetObjectItem (case_node, "_label")); + mach_emit_label (s, label); + } + cJSON *case_stmts = cJSON_GetObjectItem (case_node, "statements"); + cJSON *child; + cJSON_ArrayForEach (child, case_stmts) { + mach_gen_statement (s, child); + } + } + + mach_emit_label (s, end_label); + s->loop_break = old_break; + sys_free (end_label); + return; + } + + /* Try-catch-finally */ + if (strcmp (kind, "try") == 0) { + cJSON *try_stmts = cJSON_GetObjectItem (stmt, "statements"); + cJSON *catch_node = cJSON_GetObjectItem (stmt, "catch"); + cJSON *finally_node = cJSON_GetObjectItem (stmt, "finally"); + + char *catch_label = catch_node ? mach_gen_label (s, "catch") : NULL; + char *finally_label = finally_node ? mach_gen_label (s, "finally") : NULL; + char *end_label = mach_gen_label (s, "try_end"); + + /* try_begin */ + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("try_begin")); + cJSON_AddItemToArray (instr, cJSON_CreateString (catch_label ? catch_label : end_label)); + cJSON_AddItemToArray (instr, cJSON_CreateString (finally_label ? finally_label : end_label)); + cJSON_AddItemToArray (s->instructions, instr); + + /* Try body */ + cJSON *child; + cJSON_ArrayForEach (child, try_stmts) { + mach_gen_statement (s, child); + } + mach_emit_0 (s, "try_end"); + + /* Skip catch if no exception */ + if (catch_node) { + mach_emit_jump (s, finally_label ? finally_label : end_label); + } + + /* Catch block */ + if (catch_node) { + mach_emit_label (s, catch_label); + const char *err_name = cJSON_GetStringValue (cJSON_GetObjectItem (catch_node, "name")); + int err_slot = mach_alloc_slot (s); + mach_emit_1 (s, "catch_begin", err_slot); + if (err_name) { + mach_emit_set_var (s, err_name, err_slot); + } + cJSON *catch_stmts = cJSON_GetObjectItem (catch_node, "statements"); + cJSON_ArrayForEach (child, catch_stmts) { + mach_gen_statement (s, child); + } + mach_emit_0 (s, "catch_end"); + sys_free (catch_label); + } + + /* Finally block */ + if (finally_node) { + mach_emit_label (s, finally_label); + mach_emit_0 (s, "finally_begin"); + cJSON *finally_stmts = cJSON_GetObjectItem (finally_node, "statements"); + cJSON_ArrayForEach (child, finally_stmts) { + mach_gen_statement (s, child); + } + mach_emit_0 (s, "finally_end"); + sys_free (finally_label); + } + + mach_emit_label (s, end_label); + sys_free (end_label); + return; + } + + /* Function declaration (in statement context, add to functions) */ + if (strcmp (kind, "function") == 0) { + cJSON *name_obj = cJSON_GetObjectItem (stmt, "name"); + if (name_obj && cJSON_IsString (name_obj)) { + const char *name = cJSON_GetStringValue (name_obj); + int func_id = s->func_counter++; + cJSON *func = mach_gen_function (s, stmt); + cJSON_AddItemToArray (s->functions, func); + + int dest = mach_alloc_slot (s); + mach_emit_2 (s, "function", dest, func_id); + mach_emit_set_var (s, name, dest); + } + return; + } + + /* Expression statement (call) */ + if (strcmp (kind, "call") == 0) { + cJSON *expr = cJSON_GetObjectItem (stmt, "expression"); + mach_gen_expr (s, expr); + return; + } + + /* Any other expression used as statement */ + mach_gen_expr (s, stmt); +} + +/* Generate code for a function */ +static cJSON *mach_gen_function (MachGenState *parent, cJSON *func_node) { + MachGenState s = {0}; + s.ctx = parent->ctx; + s.instructions = cJSON_CreateArray (); + s.data = parent->data; + s.functions = parent->functions; + s.parent = parent; + s.label_counter = parent->label_counter; + s.func_counter = parent->func_counter; + + cJSON *result = cJSON_CreateObject (); + + /* Function name */ + cJSON *name_obj = cJSON_GetObjectItem (func_node, "name"); + if (name_obj && cJSON_IsString (name_obj)) { + cJSON_AddStringToObject (result, "name", cJSON_GetStringValue (name_obj)); + } else { + cJSON_AddStringToObject (result, "name", ""); + } + + /* Parameters - allocate slots 0..nr_args-1 */ + cJSON *params = cJSON_GetObjectItem (func_node, "list"); + s.nr_args = cJSON_GetArraySize (params); + s.next_slot = s.nr_args; + s.max_slot = s.nr_args; + + cJSON_AddNumberToObject (result, "nr_args", s.nr_args); + + cJSON *vars = cJSON_AddArrayToObject (result, "vars"); + cJSON *param; + cJSON_ArrayForEach (param, params) { + const char *param_name = cJSON_GetStringValue (cJSON_GetObjectItem (param, "name")); + if (param_name) { + cJSON_AddItemToArray (vars, cJSON_CreateString (param_name)); + } + } + + /* Generate body */ + cJSON *stmts = cJSON_GetObjectItem (func_node, "statements"); + cJSON *stmt; + cJSON_ArrayForEach (stmt, stmts) { + mach_gen_statement (&s, stmt); + } + + /* Add implicit return null */ + mach_emit_0 (&s, "return_undef"); + + cJSON_AddNumberToObject (result, "nr_slots", s.max_slot + 1); + cJSON_AddItemToObject (result, "labels", cJSON_CreateObject ()); + cJSON_AddItemToObject (result, "instructions", s.instructions); + + /* Propagate counters back */ + parent->label_counter = s.label_counter; + parent->func_counter = s.func_counter; + + return result; +} + +/* Generate machine code for the main program */ +static cJSON *mach_gen_program (MachGenState *s, cJSON *ast) { + cJSON *result = cJSON_CreateObject (); + + /* Filename */ + const char *filename = cJSON_GetStringValue (cJSON_GetObjectItem (ast, "filename")); + cJSON_AddStringToObject (result, "name", filename ? filename : ""); + + /* Data section (empty for now) */ + s->data = cJSON_AddObjectToObject (result, "data"); + + /* Functions array */ + s->functions = cJSON_AddArrayToObject (result, "functions"); + + /* Process top-level function declarations first */ + cJSON *functions = cJSON_GetObjectItem (ast, "functions"); + cJSON *func_node; + cJSON_ArrayForEach (func_node, functions) { + cJSON *name_obj = cJSON_GetObjectItem (func_node, "name"); + if (name_obj && cJSON_IsString (name_obj)) { + const char *name = cJSON_GetStringValue (name_obj); + int func_id = s->func_counter++; + cJSON *func = mach_gen_function (s, func_node); + cJSON_AddItemToArray (s->functions, func); + + int dest = mach_alloc_slot (s); + mach_emit_2 (s, "function", dest, func_id); + mach_emit_set_var (s, name, dest); + } + } + + /* Generate main code */ + cJSON *statements = cJSON_GetObjectItem (ast, "statements"); + cJSON *stmt; + cJSON_ArrayForEach (stmt, statements) { + mach_gen_statement (s, stmt); + } + + /* Main section */ + cJSON *main_obj = cJSON_CreateObject (); + cJSON_AddNumberToObject (main_obj, "nr_slots", s->max_slot + 1); + cJSON_AddItemToObject (main_obj, "vars", cJSON_CreateArray ()); + cJSON_AddItemToObject (main_obj, "labels", cJSON_CreateObject ()); + cJSON_AddItemToObject (main_obj, "instructions", s->instructions); + cJSON_AddItemToObject (result, "main", main_obj); + + return result; +} + +char *JS_Mach (JSContext *ctx, const char *ast_json) { + cJSON *ast = cJSON_Parse (ast_json); + if (!ast) return NULL; + + MachGenState s = {0}; + s.ctx = ctx; + s.instructions = cJSON_CreateArray (); + + cJSON *mach = mach_gen_program (&s, ast); + cJSON_Delete (ast); + + if (!mach) return NULL; + + char *json = cJSON_Print (mach); + cJSON_Delete (mach); + return json; +} diff --git a/source/quickjs.h b/source/quickjs.h index 4d87be9c..fea65f4b 100644 --- a/source/quickjs.h +++ b/source/quickjs.h @@ -1222,6 +1222,10 @@ CellModule *JS_CompileModule (JSContext *ctx, const char *input, size_t input_le Returns malloc'd JSON string (caller must free), or NULL on error. */ char *JS_AST (JSContext *ctx, const char *source, size_t len, const char *filename); +/* Generate register-based machine code from AST JSON. + Returns malloc'd JSON string (caller must free), or NULL on error. */ +char *JS_Mach (JSContext *ctx, const char *ast_json); + /* Integrate a CellModule with an environment and execute. Returns callable function value, or JS_EXCEPTION on error. */ JSValue cell_module_integrate (JSContext *ctx, CellModule *mod, JSValue env);