Feeding the Snake
string-builtins

Add str_addr(s) and str_len(s) as builtins that extract the address and length from a packed string value. Add print_str(s) that writes the bytes to stderr via Zig's print.

    if (streq(name, "str_len")) {
        pos += 1; skip();
        const val: i64 = expression();
        if (cur() == ')') { pos += 1; skip(); }
        return @rem(val, 65536);
    }
    if (streq(name, "str_addr")) {
        pos += 1; skip();
        const val: i64 = expression();
        if (cur() == ')') { pos += 1; skip(); }
        return @divTrunc(val, 65536);
    }
    if (streq(name, "print_str")) {
        pos += 1; skip();
        const val: i64 = expression();
        if (cur() == ')') { pos += 1; skip(); }
        const addr: usize = @intCast(@divTrunc(val, 65536));
        const len: usize = @intCast(@rem(val, 65536));
        print("{s}", .{heap[addr..addr + len]});
        return 0;
    }

Now:

    check("str_len(\"hello\")", 5);
    check("str_len(\"\")", 0);
    check("str_addr(\"hello\")", 60000);