Merge branch 'mach' into pitweb

This commit is contained in:
2026-02-09 11:15:44 -06:00
11 changed files with 222 additions and 361 deletions

40
internal/bootstrap.cm Normal file
View File

@@ -0,0 +1,40 @@
// Hidden vars (os, program) come from env
var load_internal = os.load_internal
function use_embed(name) {
return load_internal("js_" + name + "_use")
}
var fd = use_embed('fd')
var use_cache = {}
use_cache['fd'] = fd
use_cache['os'] = os
function use(path) {
if (use_cache[path])
return use_cache[path];
var file_path = path + '.cm'
var script = null
var result = null
var exports = null
if (fd.is_file(file_path)) {
script = text(fd.slurp(file_path))
exports = {}
mach_eval(path, script, {use: use, exports: exports})
use_cache[path] = exports
return exports
}
// Try embedded C module
result = use_embed(replace(path, '/', '_'))
use_cache[path] = result
return result
}
// Load and run the user's program
var blob = fd.slurp(program)
stone(blob)
var script = text(blob)
mach_eval(program, script, {use: use})

View File

@@ -578,7 +578,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
JSValue js_os_use(JSContext *js) {
JS_NewClassID(&js_dylib_class_id);
JS_NewClass(js, js_dylib_class_id, &js_dylib_class);
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,js_os_funcs,countof(js_os_funcs));
return mod;

View File

