The Compiler
c-program

Write c_program() -- the top-level function. Three passes: scan globals, compile functions, compile main.

fn c_program(input: []const u8) void {
    out_len = 0;
    c_global_count = 0;

    // Pass 1: scan for top-level globals
    source = input; pos = 0; skip();
    var depth: i32 = 0;
    while (pos < source.len) {
        if (source[pos] == '{') { depth += 1; pos += 1; continue; }
        if (source[pos] == '}') { depth -= 1; pos += 1; continue; }
        if (depth == 0 and is_letter(cur())) {
            const s: usize = pos;
            const w: []const u8 = read_name();
            if (streq(w, "var") or streq(w, "const")) {
                const vn: []const u8 = read_name();
                if (cur() == ':') { pos += 1; skip(); _ = read_name(); }
                c_global_names[c_global_count] = vn;
                c_global_count += 1;
            } else if (streq(w, "fn")) {
                while (cur() != '{' and cur() != 0) { pos += 1; }
                if (cur() == '{') { skip_block(); }
            } else {
                pos = s; pos += 1;
            }
        } else { pos += 1; }
    }

    // Emit globals
    for (0..c_global_count) |i| {
        emit_str("(global $");
        emit_str(c_global_names[i]);
        emit_str(" (mut i64) (i64.const 0))\n");
    }

    // Pass 2: compile function definitions
    source = input; pos = 0; skip();
    while (cur() != 0) {
        if (is_letter(cur())) {
            const s: usize = pos;
            const w: []const u8 = read_name();
            if (streq(w, "fn")) { pos = s; _ = c_stmt(); continue; }
            pos = s;
        }
        if (cur() == '{') { skip_block(); } else { pos += 1; }
    }

    // Pass 3: compile top-level code into main
    emit_str("(func (export \"main\") (result i64)\n");

    // Scan for top-level locals
    source = input; pos = 0; skip();
    local_count = 0;
    depth = 0;
    while (pos < source.len) {
        if (source[pos] == '{') { depth += 1; pos += 1; continue; }
        if (source[pos] == '}') { depth -= 1; pos += 1; continue; }
        if (depth == 0 and is_letter(cur())) {
            const s: usize = pos;
            const w: []const u8 = read_name();
            if (streq(w, "var") or streq(w, "const")) {
                const vn: []const u8 = read_name();
                if (cur() == ':') { pos += 1; skip(); _ = read_name(); }
                if (!c_is_global(vn)) {
                    var dup: bool = false;
                    for (0..local_count) |i| { if (streq(local_names[i], vn)) { dup = true; break; } }
                    if (!dup) { local_names[local_count] = vn; local_count += 1; }
                }
            } else if (streq(w, "fn")) {
                while (cur() != '{' and cur() != 0) { pos += 1; }
                if (cur() == '{') { skip_block(); }
            } else { pos = s; pos += 1; }
        } else { pos += 1; }
    }
    emit_locals();

    // Compile top-level statements (skip fn definitions)
    source = input; pos = 0; skip();
    var last_had_value: bool = false;
    while (cur() != 0) {
        if (is_letter(cur())) {
            const s: usize = pos;
            const w: []const u8 = read_name();
            if (streq(w, "fn")) {
                while (cur() != '{' and cur() != 0) { pos += 1; }
                skip_block();
                continue;
            }
            pos = s;
        }
        if (last_had_value) { emit_op("drop"); }
        last_had_value = c_stmt();
    }
    if (!last_had_value) { emit_const(0); }

    emit_str(")\n");
}