// selfhost/cmd/wwdump/main.ww — ww-side port of cmd/wwdump/main.c. // // Reads a .ww file, runs the ww-side lexer, prints tokens through // the ww-side tokprint. The 990_selfhost test diffs this output // byte-for-byte against the C-side wwdump on the same file. Any // divergence is a port bug in lex.ww or tok.ww. // // Modes: // wwdump -t file.ww tokens (default) // wwdump -a file.ww AST (not yet implemented; reserved) use os; use mem; use tok; use lex; use ast; use parse; use typ; use sym; use check; use cgen; use strconv; // ---- argv helpers ----------------------------------------------------- // argstrlen — strlen on a NUL-terminated *u8. argv strings are always // NUL-terminated (kernel-supplied) so this is safe. fn argstrlen(s: *u8) i32 = { let n: i32 = 0; for (s[n] != 0u8) { n += 1; }; return n; }; fn argstr(p: *u8) str = { let s: str; s.ptr = p; s.len = argstrlen(p); return s; }; // streq_lit — compare a NUL-terminated argv entry to a string literal. fn streq_lit(p: *u8, lit: str) bool = { let i: i32 = 0; for (i < lit.len) { if (p[i] != lit[i]) { return false; }; i += 1; }; return p[i] == 0u8; }; // ---- main ------------------------------------------------------------- export fn main(argc: i32, argv: **u8) i32 = { let mode: i32 = 116; // 't' let path: *u8 = nil; let i: i32 = 1; for (i < argc) { let a: *u8 = argv[i]; if (streq_lit(a, "-t")) { mode = 116; } else { if (streq_lit(a, "-a")) { mode = 97; // 'a' } else { if (streq_lit(a, "-r")) { mode = 114; // 'r' — resolve / name-check } else { if (streq_lit(a, "-c")) { mode = 99; // 'c' — codegen / emit asm } else { if (path == nil) { path = a; };};};};}; i += 1; }; if (path == nil) { os.write(2, "usage: wwdump [-t|-a] file.ww\n".ptr, 30u64); return 2; }; let fd_or_err: (i32 | str) = os.tryopen(path, os.O_RDONLY, 0i32); let fd: i32 = -1; match (fd_or_err) { case let v: i32 => fd = v; case let e: str => { os.write(2, "wwdump: cannot open ".ptr, 20u64); os.write(2, path, argstrlen(path): u64); os.write(2, "\n".ptr, 1u64); return 1; }; }; let sz: i64 = os.filesize(fd); if (sz < 0i64) { os.write(2, "wwdump: filesize failed\n".ptr, 24u64); os.close(fd); return 1; }; let a: *arena = newarena(); let buf: *u8 = amalloc(a, sz: u64): *u8; let r: i64 = os.readfull(fd, buf, sz: u64); os.close(fd); if (r != sz) { os.write(2, "wwdump: short read\n".ptr, 19u64); return 1; }; let l: lex; lexinit(&l, a, argstr(path), buf, sz: u64); if (mode == 116) { // '-t' for (true) { let t: tok; lexnext(&l, &t); tokprint(1i32, &t); if (t.kind == TK_EOF) { break; }; if (t.kind == TK_ERR) { break; }; }; } else { if (mode == 97) { // '-a' let ps: parser; parserinit(&ps, a, &l); let f: *node = parsefile(&ps); astprint(1i32, f); } else { if (mode == 114) { // '-r' — name resolve report let ps: parser; parserinit(&ps, a, &l); let f: *node = parsefile(&ps); let tc: tctx; typesinit(&tc, a); let ck: checker; check_init(&ck, a, &tc); // Quiet by default; flip to 1 when debugging missing names. ck.verbose = 0; check_file(&ck, f); // (close out the if-else chain — we'll close all braces below) // ": / resolved" os.write(1, argstr(path).ptr, argstrlen(path): u64); os.write(1, ": ".ptr, 2u64); let buf: [32]u8; let n: i32 = strconv.i64toa(buf[0:32], ck.nresolved: i64); os.write(1, buf.ptr, n: u64); os.write(1, "/".ptr, 1u64); let total: i32 = ck.nresolved + ck.nunresolved; n = strconv.i64toa(buf[0:32], total: i64); os.write(1, buf.ptr, n: u64); os.write(1, " resolved\n".ptr, 10u64); if (ck.nunresolved > 0) { return 1; }; } else { if (mode == 99) { // '-c' — codegen / emit asm let ps: parser; parserinit(&ps, a, &l); let f: *node = parsefile(&ps); let cg: cgen; cgen_init(&cg, a); cg_file(&cg, f); };};};}; if (l.errs > 0) { return 1; }; return 0; };