// Port of cmd/w6c/main.c. package main; import os; import rt; import strings; import syntax; import check; import cgen; import wwi; fn cstreq(a: *u8, lit: str) bool = { let n: u64 = lit.len: u64; let i: u64 = 0u64; for (i < n) { let li: i32 = i: i32; if (a[i] != lit[li]) { return false; }; i += 1u64; }; if (a[i] != 0u8) { return false; }; return true; }; fn cstrlen(p: *u8) u64 = { let n: u64 = 0u64; for (p[n] != 0u8) { n += 1u64; }; return n; }; // pathstr — view a NUL-terminated *u8 as a str. lib/os entrypoints // take str post-task-#23; this bridges call sites that still hold // C-string paths (argv entries, arena-allocated buffers). fn pathstr(p: *u8) str = { let r: str; r.ptr = p; r.len = cstrlen(p): i32; return r; }; fn slurp(path: *u8) (*u8, u64) = { let fd: i32 = os.open(pathstr(path), os.flag.RDONLY, 0i32); if (fd < 0) { return nil, 0u64; }; let szr: (i64 | os.oserror) = os.filesize(fd); let n: i64 = 0i64; match (szr) { case let v: i64 => n = v; case let e: os.oserror => { os.close(fd); return nil, 0u64; }; }; let nz: u64 = n: u64; let buf: []u8 = alloc([], nz + 1u64)!; buf.len = (nz + 1u64): i32; let rr: (i64 | os.oserror) = os.readall(fd, buf.ptr, nz); os.close(fd); let got: i64 = 0i64; match (rr) { case let v: i64 => got = v; case let e: os.oserror => return nil, 0u64; }; if (got != n) { return nil, 0u64; }; buf[nz] = 0u8; return buf.ptr, nz; }; export fn main(argc: i32, argv: **u8) i32 = { let src: *u8 = nil; let out: *u8 = nil; let wwiout: *u8 = nil; // -I : M2 export-data producer let testmode: i32 = 0i32; // #15: `-T` test-mode let sepmode: i32 = 0i32; // -c: #22 M3 separate-compile / primary- // only codegen (emit imported==0 decls // only; treat `.wwi` deps as external) let i: i32 = 1; for (i < argc) { let a: *u8 = argv[i]; if (cstreq(a, "-o")) { i += 1; if (i >= argc) { let m: str = "w6c: -o requires arg\n"; os.write(2, m.ptr, m.len: u64); return 2; }; out = argv[i]; } else { if (cstreq(a, "-I")) { i += 1; if (i >= argc) { let m: str = "w6c: -I requires arg\n"; os.write(2, m.ptr, m.len: u64); return 2; }; wwiout = argv[i]; } else { if (cstreq(a, "-T")) { testmode = 1i32; } else { if (cstreq(a, "-c")) { sepmode = 1i32; } else { if (a[0u64] == 45u8) { let m: str = "w6c: unknown flag\n"; os.write(2, m.ptr, m.len: u64); return 2; } else { if (src != nil) { let m: str = "w6c: only one input\n"; os.write(2, m.ptr, m.len: u64); return 2; }; src = a; }; }; }; }; }; i += 1; }; if (src == nil) { let m: str = "usage: w6c_ww [-T] [-o out.s] file.ww\n"; os.write(2, m.ptr, m.len: u64); return 2; }; let buf: *u8; let blen: u64; buf, blen = slurp(src); if (buf == nil) { let m: str = "w6c: cannot read input\n"; os.write(2, m.ptr, m.len: u64); return 1; }; // Redirect fd 1 to the output file before any cgen emit runs. // cgen.ww writes directly to fd 1; dup2 lets us reuse it without // threading a file descriptor through the emit helpers. if (out != nil) { let ofd: i32 = os.open(pathstr(out), os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 420i32); // 0o644 if (ofd < 0) { let m: str = "w6c: cannot open output\n"; os.write(2, m.ptr, m.len: u64); return 1; }; if (os.dup2(ofd, 1i32) < 0) { let m: str = "w6c: dup2 failed\n"; os.write(2, m.ptr, m.len: u64); os.close(ofd); return 1; }; os.close(ofd); }; let nlen: u64 = cstrlen(src); let view: str; view.ptr = src; view.len = nlen: i32; let fname: str = strings.dup(view); let l: lex; lexinit(&l, fname, buf, blen); let ps: parser; parserinit(&ps, &l); let f: *node = parsefile(&ps); // Gate cgen on parse-stage errors. Mirrors cmd/w6c/main.c's // `if (l.errs || p.errs) return 1;` — broken AST otherwise reaches // cgen and emits junk asm with a zero exit (silent miscompile). if (l.errs > 0 || ps.errs > 0) { return 1; }; // #50: run check before cgen so AST mutations from #42 (size/align/ // offset fold) and the audit §1.8 node.type_ population land before // cgen walks the file. Mirrors cmd/w6c/main.c:73-75. Five precondition // fixes for fixture cleanliness: #51 cross-module type refs, #52 // enum↔int reinterpret, #53 N_BLOCK scoping, #55 nominal-first variant // compare, #56 bare-leaf same-module preference. let tc: tctx; typesinit(&tc); let ck: checker; checkinit(&ck, &tc); ck.istest = testmode; checkfile(&ck, f); if (ck.errs > 0) { return 1; }; // M2 export-data: write the `.wwi` after a clean check, before cgen. // Dead on the live path (no existing invocation passes -I); the // producer's check_exported_type may reject a dangling export. if (wwiout != nil) { if (wwiemit(&ck, f, pathstr(wwiout)) != 0) { return 1; }; }; let cg: cgen; cgeninit(&cg); cg.sepmode = sepmode; // #99: wwiout != nil <=> this is a sep DEP unit (the producer passes // -I to deps only; the root's .wwi is stripped per #69). Gates the // bare-`main` carve-out so only the root/link-entry main stays bare. if (wwiout != nil) { cg.sepisdep = 1i32; }; cgfile(&cg, f); return 0; };