@@ -584,59 +584,54 @@ int cell_init(int argc, char **argv)
/* Check for --run-mcode flag to execute via MCODE interpreter */
if (argc >= 3 && strcmp(argv[1], "--run-mcode") == 0) {
const char *script_or_file = argv[2];
char *script = NULL;
char *allocated_script = NULL;
const char *filename = "<eval>";
const char *filename = argv[2];
if (!find_cell_shop()) return 1;
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\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;
}
cJSON *ast = JS_ASTTree(script, strlen(script), filename);
if (!ast) {
printf("Failed to parse AST\n");
free(allocated_script);
size_t boot_size;
char *boot_data = load_core_file("internal/bootstrap.cm", &boot_size);
if (!boot_data) {
printf("ERROR: Could not load internal/bootstrap.cm from %s\n", core_path);
return 1;
}
if (print_tree_errors(ast)) {
cJSON_Delete(ast); free(allocated_script);
cJSON *boot_ast = JS_ASTTree(boot_data, boot_size, "internal/bootstrap.cm");
free(boot_data);
if (!boot_ast) {
printf("Failed to parse internal/bootstrap.cm\n");
return 1;
}
cJSON *mcode = JS_McodeTree(ast);
cJSON_Delete(ast);
if (print_tree_errors(boot_ast)) {
cJSON_Delete(boot_ast);
return 1;
}
cJSON *mcode = JS_McodeTree(boot_ast);
cJSON_Delete(boot_ast);
if (!mcode) {
printf("Failed to generate MCODE\n");
free(allocated_script);
return 1;
}
if (print_tree_errors(mcode)) {
cJSON_Delete(mcode); free(allocated_script);
cJSON_Delete(mcode);
return 1;
}
/* Use a larger heap context for execution */
JSRuntime *rt = JS_NewRuntime();
if (!rt) { printf("Failed to create JS runtime\n"); cJSON_Delete(mcode); free(allocated_script); return 1; }
JSContext *ctx = JS_NewContextWithHeapSize(rt, 64 * 1024);
if (!ctx) { printf("Failed to create execution context\n"); cJSON_Delete(mcode); JS_FreeRuntime(rt); free(allocated_script); return 1; }
if (!rt) { printf("Failed to create JS runtime\n"); cJSON_Delete(mcode); return 1; }
JSContext *ctx = JS_NewContextWithHeapSize(rt, 256 * 1024);
if (!ctx) { printf("Failed to create execution context\n"); cJSON_Delete(mcode); JS_FreeRuntime(rt); return 1; }
JSValue result = JS_CallMcodeTree(ctx, mcode); /* takes ownership of mcode */
JS_FreeValue(ctx, js_blob_use(ctx));
JSValue hidden_env = JS_NewObject(ctx);
JS_SetPropertyStr(ctx, hidden_env, "os", js_os_use(ctx));
JS_SetPropertyStr(ctx, hidden_env, "program", JS_NewString(ctx, filename));
hidden_env = JS_Stone(ctx, hidden_env);
JSValue result = JS_CallMcodeTreeEnv(ctx, mcode, hidden_env);
if (JS_IsException(result)) {
JSValue exc = JS_GetException(ctx);
@@ -663,7 +658,6 @@ int cell_init(int argc, char **argv)
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return JS_IsException(result) ? 1 : 0;
}
@@ -733,61 +727,50 @@ int cell_init(int argc, char **argv)
/* Check for --mach-run flag to compile and run through MACH VM */
if (argc >= 3 && strcmp(argv[1], "--mach-run") == 0) {
const char *script_or_file = argv[2];
char *script = NULL;
char *allocated_script = NULL;
const char *filename = "<eval>";
const char *filename = argv[2];
if (!find_cell_shop()) return 1;
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;
}
cJSON *ast = JS_ASTTree(script, strlen(script), filename);
if (!ast) {
printf("Failed to parse AST\n");
free(allocated_script);
size_t boot_size;
char *boot_data = load_core_file("internal/bootstrap.cm", &boot_size);
if (!boot_data) {
printf("ERROR: Could not load internal/bootstrap.cm from %s\n", core_path);
return 1;
}
if (print_tree_errors(ast)) {
cJSON_Delete(ast);
free(allocated_script);
cJSON *boot_ast = JS_ASTTree(boot_data, boot_size, "internal/bootstrap.cm");
free(boot_data);
if (!boot_ast) {
printf("Failed to parse internal/bootstrap.cm\n");
return 1;
}
if (print_tree_errors(boot_ast)) {
cJSON_Delete(boot_ast);
return 1;
}
JSRuntime *rt = JS_NewRuntime();
if (!rt) {
printf("Failed to create JS runtime\n");
cJSON_Delete(ast); free(allocated_script);
cJSON_Delete(boot_ast);
return 1;
}
JSContext *ctx = JS_NewContext(rt);
JSContext *ctx = JS_NewContextWithHeapSize(rt, 256 * 1024);
if (!ctx) {
printf("Failed to create JS context\n");
cJSON_Delete(ast); JS_FreeRuntime(rt); free(allocated_script);
cJSON_Delete(boot_ast); JS_FreeRuntime(rt);
return 1;
}
JSValue result = JS_RunMachTree(ctx, ast, JS_NULL);
cJSON_Delete(ast);
JS_FreeValue(ctx, js_blob_use(ctx));
JSValue hidden_env = JS_NewObject(ctx);
JS_SetPropertyStr(ctx, hidden_env, "os", js_os_use(ctx));
JS_SetPropertyStr(ctx, hidden_env, "program", JS_NewString(ctx, filename));
hidden_env = JS_Stone(ctx, hidden_env);
JSValue result = JS_RunMachTree(ctx, boot_ast, hidden_env);
cJSON_Delete(boot_ast);
int exit_code = 0;
if (JS_IsException(result)) {
@@ -822,7 +805,6 @@ int cell_init(int argc, char **argv)
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return exit_code;
}

View File

@@ -93,9 +93,6 @@ typedef struct MachCompState {
int loop_break; /* instruction index to patch, or -1 */
int loop_continue; /* instruction index to patch, or -1 */
/* Scope depth for block scoping */
int scope_depth;
/* Parent for nested function compilation */
struct MachCompState *parent;
@@ -241,7 +238,6 @@ static void mach_add_var(MachCompState *cs, const char *name, int slot, int is_c
v->slot = slot;
v->is_const = is_const;
v->is_closure = 0;
v->scope_depth = cs->scope_depth;
}
/* Find a variable in the current scope */
@@ -1353,17 +1349,7 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
cJSON *right = cJSON_GetObjectItemCaseSensitive(stmt, "right");
const char *name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(left, "name"));
if (!name) return;
/* Check if var exists at current scope depth — if so, reuse it.
If it exists at a shallower depth, shadow it with a new slot. */
int slot = -1;
for (int i = cs->var_count - 1; i >= 0; i--) {
if (strcmp(cs->vars[i].name, name) == 0) {
if (cs->vars[i].scope_depth == cs->scope_depth) {
slot = cs->vars[i].slot; /* same scope — reuse */
}
break;
}
}
int slot = mach_find_var(cs, name);
if (slot < 0) {
slot = mach_reserve_reg(cs);
mach_add_var(cs, name, slot, strcmp(kind, "def") == 0);
@@ -1438,8 +1424,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
/* Block */
if (strcmp(kind, "block") == 0) {
int saved_var_count = cs->var_count;
cs->scope_depth++;
cJSON *stmts = cJSON_GetObjectItemCaseSensitive(stmt, "statements");
if (stmts && cJSON_IsArray(stmts)) {
int count = cJSON_GetArraySize(stmts);
@@ -1447,10 +1431,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
mach_compile_stmt(cs, cJSON_GetArrayItem(stmts, i));
}
}
cs->scope_depth--;
for (int i = saved_var_count; i < cs->var_count; i++)
sys_free(cs->vars[i].name);
cs->var_count = saved_var_count;
return;
}
@@ -1470,8 +1450,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
/* Compile then branch — "then" is a direct array of statements */
if (then_body) {
int saved_vc = cs->var_count;
cs->scope_depth++;
if (cJSON_IsArray(then_body)) {
int count = cJSON_GetArraySize(then_body);
for (int i = 0; i < count; i++)
@@ -1486,10 +1464,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
mach_compile_stmt(cs, then_body);
}
}
cs->scope_depth--;
for (int i = saved_vc; i < cs->var_count; i++)
sys_free(cs->vars[i].name);
cs->var_count = saved_vc;
}
/* Check for else-if chain ("list") or plain else */
@@ -1508,8 +1482,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
cs->code[jmpfalse_pc] = MACH_AsBx(MACH_JMPFALSE, cr, (int16_t)offset);
/* Compile else — could be a direct array, object, or else-if stmt */
int saved_vc = cs->var_count;
cs->scope_depth++;
if (cJSON_IsArray(else_body)) {
int count = cJSON_GetArraySize(else_body);
for (int i = 0; i < count; i++)
@@ -1524,10 +1496,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
mach_compile_stmt(cs, else_body);
}
}
cs->scope_depth--;
for (int i = saved_vc; i < cs->var_count; i++)
sys_free(cs->vars[i].name);
cs->var_count = saved_vc;
/* Patch jmpend */
offset = mach_current_pc(cs) - (jmpend_pc + 1);
@@ -1560,8 +1528,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
/* Compile body — "statements" on a child "block"/"body", or directly on the node */
{
int saved_vc = cs->var_count;
cs->scope_depth++;
cJSON *body = cJSON_GetObjectItemCaseSensitive(stmt, "block");
if (!body) body = cJSON_GetObjectItemCaseSensitive(stmt, "body");
cJSON *stmts = body ? cJSON_GetObjectItemCaseSensitive(body, "statements") : NULL;
@@ -1573,10 +1539,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
} else if (body) {
mach_compile_stmt(cs, body);
}
cs->scope_depth--;
for (int i = saved_vc; i < cs->var_count; i++)
sys_free(cs->vars[i].name);
cs->var_count = saved_vc;
}
/* Patch continue chain to loop_top */
@@ -1613,8 +1575,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
/* For loop */
if (strcmp(kind, "for") == 0) {
int saved_vc = cs->var_count;
cs->scope_depth++;
cJSON *init = cJSON_GetObjectItemCaseSensitive(stmt, "init");
cJSON *cond = cJSON_GetObjectItemCaseSensitive(stmt, "test");
cJSON *update = cJSON_GetObjectItemCaseSensitive(stmt, "update");
@@ -1643,8 +1603,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
/* Body — "statements" on a child "block"/"body", or directly on the for node */
{
int body_vc = cs->var_count;
cs->scope_depth++;
cJSON *stmts = body ? cJSON_GetObjectItemCaseSensitive(body, "statements") : NULL;
if (!stmts) stmts = cJSON_GetObjectItemCaseSensitive(stmt, "statements");
if (stmts && cJSON_IsArray(stmts)) {
@@ -1654,10 +1612,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
} else if (body) {
mach_compile_stmt(cs, body);
}
cs->scope_depth--;
for (int i = body_vc; i < cs->var_count; i++)
sys_free(cs->vars[i].name);
cs->var_count = body_vc;
}
/* Patch continue chain to update (or loop_top if no update) */
@@ -1699,10 +1653,6 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
}
cs->loop_break = old_break;
cs->loop_continue = old_continue;
cs->scope_depth--;
for (int i = saved_vc; i < cs->var_count; i++)
sys_free(cs->vars[i].name);
cs->var_count = saved_vc;
return;
}

