rm block scope

This commit is contained in:
2026-02-09 10:11:22 -06:00
parent 7469383e66
commit 0503acb7e6
6 changed files with 91 additions and 284 deletions

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) {

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

@@ -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

@@ -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])
}
}