// selfhost/test/smoke.ww — end-to-end smoke for the selfhost path. // // Exercises the patterns the real ww-side compiler port will use: // - bump arena allocator (mem.ww shape) // - error idiom (T | str) // - struct of fn pointers + ctx pointer (the io.stream-style // polymorphism we use instead of interfaces) // - byte-level scanning that mirrors the hot path inside lex.ww // - strconv round-trip via the real stdlib // // `main` returns 42 when every check passes, 1..N on failure // indicating which probe broke. The 990_selfhost test asserts 42. // // Note: only stack-local mutable state. Top-level `let` mutation // requires a writable .data segment in w6l, which is a separate // task; until then we exercise polymorphism via ctx pointers, which // is what the real port wants anyway. use os; use strconv; use ascii; // --- bump arena --------------------------------------------------------- type arena = struct { buf: *u8, off: u64, cap: u64, }; // In-place init. Returning a 24-byte struct by value isn't yet // supported in w6c (SysV requires a hidden return-slot pointer for // structs >16 bytes), so we initialize through a pointer like the // real compiler does today. fn arena_init(a: *arena, buf: *u8, cap: u64) void = { a.buf = buf; a.off = 0u64; a.cap = cap; }; fn arena_alloc(a: *arena, n: u64) *u8 = { if (n > a.cap - a.off) { return nil; }; let p: *u8 = a.buf + a.off; a.off += n; return p; }; // --- (i32 | str) error idiom ------------------------------------------- fn checked_div(num: i32, den: i32) (i32 | str) = { if (den == 0) { return "div by zero"; }; return num / den; }; // --- struct-of-fn-pointer polymorphism --------------------------------- // // A trivial "writer" abstraction: a function pointer plus a context. // This mirrors how io.stream / Plan 9 Bio work. The ctx pointer lets // the implementation own its own state without a global. type counter = struct { n: i32, }; type writer = struct { ctx: *void, emit: fn(ctx: *void, b: u8) void, }; fn count_emit(ctx: *void, b: u8) void = { let c: *counter = ctx: *counter; c.n += 1; }; // --- byte scanner like lex.ww's hot path ------------------------------- fn count_digits(s: str) i32 = { let i: i32 = 0; let n: i32 = 0; for (i < s.len) { let c: u8 = s[i]; if (c >= 48u8) { if (c <= 57u8) { n += 1; }; }; i += 1; }; return n; }; // --- entry -------------------------------------------------------------- export fn main() i32 = { // Probe 1 — arena hands out distinct pointers, refuses oversize. let buf: [256]u8; let a: arena; arena_init(&a, buf.ptr, 256u64); let p1: *u8 = arena_alloc(&a, 32u64); let p2: *u8 = arena_alloc(&a, 32u64); if (p1 == nil) { return 1; }; if (p2 == nil) { return 2; }; if (p1 == p2) { return 3; }; let p3: *u8 = arena_alloc(&a, 1024u64); if (p3 != nil) { return 4; }; // Probe 2 — error union both ways. let r_ok: (i32 | str) = checked_div(84, 2); let r_bad: (i32 | str) = checked_div(1, 0); let acc: i32 = 0; match (r_ok) { case let v: i32 => acc = v; case let e: str => return 5; }; if (acc != 42) { return 6; }; match (r_bad) { case let v: i32 => return 7; case let e: str => acc = e.len: i32; }; if (acc != 11) { return 8; }; // len("div by zero") == 11 // Probe 3 — struct-of-fn-pointer dispatch via ctx pointer. let c: counter = counter { n = 0 }; let w: writer = writer { ctx = (&c): *void, emit = count_emit }; w.emit(w.ctx, 65u8); w.emit(w.ctx, 66u8); w.emit(w.ctx, 67u8); if (c.n != 3) { return 9; }; // Probe 4 — byte scan over a literal. let dn: i32 = count_digits("ww123abc"); if (dn != 3) { return 10; }; // Probe 5 — strconv round-trip via the real stdlib. let outbuf: [32]u8; let nb: i32 = strconv.i64toa(outbuf[0:32], 4242i64); if (nb != 4) { return 11; }; if (outbuf[0] != 52u8) { return 12; }; // '4' if (outbuf[3] != 50u8) { return 13; }; // '2' // Probe 6 — ascii classifications. if (!ascii.isdigit(53u8)) { return 14; }; // '5' if (ascii.isdigit(65u8)) { return 15; }; // 'A' is not a digit if (!ascii.isalpha(122u8)) { return 16; }; // 'z' if (!ascii.isidstart(95u8)) { return 17; }; // '_' if (!ascii.isidpart(48u8)) { return 18; }; // '0' is part if (ascii.digitval(70u8) != 15) { return 19; }; // 'F' = 15 if (ascii.tolower(65u8) != 97u8) { return 20; }; // 'A' -> 'a' // Probe 7 — file open/read via the new os APIs. /proc/self/cmdline // always exists on Linux, no write side, and is non-empty. let path: str = "/proc/self/cmdline"; let fd_or_err: (i32 | str) = os.tryopen(path.ptr, os.O_RDONLY, 0i32); let fd: i32 = 0; match (fd_or_err) { case let v: i32 => fd = v; case let e: str => return 21; }; let rbuf: [128]u8; let n: i64 = os.readfull(fd, rbuf.ptr, 128u64); os.close(fd); if (n <= 0i64) { return 22; }; return 42; };