Local variable scanning. WAT requires all (local ...) declarations at the top of a function. We scan the body before compiling it:
var local_names: [64][]const u8 = undefined;
var local_count: usize = 0;
fn scan_for_locals(start: usize, end_pos: usize) void {
const saved: usize = pos;
pos = start;
local_count = 0;
while (pos < end_pos) {
if (is_letter(cur())) {
const w: []const u8 = read_name();
if (streq(w, "var") or streq(w, "const")) {
const vname: []const u8 = read_name();
if (cur() == ':') { pos += 1; skip(); _ = read_name(); }
var dup: bool = false;
for (0..local_count) |i| {
if (streq(local_names[i], vname)) { dup = true; break; }
}
if (!dup and !c_is_global(vname)) {
local_names[local_count] = vname;
local_count += 1;
}
}
} else {
pos += 1;
}
}
pos = saved;
}
fn emit_locals() void {
for (0..local_count) |i| {
emit_str(" (local $");
emit_str(local_names[i]);
emit_str(" i64)\n");
}
emit_str(" (local $__tmp i64)\n");
}
Every function gets a $__tmp local -- the store instructions need it to swap operand order on the stack.