View File

@@ -500,13 +500,11 @@ static int mach_gen_compound_assign (MachGenState *s, cJSON *node, const char *o
if (strcmp (left_kind, "name") == 0) {
const char *name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (left, "name"));
const char *sn = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (left, "scope_name"));
const char *ln = sn ? sn : name;
cJSON *level_node = cJSON_GetObjectItemCaseSensitive (left, "level");
int level = level_node ? (int)cJSON_GetNumberValue (level_node) : -1;
int left_slot = mach_gen_alloc_slot (s);
if (level == 0 || level == -1) {
int local = mach_gen_find_var (s, ln);
int local = mach_gen_find_var (s, name);
if (local >= 0) {
mach_gen_emit_2 (s, "move", left_slot, local);
level = 0; /* treat as local for the store below */
@@ -515,7 +513,7 @@ static int mach_gen_compound_assign (MachGenState *s, cJSON *node, const char *o
if (level > 0) {
MachGenState *target = s;
for (int i = 0; i < level; i++) target = target->parent;
int slot = mach_gen_find_var (target, ln);
int slot = mach_gen_find_var (target, name);
mach_gen_emit_3 (s, "get", left_slot, slot, level);
} else if (level == -1) {
cJSON *instr = cJSON_CreateArray ();
@@ -532,12 +530,12 @@ static int mach_gen_compound_assign (MachGenState *s, cJSON *node, const char *o
int dest = mach_gen_alloc_slot (s);
mach_gen_emit_3 (s, op, dest, left_slot, right_slot);
if (level == 0) {
int local = mach_gen_find_var (s, ln);
int local = mach_gen_find_var (s, name);
if (local >= 0) mach_gen_emit_2 (s, "move", local, dest);
} else if (level > 0) {
MachGenState *target = s;
for (int i = 0; i < level; i++) target = target->parent;
int slot = mach_gen_find_var (target, ln);
int slot = mach_gen_find_var (target, name);
mach_gen_emit_3 (s, "put", dest, slot, level);
} else {
cJSON *instr = cJSON_CreateArray ();
@@ -606,12 +604,10 @@ static int mach_gen_assign (MachGenState *s, cJSON *node) {
if (strcmp (left_kind, "name") == 0) {
const char *name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (left, "name"));
const char *sn = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (left, "scope_name"));
const char *ln = sn ? sn : name;
cJSON *level_node = cJSON_GetObjectItemCaseSensitive (left, "level");
int level = level_node ? (int)cJSON_GetNumberValue (level_node) : -1;
if (level == 0 || level == -1) {
int slot = mach_gen_find_var (s, ln);
int slot = mach_gen_find_var (s, name);
if (slot >= 0) mach_gen_emit_2 (s, "move", slot, val_slot);
else if (level == -1) {
/* No annotation and not local — set global */
@@ -624,7 +620,7 @@ static int mach_gen_assign (MachGenState *s, cJSON *node) {
} else if (level > 0) {
MachGenState *target = s;
for (int i = 0; i < level; i++) target = target->parent;
int slot = mach_gen_find_var (target, ln);
int slot = mach_gen_find_var (target, name);
mach_gen_emit_3 (s, "put", val_slot, slot, level);
} else {
mach_gen_error (s, node, "cannot assign to unbound variable '%s'", name);
@@ -755,18 +751,16 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) {
/* Variable reference — uses parser-provided level annotation */
if (strcmp (kind, "name") == 0) {
const char *name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (expr, "name"));
const char *scope_name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (expr, "scope_name"));
const char *lookup_name = scope_name ? scope_name : name;
cJSON *level_node = cJSON_GetObjectItemCaseSensitive (expr, "level");
int level = level_node ? (int)cJSON_GetNumberValue (level_node) : -1;
if (level == 0 || level == -1) {
/* level 0 = known local; level -1 = no annotation, try local first */
int slot = mach_gen_find_var (s, lookup_name);
int slot = mach_gen_find_var (s, name);
if (slot >= 0) return slot;
} else if (level > 0) {
MachGenState *target = s;
for (int i = 0; i < level; i++) target = target->parent;
int parent_slot = mach_gen_find_var (target, lookup_name);
int parent_slot = mach_gen_find_var (target, name);
int dest = mach_gen_alloc_slot (s);
mach_gen_emit_3 (s, "get", dest, parent_slot, level);
return dest;
@@ -934,19 +928,17 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) {
if (strcmp (operand_kind, "name") == 0) {
const char *name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (operand, "name"));
const char *inc_sn = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (operand, "scope_name"));
const char *inc_ln = inc_sn ? inc_sn : name;
cJSON *level_node = cJSON_GetObjectItemCaseSensitive (operand, "level");
int level = level_node ? (int)cJSON_GetNumberValue (level_node) : -1;
int old_slot = mach_gen_alloc_slot (s);
/* Load current value */
if (level == 0) {
int local = mach_gen_find_var (s, inc_ln);
int local = mach_gen_find_var (s, name);
if (local >= 0) mach_gen_emit_2 (s, "move", old_slot, local);
} else if (level > 0) {
MachGenState *target = s;
for (int i = 0; i < level; i++) target = target->parent;
int slot = mach_gen_find_var (target, inc_ln);
int slot = mach_gen_find_var (target, name);
mach_gen_emit_3 (s, "get", old_slot, slot, level);
} else {
cJSON *instr = cJSON_CreateArray ();
@@ -963,12 +955,12 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) {
mach_gen_emit_3 (s, arith_op, new_slot, old_slot, one_slot);
/* Store new value */
if (level == 0) {
int local = mach_gen_find_var (s, inc_ln);
int local = mach_gen_find_var (s, name);
if (local >= 0) mach_gen_emit_2 (s, "move", local, new_slot);
} else if (level > 0) {
MachGenState *target = s;
for (int i = 0; i < level; i++) target = target->parent;
int slot = mach_gen_find_var (target, inc_ln);
int slot = mach_gen_find_var (target, name);
mach_gen_emit_3 (s, "put", new_slot, slot, level);
}
return postfix ? old_slot : new_slot;
@@ -1135,9 +1127,7 @@ static void mach_gen_statement (MachGenState *s, cJSON *stmt) {
cJSON *left = cJSON_GetObjectItemCaseSensitive (stmt, "left");
cJSON *right = cJSON_GetObjectItemCaseSensitive (stmt, "right");
const char *name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (left, "name"));
const char *scope_name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (left, "scope_name"));
const char *lookup_name = scope_name ? scope_name : name;
int local_slot = mach_gen_find_var (s, lookup_name);
int local_slot = mach_gen_find_var (s, name);
/* Pop: var val = arr[] */
cJSON *pop_flag = cJSON_GetObjectItemCaseSensitive (stmt, "pop");
if (pop_flag && cJSON_IsTrue (pop_flag) && right) {
@@ -3454,6 +3444,34 @@ JSValue JS_CallMcodeTree(JSContext *ctx, cJSON *root) {
return result;
}
JSValue JS_CallMcodeTreeEnv(JSContext *ctx, cJSON *root, JSValue env) {
if (!root) return JS_ThrowSyntaxError(ctx, "invalid MCODE tree");
cJSON *main_obj = cJSON_GetObjectItemCaseSensitive(root, "main");
if (!main_obj) {
cJSON_Delete(root);
return JS_ThrowSyntaxError(ctx, "MCODE tree missing 'main' section");
}
cJSON *functions = cJSON_GetObjectItemCaseSensitive(root, "functions");
/* Parse main code */
JSMCode *code = jsmcode_parse(main_obj, functions);
if (!code) {
cJSON_Delete(root);
return JS_ThrowInternalError(ctx, "failed to parse MCODE");
}
code->json_root = root; /* Keep tree alive — instrs point into it */
/* Execute with global_obj as this, passing env as outer_frame */
JSValue result = mcode_exec(ctx, code, ctx->global_obj, 0, NULL, env);
/* Clear frame ref before freeing mcode — stack trace data is inside code */
ctx->reg_current_frame = JS_NULL;
jsmcode_free(code);
return result;
}
JSValue JS_CallMcode(JSContext *ctx, const char *mcode_json) {
cJSON *root = cJSON_Parse(mcode_json);
if (!root) return JS_ThrowSyntaxError(ctx, "invalid MCODE JSON");

View File

@@ -966,7 +966,6 @@ cJSON *ast_parse_statement (ASTParseState *s) {
case TOK_VAR:
case TOK_DEF: {
const char *kind_name = (s->token_val == TOK_VAR) ? "var" : "def";
int is_def = (s->token_val == TOK_DEF);
ast_next_token (s);
/* Expect an identifier */
@@ -998,9 +997,9 @@ cJSON *ast_parse_statement (ASTParseState *s) {
const char *right_kind = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (right, "kind"));
if (right_kind && strcmp (right_kind, "[") == 0 && !cJSON_GetObjectItemCaseSensitive (right, "right"))
cJSON_AddBoolToObject (node, "pop", 1);
} else if (is_def) {
/* def (constant) requires initializer */
ast_error (s, var_ptr, "missing initializer for constant '%s'", var_name);
} else {
/* var and def both require initializer */
ast_error (s, var_ptr, "missing initializer for '%s' '%s'", kind_name, var_name);
}
sys_free (var_name);
ast_node_end (s, node, s->buf_ptr);
@@ -1097,16 +1096,11 @@ cJSON *ast_parse_statement (ASTParseState *s) {
if (s->token_val == '(') ast_next_token (s);
else ast_error (s, s->token_ptr, "expected '(' after for");
/* Init */
/* Init — only expressions allowed (no var/def) */
if (s->token_val != ';') {
if (s->token_val == TOK_VAR || s->token_val == TOK_DEF) {
cJSON *init = ast_parse_statement (s);
cJSON_AddItemToObject (node, "init", init);
} else {
cJSON *init = ast_parse_expr (s);
cJSON_AddItemToObject (node, "init", init);
if (s->token_val == ';') ast_next_token (s);
}
cJSON *init = ast_parse_expr (s);
cJSON_AddItemToObject (node, "init", init);
if (s->token_val == ';') ast_next_token (s);
} else {
ast_next_token (s);
}
@@ -1271,7 +1265,6 @@ static cJSON *ast_parse_program (ASTParseState *s) {
typedef struct ASTSemVar {
const char *name;
const char *scope_name; /* disambiguated name for block-scope vars (NULL = use name) */
int is_const;
const char *make; /* "def", "var", "function", "input" */
int function_nr; /* which function this var belongs to */
@@ -1286,7 +1279,6 @@ typedef struct ASTSemScope {
int in_loop;
int function_nr; /* function_nr of enclosing function */
int is_function_scope; /* 1 if this is a function's top-level scope */
int block_depth; /* 0 = function scope, 1+ = block scope */
} ASTSemScope;
typedef struct ASTSemState {
@@ -1295,7 +1287,6 @@ typedef struct ASTSemState {
cJSON *scopes_array;
const char *intrinsics[256];
int intrinsic_count;
int block_var_counter; /* monotonically increasing counter for unique block var names */
} ASTSemState;
static void ast_sem_error (ASTSemState *st, cJSON *node, const char *fmt, ...) {
@@ -1324,7 +1315,6 @@ static void ast_sem_add_var (ASTSemScope *scope, const char *name, int is_const,
if (scope->var_count < AST_SEM_MAX_VARS) {
ASTSemVar *v = &scope->vars[scope->var_count];
v->name = name;
v->scope_name = NULL;
v->is_const = is_const;
v->make = make;
v->function_nr = function_nr;
@@ -1334,26 +1324,6 @@ static void ast_sem_add_var (ASTSemScope *scope, const char *name, int is_const,
}
}
/* Propagate block-scope vars to the function scope (parent) with disambiguated names */
static void ast_sem_propagate_block_vars (ASTSemState *st, ASTSemScope *parent,
ASTSemScope *block) {
for (int i = 0; i < block->var_count; i++) {
ASTSemVar *v = &block->vars[i];
const char *sn = v->scope_name ? v->scope_name : v->name;
if (parent->var_count < AST_SEM_MAX_VARS) {
ASTSemVar *pv = &parent->vars[parent->var_count];
pv->name = sn;
pv->scope_name = NULL;
pv->is_const = v->is_const;
pv->make = v->make;
pv->function_nr = v->function_nr;
pv->nr_uses = v->nr_uses;
pv->closure = v->closure;
parent->var_count++;
}
}
}
typedef struct {
ASTSemVar *var;
int level;
@@ -1486,8 +1456,6 @@ static void ast_sem_check_assign_target (ASTSemState *st, ASTSemScope *scope, cJ
if (r.var) {
cJSON_AddNumberToObject (left, "level", r.level);
cJSON_AddNumberToObject (left, "function_nr", r.def_function_nr);
if (r.var->scope_name)
cJSON_AddStringToObject (left, "scope_name", r.var->scope_name);
} else {
cJSON_AddNumberToObject (left, "level", -1);
}
@@ -1540,13 +1508,11 @@ static void ast_sem_check_expr (ASTSemState *st, ASTSemScope *scope, cJSON *expr
} else if (v->is_const) {
ast_sem_error (st, expr, "cannot assign to constant '%s'", name);
}
/* Annotate with level/function_nr/scope_name so compilers can emit correct set instructions */
/* Annotate with level/function_nr so compilers can emit correct set instructions */
ASTSemLookup r = ast_sem_lookup_var (scope, name);
if (r.var) {
cJSON_AddNumberToObject (operand, "level", r.level);
cJSON_AddNumberToObject (operand, "function_nr", r.def_function_nr);
if (r.var->scope_name)
cJSON_AddStringToObject (operand, "scope_name", r.var->scope_name);
} else {
cJSON_AddNumberToObject (operand, "level", -1);
}
@@ -1697,8 +1663,6 @@ static void ast_sem_check_expr (ASTSemState *st, ASTSemScope *scope, cJSON *expr
cJSON_AddNumberToObject (expr, "function_nr", r.def_function_nr);
r.var->nr_uses++;
if (r.level > 0) r.var->closure = 1;
if (r.var->scope_name)
cJSON_AddStringToObject (expr, "scope_name", r.var->scope_name);
} else {
cJSON_AddNumberToObject (expr, "level", -1);
ast_sem_add_intrinsic (st, name);
@@ -1717,6 +1681,10 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
if (!kind) return;
if (strcmp (kind, "var_list") == 0) {
if (!scope->is_function_scope) {
ast_sem_error (st, stmt, "'var' declaration must be at function body level");
return;
}
cJSON *item;
cJSON_ArrayForEach (item, cJSON_GetObjectItemCaseSensitive (stmt, "list")) {
ast_sem_check_stmt (st, scope, item);
@@ -1725,6 +1693,10 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
}
if (strcmp (kind, "var") == 0) {
if (!scope->is_function_scope) {
ast_sem_error (st, stmt, "'var' declaration must be at function body level");
return;
}
/* Register variable */
cJSON *left = cJSON_GetObjectItemCaseSensitive (stmt, "left");
const char *name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (left, "name"));
@@ -1733,23 +1705,18 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
if (existing && existing->is_const) {
ast_sem_error (st, left, "cannot redeclare constant '%s'", name);
}
if (!existing || existing->function_nr != scope->function_nr
|| scope->block_depth > 0)
if (!existing || existing->function_nr != scope->function_nr)
ast_sem_add_var (scope, name, 0, "var", scope->function_nr);
if (scope->block_depth > 0) {
char buf[128];
snprintf (buf, sizeof (buf), "_%s_%d", name, st->block_var_counter++);
char *sn = sys_malloc (strlen (buf) + 1);
strcpy (sn, buf);
scope->vars[scope->var_count - 1].scope_name = sn;
cJSON_AddStringToObject (left, "scope_name", sn);
}
}
ast_sem_check_expr (st, scope, cJSON_GetObjectItemCaseSensitive (stmt, "right"));
return;
}
if (strcmp (kind, "def") == 0) {
if (!scope->is_function_scope) {
ast_sem_error (st, stmt, "'def' declaration must be at function body level");
return;
}
/* Register constant */
cJSON *left = cJSON_GetObjectItemCaseSensitive (stmt, "left");
const char *name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (left, "name"));
@@ -1763,14 +1730,6 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
existing->make = "def";
} else {
ast_sem_add_var (scope, name, 1, "def", scope->function_nr);
if (scope->block_depth > 0) {
char buf[128];
snprintf (buf, sizeof (buf), "_%s_%d", name, st->block_var_counter++);
char *sn = sys_malloc (strlen (buf) + 1);
strcpy (sn, buf);
scope->vars[scope->var_count - 1].scope_name = sn;
cJSON_AddStringToObject (left, "scope_name", sn);
}
}
}
ast_sem_check_expr (st, scope, cJSON_GetObjectItemCaseSensitive (stmt, "right"));
@@ -1789,31 +1748,25 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
ASTSemScope then_scope = {0};
then_scope.parent = scope;
then_scope.function_nr = scope->function_nr;
then_scope.block_depth = scope->block_depth + 1;
cJSON_ArrayForEach (s2, cJSON_GetObjectItemCaseSensitive (stmt, "then")) {
ast_sem_check_stmt (st, &then_scope, s2);
}
ast_sem_propagate_block_vars (st, scope, &then_scope);
}
{
ASTSemScope list_scope = {0};
list_scope.parent = scope;
list_scope.function_nr = scope->function_nr;
list_scope.block_depth = scope->block_depth + 1;
cJSON_ArrayForEach (s2, cJSON_GetObjectItemCaseSensitive (stmt, "list")) {
ast_sem_check_stmt (st, &list_scope, s2);
}
ast_sem_propagate_block_vars (st, scope, &list_scope);
}
{
ASTSemScope else_scope = {0};
else_scope.parent = scope;
else_scope.function_nr = scope->function_nr;
else_scope.block_depth = scope->block_depth + 1;
cJSON_ArrayForEach (s2, cJSON_GetObjectItemCaseSensitive (stmt, "else")) {
ast_sem_check_stmt (st, &else_scope, s2);
}
ast_sem_propagate_block_vars (st, scope, &else_scope);
}
return;
}
@@ -1824,12 +1777,10 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
loop_scope.parent = scope;
loop_scope.in_loop = 1;
loop_scope.function_nr = scope->function_nr;
loop_scope.block_depth = scope->block_depth + 1;
cJSON *s2;
cJSON_ArrayForEach (s2, cJSON_GetObjectItemCaseSensitive (stmt, "statements")) {
ast_sem_check_stmt (st, &loop_scope, s2);
}
ast_sem_propagate_block_vars (st, scope, &loop_scope);
return;
}
@@ -1838,12 +1789,10 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
loop_scope.parent = scope;
loop_scope.in_loop = 1;
loop_scope.function_nr = scope->function_nr;
loop_scope.block_depth = scope->block_depth + 1;
cJSON *s2;
cJSON_ArrayForEach (s2, cJSON_GetObjectItemCaseSensitive (stmt, "statements")) {
ast_sem_check_stmt (st, &loop_scope, s2);
}
ast_sem_propagate_block_vars (st, scope, &loop_scope);
ast_sem_check_expr (st, scope, cJSON_GetObjectItemCaseSensitive (stmt, "expression"));
return;
}
@@ -1853,24 +1802,16 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
loop_scope.parent = scope;
loop_scope.in_loop = 1;
loop_scope.function_nr = scope->function_nr;
loop_scope.block_depth = scope->block_depth + 1;
/* init may be a var/def statement or expression */
/* init is expression only (no var/def in for init) */
cJSON *init = cJSON_GetObjectItemCaseSensitive (stmt, "init");
if (init) {
const char *init_kind = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (init, "kind"));
if (init_kind && (strcmp (init_kind, "var") == 0 || strcmp (init_kind, "def") == 0)) {
ast_sem_check_stmt (st, &loop_scope, init);
} else {
ast_sem_check_expr (st, &loop_scope, init);
}
}
if (init)
ast_sem_check_expr (st, &loop_scope, init);
ast_sem_check_expr (st, &loop_scope, cJSON_GetObjectItemCaseSensitive (stmt, "test"));
ast_sem_check_expr (st, &loop_scope, cJSON_GetObjectItemCaseSensitive (stmt, "update"));
cJSON *s2;
cJSON_ArrayForEach (s2, cJSON_GetObjectItemCaseSensitive (stmt, "statements")) {
ast_sem_check_stmt (st, &loop_scope, s2);
}
ast_sem_propagate_block_vars (st, scope, &loop_scope);
return;
}
@@ -1901,12 +1842,10 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
ASTSemScope block_scope = {0};
block_scope.parent = scope;
block_scope.function_nr = scope->function_nr;
block_scope.block_depth = scope->block_depth + 1;
cJSON *s2;
cJSON_ArrayForEach (s2, cJSON_GetObjectItemCaseSensitive (stmt, "statements")) {
ast_sem_check_stmt (st, &block_scope, s2);
}
ast_sem_propagate_block_vars (st, scope, &block_scope);
return;
}

View File

@@ -101,7 +101,7 @@
// #define DUMP_ROPE_REBALANCE
/* test the GC by forcing it before each object allocation */
#define FORCE_GC_AT_MALLOC
// #define FORCE_GC_AT_MALLOC
#define POISON_HEAP
/* POISON_HEAP: Use ASan's memory poisoning to detect stale pointer access */
@@ -1738,7 +1738,6 @@ typedef struct MachVarInfo {
int slot;
int is_const; /* 1 for def, function args; 0 for var */
int is_closure; /* 1 if captured by a nested function */
int scope_depth; /* block scope nesting level */
} MachVarInfo;
/* === PPretext (parser pretext, system-malloc, used by cell_js.c parser) === */

View File

@@ -1268,6 +1268,9 @@ char *JS_Mcode (const char *ast_json);
/* Execute MCODE from cJSON tree. Takes ownership of root. */
JSValue JS_CallMcodeTree (JSContext *ctx, struct cJSON *root);
/* Execute MCODE from cJSON tree with hidden env. Takes ownership of root. */
JSValue JS_CallMcodeTreeEnv (JSContext *ctx, struct cJSON *root, JSValue env);
/* Parse and execute MCODE JSON string.
Returns result of execution, or JS_EXCEPTION on error. */
JSValue JS_CallMcode (JSContext *ctx, const char *mcode_json);

View File

@@ -10655,7 +10655,8 @@ static JSValue js_mach_eval (JSContext *ctx, JSValue this_val, int argc, JSValue
return JS_ThrowSyntaxError (ctx, "mach_eval: failed to parse AST");
}
JSValue result = JS_RunMachTree (ctx, ast, JS_NULL);
JSValue env = (argc >= 3 && JS_IsObject (argv[2])) ? argv[2] : JS_NULL;
JSValue result = JS_RunMachTree (ctx, ast, env);
cJSON_Delete (ast);
JS_FreeCString (ctx, name);
return result;

View File

@@ -7,7 +7,8 @@ var error_names = []
var error_reasons = []
var fail_msg = ""
for (var _i = 0; _i < 100; _i++) {
var _i = 0
for (_i = 0; _i < 100; _i++) {
error_names[] = null
error_reasons[] = null
}
@@ -105,11 +106,6 @@ run("var declaration", function() {
assert_eq(x, 5, "var init")
})
run("var uninitialized", function() {
var x
assert_eq(x, null, "var defaults to null")
})
run("var multiple declaration", function() {
var a = 1, b = 2, c = 3
assert_eq(a + b + c, 6, "multi var")
@@ -330,19 +326,21 @@ run("while break continue", function() {
run("for loop", function() {
var sum = 0
for (var i = 0; i < 5; i++) sum += i
var i = 0
for (i = 0; i < 5; i++) sum += i
assert_eq(sum, 10, "for basic")
})
run("for break continue", function() {
var sum = 0
for (var i = 0; i < 10; i++) {
var i = 0
for (i = 0; i < 10; i++) {
if (i == 5) break
sum += i
}
assert_eq(sum, 10, "for break")
sum = 0
for (var i = 0; i < 5; i++) {
for (i = 0; i < 5; i++) {
if (i % 2 == 0) continue
sum += i
}
@@ -351,28 +349,13 @@ run("for break continue", function() {
run("nested for", function() {
var sum = 0
for (var i = 0; i < 3; i++)
for (var j = 0; j < 3; j++)
var i = 0, j = 0
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
sum++
assert_eq(sum, 9, "nested for")
})
// === BLOCK SCOPING ===
run("block scoping", function() {
var x = 1
{
var x = 2
assert_eq(x, 2, "inner block")
}
assert_eq(x, 1, "outer preserved")
})
run("for iterator scope", function() {
for (var i = 0; i < 1; i++) {}
assert_eq(should_disrupt(function() { var y = i }), true, "for var does not leak")
})
// === FUNCTIONS ===
run("function expression", function() {
@@ -649,9 +632,10 @@ run("inequality not confused with bang ident", function() {
// === SUMMARY ===
print(text(passed) + " passed, " + text(failed) + " failed out of " + text(passed + failed))
var _j = 0
if (failed > 0) {
print("")
for (var _j = 0; _j < failed; _j++) {
for (_j = 0; _j < failed; _j++) {
print(" FAIL " + error_names[_j] + ": " + error_reasons[_j])
}
}

View File

@@ -9,7 +9,8 @@ var error_reasons = []
var fail_msg = ""
// pre-allocate 500 slots to avoid array growth during disruption handlers
for (var _i = 0; _i < 5; _i++) {
var _i = 0
for (_i = 0; _i < 5; _i++) {
error_names[] = null
error_reasons[] = null
}
@@ -295,79 +296,9 @@ run("var reassignment", function() {
if (x != 10) fail("var reassignment failed")
})
run("var block scope basic", function() {
var x = 1
{
var x = 2
if (x != 2) fail("var should be block scoped - inner scope failed")
}
if (x != 1) fail("var should be block scoped - outer scope affected")
})
run("var block scope if", function() {
var x = 1
if (true) {
var x = 2
if (x != 2) fail("var in if block should be scoped")
}
if (x != 1) fail("var in if block should not affect outer scope")
})
run("var block scope for", function() {
var x = 1
for (var i = 0; i < 1; i = i + 1) {
var x = 2
if (x != 2) fail("var in for block should be scoped")
}
if (x != 1) fail("var in for block should not affect outer scope")
})
run("var for loop iterator scope", function() {
var sum = 0
for (var i = 0; i < 3; i = i + 1) {
sum = sum + i
}
if (sum != 3) fail("for loop should work with block scoped var")
if (!should_disrupt(function() { var y = i })) fail("for loop iterator should not leak to outer scope")
})
run("var nested blocks", function() {
var x = 1
{
var x = 2
{
var x = 3
if (x != 3) fail("var in nested block level 2 failed")
}
if (x != 2) fail("var in nested block level 1 failed")
}
if (x != 1) fail("var in nested blocks outer scope failed")
})
run("var redeclaration different scope", function() {
var x = 1
{
var x = 2
}
if (x != 1) fail("var in different scope should not affect outer")
})
run("var while scope", function() {
var x = 1
var count = 0
while (count < 1) {
var x = 2
if (x != 2) fail("var in while should be block scoped")
count = count + 1
}
if (x != 1) fail("var in while should not affect outer scope")
})
run("var no initialization", function() {
{
var x
if (x != null) fail("uninitialized var should be null")
}
run("var null initialization", function() {
var x = null
if (x != null) fail("null-initialized var should be null")
})
run("multiple var declaration", function() {
@@ -717,7 +648,8 @@ run("while continue", function() {
run("for loop", function() {
var sum = 0
for (var i = 0; i < 5; i = i + 1) {
var i = 0
for (i = 0; i < 5; i = i + 1) {
sum = sum + i
}
if (sum != 10) fail("for loop failed")
@@ -725,7 +657,8 @@ run("for loop", function() {
run("for loop break", function() {
var sum = 0
for (var i = 0; i < 10; i = i + 1) {
var i = 0
for (i = 0; i < 10; i = i + 1) {
if (i == 5) break
sum = sum + i
}
@@ -734,7 +667,8 @@ run("for loop break", function() {
run("for loop continue", function() {
var sum = 0
for (var i = 0; i < 10; i = i + 1) {
var i = 0
for (i = 0; i < 10; i = i + 1) {
if (i % 2 == 0) continue
sum = sum + i
}
@@ -743,8 +677,9 @@ run("for loop continue", function() {
run("nested for loops", function() {
var sum = 0
for (var i = 0; i < 3; i = i + 1) {
for (var j = 0; j < 3; j = j + 1) {
var i = 0, j = 0
for (i = 0; i < 3; i = i + 1) {
for (j = 0; j < 3; j = j + 1) {
sum = sum + 1
}
}
@@ -877,7 +812,7 @@ run("is_null", function() {
if (is_null("")) fail("is_null empty string should be false")
if (is_null({})) fail("is_null object should be false")
if (is_null([])) fail("is_null array should be false")
var x
var x = null
if (!is_null(x)) fail("is_null undefined variable should be true")
})
@@ -1265,9 +1200,9 @@ run("function length property", function() {
// NULL AND UNDEFINED BEHAVIOR
// ============================================================================
run("undefined variable is null", function() {
var x
if (x != null) fail("undefined variable should be null")
run("null initialized variable is null", function() {
var x = null
if (x != null) fail("null initialized variable should be null")
})
// ============================================================================
@@ -1716,7 +1651,8 @@ run("function proxy with one arg", function() {
run("function proxy with multiple args", function() {
var proxy = function(name, args) {
var sum = 0
for (var i = 0; i < length(args); i++) {
var i = 0
for (i = 0; i < length(args); i++) {
sum = sum + args[i]
}
return `${name}:${sum}`
@@ -3183,7 +3119,8 @@ run("gc cycle object self", function() {
run("gc cycle array self", function() {
var arr = []
for (var i = 0; i < 10; i++) {
var i = 0
for (i = 0; i < 10; i++) {
arr[] = arr
}
if (arr[0] != arr) fail("array self cycle failed")
@@ -3336,22 +3273,26 @@ run("fn.apply with single value", function() {
run("gc reverse under pressure", function() {
var arrays = []
for (var i = 0; i < 100; i = i + 1) {
var i = 0
var rev = null
for (i = 0; i < 100; i = i + 1) {
arrays[i] = [i, i+1, i+2, i+3, i+4]
}
for (var i = 0; i < 100; i = i + 1) {
var rev = reverse(arrays[i])
for (i = 0; i < 100; i = i + 1) {
rev = reverse(arrays[i])
if (rev[0] != i+4) fail("gc reverse stress failed")
}
})
run("gc object select under pressure", function() {
var objs = []
for (var i = 0; i < 100; i = i + 1) {
var i = 0
var selected = null
for (i = 0; i < 100; i = i + 1) {
objs[i] = {a: i, b: i+1, c: i+2, d: i+3}
}
for (var i = 0; i < 100; i = i + 1) {
var selected = object(objs[i], ["a", "c"])
for (i = 0; i < 100; i = i + 1) {
selected = object(objs[i], ["a", "c"])
if (selected.a != i) fail("gc object select stress failed")
if (selected.c != i+2) fail("gc object select stress c failed")
}
@@ -3359,12 +3300,15 @@ run("gc object select under pressure", function() {
run("gc object from keys function under pressure", function() {
var keysets = []
for (var i = 0; i < 50; i = i + 1) {
var i = 0
var obj = null
var expected = null
for (i = 0; i < 50; i = i + 1) {
keysets[i] = [`k${i}`, `j${i}`, `m${i}`]
}
for (var i = 0; i < 50; i = i + 1) {
var obj = object(keysets[i], function(k) { return k + "_value" })
var expected = `k${i}_value`
for (i = 0; i < 50; i = i + 1) {
obj = object(keysets[i], function(k) { return k + "_value" })
expected = `k${i}_value`
if (obj[`k${i}`] != expected) fail("gc object from keys func stress failed")
}
})
@@ -3550,9 +3494,10 @@ run("functino >>>! unsigned shift right", function() {
// ============================================================================
print(text(passed) + " passed, " + text(failed) + " failed out of " + text(passed + failed))
var _j = 0
if (failed > 0) {
print("")
for (var _j = 0; _j < failed; _j++) {
for (_j = 0; _j < failed; _j++) {
print(" FAIL " + error_names[_j] + ": " + error_reasons[_j])
}
}