Temporary chapter name for all chapters
multi-digit-compiler

The compiler still emits one byte per number:

appendStringLiteral(&wat_buffer, &wat_length, "i64.const ");
appendChar(&wat_buffer, &wat_length, input[input_pos]);
appendChar(&wat_buffer, &wat_length, '\n');
input_pos += 1;

Cleanest path: parse the number with parseNumber (gives us an i64), then write the digits back into the wat buffer with a new helper. Same shape as printNumber, but the output sink is appendChar(buffer, position, c) instead of printChar(c):

fn appendNumber(
    buffer: [*]u8,
    position: *usize,
    n: i64,
) void {
    var value: i64 = n;
    if (value < 0) {
        appendChar(buffer, position, '-');
        value = -value;
    }
    if (value == 0) {
        appendChar(buffer, position, '0');
        return;
    }
    var buf: [20]u8 = undefined;
    var i: usize = 0;
    while (value > 0) {
        const d: i64 = @mod(value, 10);
        buf[i] = @intCast('0' + d);
        i += 1;
        value = @divTrunc(value, 10);
    }
    while (i > 0) {
        i -= 1;
        appendChar(buffer, position, buf[i]);
    }
}

Now in the compiler, replace the byte-copy with parseNumber + appendNumber. Same fix in two places: the initial number, and the next number inside the chain loop.

// 2. THE COMPILER
var wat_buffer: [512]u8 = undefined;
var wat_length: usize = 0;

input_pos = 0;
appendStringLiteral(&wat_buffer, &wat_length, "i64.const ");
appendNumber(&wat_buffer, &wat_length, parseNumber(input, &input_pos));
appendChar(&wat_buffer, &wat_length, '\n');

while (input[input_pos] != 0) {
    const op: u8 = input[input_pos];
    input_pos += 1;

    appendStringLiteral(&wat_buffer, &wat_length, "i64.const ");
    appendNumber(&wat_buffer, &wat_length, parseNumber(input, &input_pos));
    appendChar(&wat_buffer, &wat_length, '\n');

    if (op == '+') {
        appendStringLiteral(&wat_buffer, &wat_length, "i64.add\n");
    } else if (op == '-') {
        appendStringLiteral(&wat_buffer, &wat_length, "i64.sub\n");
    } else if (op == '*') {
        appendStringLiteral(&wat_buffer, &wat_length, "i64.mul\n");
    } else if (op == '/') {
        appendStringLiteral(&wat_buffer, &wat_length, "i64.div_s\n");
    } else {
        printString("error: unknown operator\n");
        return;
    }
}

The compiler reads from input via parseNumber, writes to wat_buffer via appendNumber. One line per number on each side. The bytes of input and the bytes of wat_buffer never touch -- everything goes through i64. Try 12+3, 100+23, 12*12 and watch the VM agree with the interpreter.