rm block scope
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
109
source/parse.c
109
source/parse.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) === */
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
|
||||
137
vm_suite.ce
137
vm_suite.ce
@@ -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])
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user