Update exec_stmt to create globals at the top level. The var handler:
if (streq(word, "var") or streq(word, "const")) {
const vname: []const u8 = read_name();
if (cur() == ':') { pos += 1; skip(); _ = read_name(); }
if (cur() == '=') { pos += 1; skip(); }
const val: i64 = expression();
if (cur() == ';') { pos += 1; skip(); }
if (!in_function) {
setGlobal(vname, val);
} else {
setVar(vname, val);
}
return val;
}
And set in_function = true in call_fn, restore it after:
fn call_fn(name: []const u8, args: *const [8]i64, ac: usize) i64 {
_ = ac;
// ... find function ...
// Save caller's LOCAL state only
const d: usize = save_depth;
save_nv[d] = num_vars;
const saved_pos: usize = pos;
const was_in_function: bool = in_function;
for (0..num_vars) |i| {
save_vn[d][i] = var_names[i];
save_vv[d][i] = var_values[i];
}
save_depth += 1;
num_vars = 0;
in_function = true;
// Bind parameters as locals
for (0..fn_pcounts[fi]) |i| {
setVar(fn_params[fi][i], args[i]);
}
// Execute
pos = fn_starts[fi];
return_flag = false;
const result: i64 = exec_block();
// Restore locals only -- globals untouched
save_depth -= 1;
pos = saved_pos;
num_vars = save_nv[d];
in_function = was_in_function;
for (0..num_vars) |i| {
var_names[i] = save_vn[d][i];
var_values[i] = save_vv[d][i];
}
return_flag = false;
return result;
}
The key line: globals are never saved or restored. They survive function calls. Locals are saved on entry and restored on exit, exactly as before.