// Port of cmd/wwdump/main.c. The 990_selfhost test diffs token 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. package main; import os; import syntax; import wcc; import strconv; // argv strings are always NUL-terminated (kernel-supplied), so the // unbounded scan 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; }; fn streqlit(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; }; 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 (streqlit(a, "-t")) { mode = 116; } else { if (streqlit(a, "-a")) { mode = 97; // 'a' } else { if (streqlit(a, "-r")) { mode = 114; // 'r' — resolve / name-check } else { if (streqlit(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 fdorerr: (i32 | os.oserror) = os.tryopen(argstr(path), os.flag.RDONLY, 0i32); let fd: i32 = -1; match (fdorerr) { case let v: i32 => fd = v; case let e: os.oserror => { os.write(2, "wwdump: cannot open ".ptr, 20u64); os.write(2, path, argstrlen(path): u64); os.write(2, "\n".ptr, 1u64); return 1; }; }; let szr: (i64 | os.oserror) = os.filesize(fd); let sz: i64 = 0i64; match (szr) { case let v: i64 => sz = v; case let e: os.oserror => { os.write(2, "wwdump: filesize failed\n".ptr, 24u64); os.close(fd); return 1; }; }; let buf: []u8 = alloc([], sz: u64)!; buf.len = sz: i32; let rr: (i64 | os.oserror) = os.readall(fd, buf.ptr, sz: u64); os.close(fd); let r: i64 = 0i64; match (rr) { case let v: i64 => r = v; case let e: os.oserror => { os.write(2, "wwdump: read failed\n".ptr, 20u64); return 1; }; }; if (r != sz) { os.write(2, "wwdump: short read\n".ptr, 19u64); return 1; }; let l: lex; lexinit(&l, argstr(path), buf.ptr, sz: u64); if (mode == 116) { // '-t' for (true) { let t: tok; lexnext(&l, &t); tokprint(1i32, &t); if (t.kind == tkind.TK_EOF) { break; }; if (t.kind == tkind.TK_ERR) { break; }; }; } else { if (mode == 97) { // '-a' let ps: parser; parserinit(&ps, &l); let f: *node = parsefile(&ps); astprint(1i32, f); if (ps.errs > 0) { return 1; }; } else { if (mode == 114) { // '-r' — name resolve report let ps: parser; parserinit(&ps, &l); let f: *node = parsefile(&ps); // #52: gate the resolve report on parse-stage errors. Without // this a parse-errored decl is silently dropped from the AST // and the report is printed with rc=0. Mirrors w6c main.ww:162 // / cmd/w6c/main.c. if (l.errs > 0 || ps.errs > 0) { return 1; }; let nresolved: i32 = 0; let nunresolved: i32 = 0; let checkrc: i32 = wcc.resolvefile(f, 0, &nresolved, &nunresolved); // ": / resolved" os.write(1, argstr(path).ptr, argstrlen(path): u64); os.write(1, ": ".ptr, 2u64); let rs: str = strconv.i64tos(nresolved: i64, strconv.base.DEC); os.write(1, rs.ptr, rs.len: u64); os.write(1, "/".ptr, 1u64); let total: i32 = nresolved + nunresolved; let ts: str = strconv.i64tos(total: i64, strconv.base.DEC); os.write(1, ts.ptr, ts.len: u64); os.write(1, " resolved\n".ptr, 10u64); if (checkrc != 0) { return 1; }; } else { if (mode == 99) { // '-c' — codegen / emit asm let ps: parser; parserinit(&ps, &l); let f: *node = parsefile(&ps); // #52: gate cgen on parse-stage errors BEFORE check/cgen. // A parse-errored decl is silently dropped from the AST; the // remaining file would otherwise emit asm with rc=0 (silent // miscompile, and the 994 byte-identity probe ships wrong asm // silently). Mirrors w6c main.ww:162 / cmd/w6c/main.c. if (l.errs > 0 || ps.errs > 0) { return 1; }; let empty: str; if (wcc.compilefile(f, 0, 0, empty, empty, 0, empty, 0) != 0) { return 1; }; };};};}; if (l.errs > 0) { return 1; }; return 0; };