Write vm_exec_line(line) -- execute a single instruction. Start with arithmetic: i64.const N, i64.add, i64.sub, i64.mul, i64.div_s, and comparisons.
Returns true normally, false if we hit something that needs special handling (we'll use this later for control flow).
fn vm_exec_line(raw: []const u8) void {
const line: []const u8 = vm_trim(raw);
if (starts_with(line, "i64.const ")) {
vm_push(parse_int(line, 10));
return;
}
if (streq(line, "i64.add")) { const b: i64 = vm_pop(); const a: i64 = vm_pop(); vm_push(a + b); return; }
if (streq(line, "i64.sub")) { const b: i64 = vm_pop(); const a: i64 = vm_pop(); vm_push(a - b); return; }
if (streq(line, "i64.mul")) { const b: i64 = vm_pop(); const a: i64 = vm_pop(); vm_push(a * b); return; }
if (streq(line, "i64.div_s")) { const b: i64 = vm_pop(); const a: i64 = vm_pop(); vm_push(@divTrunc(a, b)); return; }
if (streq(line, "i64.gt_s")) { const b: i64 = vm_pop(); const a: i64 = vm_pop(); vm_push(if (a > b) @as(i64, 1) else 0); return; }
if (streq(line, "i64.lt_s")) { const b: i64 = vm_pop(); const a: i64 = vm_pop(); vm_push(if (a < b) @as(i64, 1) else 0); return; }
if (streq(line, "i64.eq")) { const b: i64 = vm_pop(); const a: i64 = vm_pop(); vm_push(if (a == b) @as(i64, 1) else 0); return; }
if (streq(line, "i64.ne")) { const b: i64 = vm_pop(); const a: i64 = vm_pop(); vm_push(if (a != b) @as(i64, 1) else 0); return; }
if (streq(line, "i64.eqz")) { const a: i64 = vm_pop(); vm_push(if (a == 0) @as(i64, 1) else 0); return; }
if (starts_with(line, "local.get $")) {
vm_push(vm_get_local(parse_name(line, 0)));
return;
}
if (starts_with(line, "local.set $")) {
vm_set_local(parse_name(line, 0), vm_pop());
return;
}
if (starts_with(line, "(local $")) {
// local declaration -- register the name, value starts at 0
vm_set_local(parse_name(line, 0), 0);
return;
}
// Lines we skip: (func, (param, (result, ), (export, etc.
// Control flow (if, else, end, block, loop, br, call, return) handled by vm_run
}
Test it by feeding a few lines manually:
vm_sp = 0;
vm_exec_line(" i64.const 3");
vm_exec_line(" i64.const 5");
vm_exec_line(" i64.add");
print("vm: {d}\n", .{vm_pop()}); // 8
That's the VM running 3 + 5 = 8 from WAT instructions. Same answer as the interpreter. We're getting somewhere.