Temporary chapter name for all chapters
compile-to-wat

Let's take a little detour to recap Zig's syntax around arrays and pointers:

- [25]u8 is an array. It is a value and it lives on the stack, just like any value.
- *u8 is a pointer to a u8.
- [*]u8 is a pointer to a u8 that is part of an array (even though the syntax is weird, this is the most normal one).
- []u8 is a combination of a [*]u8 and a size: it is a slice, a view into an array, a fat pointer.
- [:0]u8 is a slice that ends with a zero byte (length AND sentinel).
- [*:0]u8 is like [*]u8 but the array it points into ends with a zero byte. Walk until you see the zero.
- & in front of something gives you a pointer to it (or something that is some sort of pointer such as a slice).
- .* after a pointer is the opposite: it dereferences, which means "follow this pointer".

When you write "hello", Zig puts the constant bytes h e l l o 0 in the "data section of your executable" and hands you a [:0]const u8 so you can access it.

If you are feeling overwhelmed, don't worry too much. You will learn to love Zig's explicitness around pointers; it is one of its best features.

Back to WAT. We now want to generate it instead of writing it by hand. Two helpers do the heavy lifting.

The helpers below take &wat_buffer and &wat_length so they can write into our buffer and bump the length. Inside, position.* reads or writes the value the pointer points at -- the .* "unwraps" the pointer.

fn appendChar(
    buffer: [*]u8,
    position: *usize,
    c: u8,
) void {
    buffer[position.*] = c;
    position.* += 1;
}

fn appendStringLiteral(
    buffer: [*]u8,
    position: *usize,
    s: [*:0]const u8,
) void {
    var i: usize = 0;
    while (s[i] != 0) {
        appendChar(buffer, position, s[i]);
        i += 1;
    }
}

appendStringLiteral walks s until it sees the zero -- that's why s is [*:0]const u8. Every Zig string literal has that zero at the end, including the multi-line \\ ones.

We keep both paths in main and pick between them with write_wat_manually. When true, paste the WAT in by hand; when false, the compiler emits it. Either way the buffer ends up the same. Same vm result.

Fill in the else branch.

pub fn main() void {
    const input: [:0]const u8 = "3+4";

    const a: i64 = @as(i64, input[0]) - '0';
    const b: i64 = @as(i64, input[2]) - '0';
    const result: i64 = a + b;

    printString("interpreter result for ");
    printString(input);
    printString(" : ");
    printNumber(result);
    printChar('\n');

    var wat_buffer: [256]u8 = undefined;
    var wat_length: usize = 0;

    const write_wat_manually: bool = false;
    if (write_wat_manually) {
        appendStringLiteral(&wat_buffer, &wat_length,
            \\i64.const 3
            \\i64.const 4
            \\i64.add
        );
    } else {

        // YOU: emit "i64.const X\n" for input[0],
        //      "i64.const Y\n" for input[2], "i64.add\n".
    
    }

    var stack: [16]i64 = undefined;
    var stack_pointer: usize = 0;

    var wat_pos: usize = 0;
    while (wat_pos < wat_length) {
        var wat_line_end: usize = wat_pos;
        while (wat_line_end < wat_length and wat_buffer[wat_line_end] != '\n') {
            wat_line_end += 1;
        }
        const line: []const u8 = wat_buffer[wat_pos..wat_line_end];

        if (stringStartsWith(line, "i64.const ")) {
            stack[stack_pointer] = @as(i64, line[line.len - 1]) - '0';
            stack_pointer += 1;
        } else if (stringsEqual(line, "i64.add")) {
            const y: i64 = stack[stack_pointer - 1];
            const x: i64 = stack[stack_pointer - 2];
            stack_pointer -= 2;
            stack[stack_pointer] = x + y;
            stack_pointer += 1;
        } else {
            printString("error: unknown instruction\n");
            return;
        }

        wat_pos = wat_line_end + 1;
    }

    printString("vm result: ");
    printNumber(stack[0]);
    printChar('\n');
}
appendStringLiteral(&wat_buffer, &wat_length, "i64.const ");
appendChar(&wat_buffer, &wat_length, input[0]);
appendChar(&wat_buffer, &wat_length, '\n');
appendStringLiteral(&wat_buffer, &wat_length, "i64.const ");
appendChar(&wat_buffer, &wat_length, input[2]);
appendChar(&wat_buffer, &wat_length, '\n');
appendStringLiteral(&wat_buffer, &wat_length, "i64.add\n");

We don't bother converting the digit char to a number and back -- input[0] is already a valid WAT digit. Append it as a byte.

Flip write_wat_manually to true and back. Same 7 either way.