/* * 700_e2e — end-to-end. Drive `ww build` on a small source program, * run the produced binary, check the exit status. This is the real * user-facing happy path. */ #include #include #include #include #include #include static int runwait(const char *cmd) { int rc = system(cmd); if (rc == -1) return -1; if (WIFEXITED(rc)) return WEXITSTATUS(rc); return 1; } struct row { const char *src; int want_exit; }; static const struct row rows[] = { { "fn main() i32 = { return 42; };", 42 }, { "fn add(a: i32, b: i32) i32 = { return a + b; };\n" "fn main() i32 = { return add(7, 35); };", 42 }, { "fn main() i32 = {\n" " let i: i32 = 0;\n" " let s: i32 = 0;\n" " for (i < 10) { s += i; i += 1; };\n" " return s;\n" "};", 45 }, { "fn main() i32 = {\n" " let x: i32 = 100;\n" " if (x > 50) { return 1; };\n" " return 0;\n" "};", 1 }, { "fn main() i32 = {\n" " let a: i32 = 6;\n" " let b: i32 = 7;\n" " return a * b;\n" "};", 42 }, /* multi-return tuple, divmod */ { "fn divmod(a: i64, b: i64) (i64, i64) = { return a / b, a % b; };\n" "fn main() i32 = {\n" " let q, r = divmod(17, 5);\n" " return (q + r): i32;\n" "};", 5 }, /* fixed array, byte-wise read/write */ { "fn main() i32 = {\n" " let buf: [4]u8;\n" " buf[0] = 1: u8;\n" " buf[1] = 2: u8;\n" " buf[2] = 3: u8;\n" " buf[3] = 4: u8;\n" " let sum: i32 = 0;\n" " let i: i32 = 0;\n" " for (i < 4) { sum += buf[i]: i32; i += 1; };\n" " return sum;\n" "};", 10 }, /* float: arg, arith, literal, cast back to int */ { "fn area(r: f64) f64 = { return 3.14 * r * r; };\n" "fn main() i32 = { let a: f64 = area(5.0); return a: i32; };", 78 }, /* float comparison: must emit UCOMISD + JA (not CMPQ + JG) */ { "fn main() i32 = {\n" " let a: f64 = 1.5;\n" " let b: f64 = 2.5;\n" " if (a < b) { if (b > a) { return 7; }; };\n" " return 0;\n" "};", 7 }, /* float ==/!= via UCOMISD */ { "fn main() i32 = {\n" " let a: f64 = 3.14;\n" " let b: f64 = 3.14;\n" " if (a == b) { return 11; };\n" " return 0;\n" "};", 11 }, /* function pointer: take address of a named fn, call indirectly */ { "fn add(a: i32, b: i32) i32 = { return a + b; };\n" "fn main() i32 = {\n" " let fp: fn(a: i32, b: i32) i32 = add;\n" " return fp(20, 22);\n" "};", 42 }, /* string literal via syscall — exit code = bytes written */ { "@symbol(\"rt_syscall\") fn rt_syscall(num: i64, a: i64, b: i64, c: i64) i64;\n" "fn print(s: str) i64 = { return rt_syscall(1, 1, s.ptr: i64, s.len: i64); };\n" "fn main() i32 = { return print(\"hello, world\\n\"): i32; };", 13 }, /* 9 args — 3 spill to the stack */ { "fn s9(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32, i: i32) i32 = {\n" " return a + b + c + d + e + f + g + h + i;\n" "};\n" "fn main() i32 = { return s9(1,2,3,4,5,6,7,8,9); };", 45 }, /* defer: LIFO at function return */ { "@symbol(\"rt_syscall\") fn rt_syscall(num: i64, a: i64, b: i64, c: i64) i64;\n" "fn out(c: i32) void = { rt_syscall(1, 1, (&c): i64, 1); };\n" "fn main() i32 = {\n" " let c1: i32 = 0;\n" " let c2: i32 = 0;\n" " c1 = 65;\n" /* 'A' */ " c2 = 66;\n" /* 'B' */ " defer out(c1);\n" " defer out(c2);\n" " return 0;\n" "};", 0 }, /* struct-by-value: 16B all-int passed by value */ { "type pair = struct { a: i64, b: i64 };\n" "fn sum(p: pair) i64 = { return p.a + p.b; };\n" "fn main() i32 = {\n" " let p: pair = pair { a = 10, b = 32 };\n" " return sum(p): i32;\n" "};", 42 }, /* f32 cast + arithmetic */ { "fn add32(a: f32, b: f32) f32 = { return a + b; };\n" "fn main() i32 = {\n" " let r: f32 = add32(2.5: f32, 7.5: f32);\n" " return r: i32;\n" "};", 10 }, /* slice from array: build header, iterate via index/len */ { "fn main() i32 = {\n" " let arr: [4]u8;\n" " arr[0] = 10: u8; arr[1] = 20: u8;\n" " arr[2] = 30: u8; arr[3] = 99: u8;\n" " let s: []u8 = arr[0:3];\n" " let sum: i32 = 0;\n" " let i: i32 = 0;\n" " for (i < s.len) { sum += s[i]: i32; i += 1; };\n" " return sum;\n" "};", 60 }, /* module imports: use os and call os.write; exit code = bytes */ { "use os;\n" "fn main() i32 = { return os.write(1, \"ok\\n\".ptr, 3): i32; };", 3 }, /* typed integer literals */ { "fn main() i32 = {\n" " let buf: [4]u8;\n" " buf[0] = 65u8; buf[1] = 66u8; buf[2] = 67u8; buf[3] = 0u8;\n" " let s: i32 = 0;\n" " let i: i32 = 0;\n" " for (i < 3) { s += buf[i]: i32; i += 1; };\n" " return s;\n" "};", 198 }, /* full stdlib stack: use os + strconv, slice-arg call, write * the formatted number to stdout. exit code = number length. */ { "use os;\n" "use strconv;\n" "fn main() i32 = {\n" " let buf: [32]u8;\n" " let s: []u8 = buf[0:32];\n" " let n: i32 = strconv.i64toa(s, 12345);\n" " os.write(1, buf.ptr, n: u64);\n" " os.write(1, \"\\n\".ptr, 1u64);\n" " return n;\n" "};", 5 }, /* alloc + free via mmap-backed runtime — write through allocated * memory and free it. exit = 0 if the allocation succeeded. */ { "use os;\n" "fn main() i32 = {\n" " let p: *void = os.alloc(4096u64);\n" " if (p == nil) { return 1; };\n" " let bp: *u8 = p: *u8;\n" " bp[0] = 65u8;\n" " os.write(1, bp, 1u64);\n" " os.free(p, 4096u64);\n" " return 0;\n" "};", 0 }, /* argv: kernel passes argc in DI, argv in SI. */ { "fn main(argc: i32, argv: **u8) i32 = { return argc; };", 1 }, /* i64 array: scaled indexing (elem size 8) */ { "fn main() i32 = {\n" " let arr: [4]i64;\n" " arr[0] = 10; arr[1] = 20; arr[2] = 30; arr[3] = 40;\n" " let sum: i64 = 0;\n" " let i: i32 = 0;\n" " for (i < 4) { sum += arr[i]; i += 1; };\n" " return sum: i32;\n" "};", 100 }, /* break out of an infinite loop early */ { "fn main() i32 = {\n" " let i: i32 = 0;\n" " for () {\n" " i += 1;\n" " if (i == 7) { break; };\n" " };\n" " return i;\n" "};", 7 }, /* switch with multi-expr cases + default */ { "fn classify(x: i32) i32 = {\n" " switch (x) {\n" " case 1, 2, 3: return 10;\n" " case 10: return 99;\n" " case: return 50;\n" " };\n" " return -1;\n" "};\n" "fn main() i32 = {\n" " return classify(2) + classify(10) + classify(99);\n" "};", 159 }, /* 10+99+50=159 */ /* fmt module: stdlib formatter for ints + strings */ { "use fmt;\n" "fn main() i32 = {\n" " fmt.println(\"ww\");\n" " fmt.printlnint(42);\n" " fmt.printlnint(-7);\n" " return 0;\n" "};", 0 }, /* struct with i32 fields: MOVL/MOVSXD avoids clobbering neighbors */ { "type point = struct { x: i32, y: i32 };\n" "fn distsq(p: point) i32 = { return p.x * p.x + p.y * p.y; };\n" "fn main() i32 = {\n" " let p: point = point { x = 3, y = 4 };\n" " return distsq(p);\n" "};", 25 }, /* str equality via == and != */ { "fn main() i32 = {\n" " let a: str = \"hello\";\n" " let b: str = \"hello\";\n" " let c: str = \"world\";\n" " let n: i32 = 0;\n" " if (a == b) { n += 10; };\n" " if (a != c) { n += 20; };\n" " return n;\n" "};", 30 }, /* CLAUDE.md's move pattern: ptr-to-struct compound field write */ { "type point = struct { x: i32, y: i32 };\n" "fn move(p: *point, dx: i32, dy: i32) void = {\n" " p.x += dx; p.y += dy;\n" "};\n" "fn main() i32 = {\n" " let pt: point = point { x = 0, y = 0 };\n" " move(&pt, 3, 4);\n" " return pt.x + pt.y;\n" "};", 7 }, /* vtable polymorphism: struct of fn pointers, indirect call */ { "type ops = struct { add: fn(a: i32, b: i32) i32 };\n" "fn plus(a: i32, b: i32) i32 = { return a + b; };\n" "fn main() i32 = {\n" " let v: ops = ops { add = plus };\n" " return v.add(20, 22);\n" "};", 42 }, /* compound bitwise/shift assigns */ { "fn main() i32 = {\n" " let x: i32 = 100;\n" " x &= 0x3f; x |= 0x80; x ^= 0xc4;\n" " x *= 2; x <<= 1; x >>= 2;\n" " return x;\n" "};", 96 }, /* Hare-style builtins: append(s, v) and len(s). The compiler * lowers append to a CALL into libwwrt.a's appendu8 / appendi64 * by element size — no `use rt;` or `use slices;` required. */ { "use os;\n" "fn main() i32 = {\n" " let s: []u8;\n" " s.ptr = nil; s.len = 0; s.cap = 0;\n" " append(s, 88u8); append(s, 89u8); append(s, 90u8);\n" " os.write(1, s.ptr, len(s): u64);\n" " os.write(1, \"\\n\".ptr, 1u64);\n" " return len(s);\n" "};", 3 }, /* str-returning function: 16-byte return via AX:DX (SysV). The * caller's str slot is filled from those two regs. */ { "use strings;\n" "use fmt;\n" "fn main() i32 = {\n" " let r: str = strings.concat(\"hello, \", \"world\");\n" " fmt.println(r);\n" " return r.len;\n" "};", 12 }, /* variadic append + static qualifier (Hare idiom) */ { "use os;\n" "fn main() i32 = {\n" " let s: []u8;\n" " s.ptr = nil; s.len = 0; s.cap = 0;\n" " static append(s, 72u8, 105u8, 33u8, 10u8);\n" " os.write(1, s.ptr, len(s): u64);\n" " return len(s);\n" "};", 4 }, /* alloc() builtin: heap-allocate a struct, init from struct-lit */ { "use os;\n" "type point = struct { x: i32, y: i32 };\n" "fn main() i32 = {\n" " let p: *point = alloc(point { x = 3, y = 4 });\n" " return p.x * p.x + p.y * p.y;\n" "};", 25 }, /* Hare-style range loop: for (let x .. slice) iterates elements */ { "use os;\n" "fn main() i32 = {\n" " let s: []u8;\n" " s.ptr = nil; s.len = 0; s.cap = 0;\n" " append(s, 10u8, 20u8, 30u8, 40u8);\n" " let total: i32 = 0;\n" " for (let b .. s) { total += b: i32; };\n" " return total;\n" "};", 100 }, /* alloc([], n): fresh empty slice with cap n */ { "use os;\n" "fn main() i32 = {\n" " let s: []u8 = alloc([], 16);\n" " append(s, 72u8, 105u8);\n" " return s.cap;\n" "};", 16 }, /* variadic spread: append(dst, src...) iterates src */ { "use os;\n" "fn main() i32 = {\n" " let src: []u8;\n" " src.ptr = nil; src.len = 0; src.cap = 0;\n" " append(src, 65u8, 66u8, 67u8);\n" " let dst: []u8;\n" " dst.ptr = nil; dst.len = 0; dst.cap = 0;\n" " append(dst, src...);\n" " os.write(1, dst.ptr, dst.len: u64);\n" " os.write(1, \"\\n\".ptr, 1u64);\n" " return dst.len;\n" "};", 3 }, /* Hare-style tuple destructure in let */ { "fn divmod(a: i64, b: i64) (i64, i64) = { return a / b, a % b; };\n" "fn main() i32 = {\n" " let (q, r) = divmod(17, 5);\n" " return (q + r): i32;\n" "};", 5 }, /* Hare-style tuple destructure in for-range */ { "fn main() i32 = {\n" " let buf: [4]i64;\n" " buf[0] = 1; buf[1] = 10; buf[2] = 2; buf[3] = 20;\n" " let s: [](i64, i64);\n" " s.ptr = buf.ptr: *(i64, i64);\n" " s.len = 2; s.cap = 2;\n" " let total: i64 = 0;\n" " for (let (k, v) .. s) { total += k + v; };\n" " return total: i32;\n" "};", 33 }, /* Hare-style tuple positional access: t.0, t.1 */ { "fn pair() (i64, i64) = { return 10, 32; };\n" "fn main() i32 = {\n" " let t: (i64, i64) = pair();\n" " return (t.0 + t.1): i32;\n" "};", 42 }, /* Hare-style abort/assert + free() builtin */ { "use os;\n" "type point = struct { x: i64, y: i64 };\n" "fn main() i32 = {\n" " let p: *point = alloc(point { x = 7, y = 35 });\n" " let r: i64 = p.x + p.y;\n" " free(p);\n" " os.assert(r == 42, \"sum mismatch\\n\");\n" " return r: i32;\n" "};", 42 }, /* 3-field tuple destructure in for-range */ { "fn main() i32 = {\n" " let buf: [3]i64;\n" " buf[0] = 5; buf[1] = 7; buf[2] = 30;\n" " let s: [](i64, i64, i64);\n" " s.ptr = buf.ptr: *(i64, i64, i64);\n" " s.len = 1; s.cap = 1;\n" " let total: i64 = 0;\n" " for (let (a, b, c) .. s) { total += a + b + c; };\n" " return total: i32;\n" "};", 42 }, /* Hare-style tagged union + match */ { "fn parse(n: i64) (i64 | i32) = {\n" " if (n < 0) { return 1: i32; };\n" " return n;\n" "};\n" "fn main() i32 = {\n" " let r: (i64 | i32) = parse(40);\n" " let s: i64 = 0;\n" " match (r) {\n" " case let v: i64 => s = v;\n" " case let e: i32 => s = -1;\n" " };\n" " return (s + 2): i32;\n" "};", 42 }, /* ? propagation up the stack */ { "fn try1(n: i64) (i64 | i32) = {\n" " if (n < 0) { return 99: i32; };\n" " return n;\n" "};\n" "fn try2(n: i64) (i64 | i32) = {\n" " let v: i64 = try1(n)?;\n" " return v + 100;\n" "};\n" "fn main() i32 = {\n" " let r: (i64 | i32) = try2(-1);\n" " let s: i64 = 0;\n" " match (r) {\n" " case let v: i64 => s = v;\n" " case let e: i32 => s = e: i64;\n" " };\n" " return s: i32;\n" "};", 99 }, /* match cases written in reverse variant order — dispatch must * use the variant tag, not the case position */ { "fn parse(n: i64) (i64 | i32) = {\n" " if (n < 0) { return 7: i32; };\n" " return n;\n" "};\n" "fn main() i32 = {\n" " let r: (i64 | i32) = parse(-1);\n" " match (r) {\n" " case let e: i32 => return e;\n" " case let v: i64 => return (v + 1000): i32;\n" " };\n" " return 0;\n" "};", 7 }, /* let-init from a bare variant value: tag must be synthesised */ { "fn main() i32 = {\n" " let r: (i64 | i32) = 7: i32;\n" " match (r) {\n" " case let v: i64 => return 1;\n" " case let e: i32 => return e;\n" " };\n" " return 0;\n" "};", 7 }, /* let-init from an untyped literal: variant inclusion must let * the assignability check through, and the default variant wins */ { "fn main() i32 = {\n" " let r: (i64 | i32) = 5;\n" " match (r) {\n" " case let v: i64 => return v: i32;\n" " case let e: i32 => return 99;\n" " };\n" " return 0;\n" "};", 5 }, /* assignment to a tagged-union local: same tag synthesis */ { "fn main() i32 = {\n" " let r: (i64 | i32) = 0;\n" " r = 9: i32;\n" " match (r) {\n" " case let v: i64 => return 1;\n" " case let e: i32 => return e;\n" " };\n" " return 0;\n" "};", 9 }, /* default arm `case =>` */ { "fn parse(n: i64) (i64 | i32) = {\n" " if (n < 0) { return 1: i32; };\n" " return n;\n" "};\n" "fn main() i32 = {\n" " let r: (i64 | i32) = parse(-1);\n" " match (r) {\n" " case let v: i64 => return 1;\n" " case => return 7;\n" " };\n" " return 0;\n" "};", 7 }, /* `case T =>` without binding still dispatches by tag */ { "fn parse(n: i64) (i64 | i32) = {\n" " if (n < 0) { return 1: i32; };\n" " return n;\n" "};\n" "fn main() i32 = {\n" " let r: (i64 | i32) = parse(40);\n" " match (r) {\n" " case i32 => return 1;\n" " case let v: i64 => return v: i32;\n" " };\n" " return 0;\n" "};", 40 }, /* str-typed variant payload: let-init with a string literal, * match-binding loads ptr+len from the slot */ { "fn main() i32 = {\n" " let r: (i64 | str) = \"hello, world\";\n" " match (r) {\n" " case let n: i64 => return 1;\n" " case let s: str => return s.len: i32;\n" " };\n" " return 0;\n" "};", 12 }, /* assigning a string into a tagged-union local */ { "fn main() i32 = {\n" " let r: (i64 | str) = 0;\n" " r = \"abc\";\n" " match (r) {\n" " case let n: i64 => return 1;\n" " case let s: str => return s.len: i32;\n" " };\n" " return 0;\n" "};", 3 }, /* fn returning (T | str) — wide return ABI */ { "fn parse(n: i64) (i64 | str) = {\n" " if (n < 0) { return \"negative number\"; };\n" " return n;\n" "};\n" "fn main() i32 = {\n" " let r: (i64 | str) = parse(-1);\n" " match (r) {\n" " case let n: i64 => return 1;\n" " case let s: str => return s.len: i32;\n" " };\n" " return 0;\n" "};", 15 }, /* ? propagating a str-typed error all the way up */ { "fn try1(n: i64) (i64 | str) = {\n" " if (n < 0) { return \"fail\"; };\n" " return n;\n" "};\n" "fn try2(n: i64) (i64 | str) = {\n" " let v: i64 = try1(n)?;\n" " return v + 100;\n" "};\n" "fn main() i32 = {\n" " let r: (i64 | str) = try2(-1);\n" " match (r) {\n" " case let n: i64 => return n: i32;\n" " case let s: str => return s.len: i32;\n" " };\n" " return 0;\n" "};", 4 }, /* ? success unwrap when the first variant is itself str */ { "fn make() (str | i64) = {\n" " return \"ok\";\n" "};\n" "fn main() i32 = {\n" " let r: (str | i64) = make();\n" " let v: str = r?;\n" " return v.len: i32;\n" "};", 2 }, /* type error = str; named-alias variant works through the * full happy/error path */ { "type error = str;\n" "fn read(n: i64) (i64 | error) = {\n" " if (n < 0) { return \"eof\": error; };\n" " return n + 1;\n" "};\n" "fn main() i32 = {\n" " let r: (i64 | error) = read(-1);\n" " match (r) {\n" " case let v: i64 => return v: i32;\n" " case let e: error => return e.len: i32;\n" " };\n" " return 0;\n" "};", 3 }, /* multi-pattern arm: `case T1 | T2 =>` matches either tag */ { "fn pick(n: i64) (i64 | i32 | u32) = {\n" " if (n < 0) { return 1: i32; };\n" " if (n == 0) { return 2: u32; };\n" " return n;\n" "};\n" "fn main() i32 = {\n" " let r1: (i64 | i32 | u32) = pick(0);\n" " let r2: (i64 | i32 | u32) = pick(-1);\n" " let r3: (i64 | i32 | u32) = pick(7);\n" " let acc: i32 = 0;\n" " match (r1) {\n" " case let v: i64 => acc += 100;\n" " case i32 | u32 => acc += 1;\n" " };\n" " match (r2) {\n" " case let v: i64 => acc += 100;\n" " case i32 | u32 => acc += 10;\n" " };\n" " match (r3) {\n" " case let v: i64 => acc += v: i32;\n" " case i32 | u32 => acc += 100;\n" " };\n" " return acc;\n" "};", 18 }, /* Named-alias tagged union as fn arg + ≤16B variants */ { "type result = (i64 | i32);\n" "fn unwrap(r: result) i64 = {\n" " match (r) {\n" " case let v: i64 => return v;\n" " case let e: i32 => return e: i64;\n" " };\n" " return -1;\n" "};\n" "fn main() i32 = {\n" " let r1: result = 100;\n" " let r2: result = 7: i32;\n" " return (unwrap(r1) + unwrap(r2)): i32;\n" "};", 107 }, /* 24B tagged-union arg with str variant */ { "type result = (i64 | str);\n" "fn classify(r: result) i32 = {\n" " match (r) {\n" " case let v: i64 => return 1;\n" " case let e: str => return e.len: i32;\n" " };\n" " return -1;\n" "};\n" "fn main() i32 = {\n" " let r1: result = \"hello\";\n" " let r2: result = 42;\n" " return classify(r1) + classify(r2);\n" "};", 6 }, /* Tagged union as struct field — both literal init and assign, * and match-on-field reads from the field's slot in place */ { "type point = struct {\n" " x: i32,\n" " err: (i64 | str),\n" "};\n" "fn main() i32 = {\n" " let p: point = point { x = 1, err = 0 };\n" " p.err = \"updated\";\n" " match (p.err) {\n" " case let v: i64 => return 0;\n" " case let e: str => return e.len: i32;\n" " };\n" " return -1;\n" "};", 7 }, /* Pointer variant in a tagged union */ { "type point = struct { x: i32, y: i32 };\n" "fn main() i32 = {\n" " let p: point = point { x = 3, y = 4 };\n" " let r: (*point | str) = &p;\n" " match (r) {\n" " case let pp: *point => return pp.x + pp.y;\n" " case let e: str => return -1;\n" " };\n" " return 0;\n" "};", 7 }, /* Forwarding `return inner(n)` when both fns share a tagged- * union return type — value passes through unwrapped */ { "type result = (i64 | str);\n" "fn inner(n: i64) result = {\n" " if (n < 0) { return \"neg\"; };\n" " return n + 1;\n" "};\n" "fn outer(n: i64) result = {\n" " return inner(n);\n" "};\n" "fn main() i32 = {\n" " let r: result = outer(-1);\n" " match (r) {\n" " case let v: i64 => return v: i32;\n" " case let e: str => return e.len: i32;\n" " };\n" " return 0;\n" "};", 3 }, /* match directly on a call expression (no intermediate let) */ { "fn make(n: i64) (i64 | str) = {\n" " if (n < 0) { return \"neg\"; };\n" " return n + 1;\n" "};\n" "fn main() i32 = {\n" " match (make(-1)) {\n" " case let v: i64 => return v: i32;\n" " case let e: str => return e.len: i32;\n" " };\n" " return 0;\n" "};", 3 }, /* Plan 9-style sentinel error idiom: `def NAME: error = "lit"` * inlines as the (ptr, len) pair at use sites. */ { "type error = str;\n" "def eEOF: error = \"eof\";\n" "def eShortRead: error = \"short read\";\n" "fn read(n: i64) (i64 | error) = {\n" " if (n < 0) { return eEOF; };\n" " if (n == 0) { return eShortRead; };\n" " return n + 1;\n" "};\n" "fn main() i32 = {\n" " let r0: (i64 | error) = read(0);\n" " let r1: (i64 | error) = read(-1);\n" " let r2: (i64 | error) = read(5);\n" " let acc: i32 = 0;\n" " match (r0) {\n" " case let v: i64 => acc += 100;\n" " case let e: error => acc += e.len: i32;\n" " };\n" " match (r1) {\n" " case let v: i64 => acc += 100;\n" " case let e: error => acc += e.len: i32;\n" " };\n" " match (r2) {\n" " case let v: i64 => acc += v: i32;\n" " case let e: error => acc += 100;\n" " };\n" " return acc;\n" "};", 19 }, /* End-to-end stdlib usage: pull in lib/os and exercise the * fallible API tryread/trywrite returning (i64 | str) over a * real syscall. Validates that imported tagged-union returns * survive the linker as well as the call ABI. */ { "use os;\n" "fn main() i32 = {\n" " let buf: [3]u8;\n" " buf[0] = 88: u8;\n" " let ok: (i64 | str) = os.trywrite(1, buf.ptr, 1u64);\n" " let bad: (i64 | str) = os.trywrite(999: i32, buf.ptr, 1u64);\n" " let acc: i32 = 0;\n" " match (ok) {\n" " case let n: i64 => acc += n: i32;\n" " case let e: str => acc += -100;\n" " };\n" " match (bad) {\n" " case let n: i64 => acc += -100;\n" " case let e: str => acc += e.len: i32;\n" " };\n" " return acc;\n" "};", 13 }, /* 1 byte written to fd 1, plus len(\"write failed\")=12 */ /* strconv.parse64: fallible signed decimal. Two successful * parses contribute their values; one bad parse contributes * the error message length (20 = len(\"parse: invalid digit\")). */ { "use strconv;\n" "fn main() i32 = {\n" " let r1: (i64 | str) = strconv.parse64(\"42\");\n" " let r2: (i64 | str) = strconv.parse64(\"-7\");\n" " let r3: (i64 | str) = strconv.parse64(\"abc\");\n" " let acc: i32 = 0;\n" " match (r1) {\n" " case let v: i64 => acc += v: i32;\n" " case let e: str => acc += -100;\n" " };\n" " match (r2) {\n" " case let v: i64 => acc += v: i32;\n" " case let e: str => acc += -100;\n" " };\n" " match (r3) {\n" " case let v: i64 => acc += -100;\n" " case let e: str => acc += e.len: i32;\n" " };\n" " return acc;\n" "};", 55 }, /* 42 + (-7) + 20 */ /* strconv.parseu64: success path 123, error path captures * len(\"parse: invalid digit\") = 20 for the leading-sign reject. */ { "use strconv;\n" "fn main() i32 = {\n" " let r1: (u64 | str) = strconv.parseu64(\"123\");\n" " let r2: (u64 | str) = strconv.parseu64(\"-1\");\n" " let acc: i32 = 0;\n" " match (r1) {\n" " case let v: u64 => acc += v: i32;\n" " case let e: str => acc += -100;\n" " };\n" " match (r2) {\n" " case let v: u64 => acc += -100;\n" " case let e: str => acc += e.len: i32;\n" " };\n" " return acc;\n" "};", 143 }, /* 123 + 20 */ /* strings.indexbyte (Plan 9 -1) and strings.index (substring). */ { "use strings;\n" "fn main() i32 = {\n" " let s: str = \"hello, world\";\n" " let i1: i32 = strings.indexbyte(s, 44u8);\n" " let i2: i32 = strings.indexbyte(s, 122u8);\n" " let i3: i32 = strings.index(s, \"world\");\n" " let i4: i32 = strings.index(s, \"nope\");\n" " return i1 + i2 + i3 + i4;\n" "};", 10 }, /* 5 + (-1) + 7 + (-1) */ /* bytes.indexsub: substring search over []u8. */ { "use bytes;\n" "fn main() i32 = {\n" " let buf: [12]u8;\n" " buf[0] = 104u8; buf[1] = 101u8; buf[2] = 108u8; buf[3] = 108u8;\n" " buf[4] = 111u8; buf[5] = 44u8; buf[6] = 32u8; buf[7] = 119u8;\n" " buf[8] = 111u8; buf[9] = 114u8; buf[10] = 108u8; buf[11] = 100u8;\n" " let needle: [3]u8;\n" " needle[0] = 119u8; needle[1] = 111u8; needle[2] = 114u8;\n" " return bytes.indexsub(buf[0:12], needle[0:3]);\n" "};", 7 }, /* errors.is — sentinel comparison through a (T | error) union. * Sets up two errors, dispatches each, and confirms the matching * sentinel detection. */ { "use errors;\n" "fn parse(n: i64) (i64 | errors.error) = {\n" " if (n < 0) { return errors.eEOF; };\n" " if (n == 0) { return errors.eShortRead; };\n" " return n;\n" "};\n" "fn main() i32 = {\n" " let r1: (i64 | errors.error) = parse(-1);\n" " let r2: (i64 | errors.error) = parse(0);\n" " let acc: i32 = 0;\n" " match (r1) {\n" " case let v: i64 => acc += -100;\n" " case let e: errors.error =>\n" " if (errors.is(e, errors.eEOF)) { acc += 1; }\n" " else { acc += -100; };\n" " };\n" " match (r2) {\n" " case let v: i64 => acc += -100;\n" " case let e: errors.error =>\n" " if (errors.is(e, errors.eShortRead)) { acc += 10; }\n" " else { acc += -100; };\n" " };\n" " return acc;\n" "};", 11 }, /* bufio.takeline: drain successive '\\n'-terminated lines from a * pre-filled buffer, then a trailing fragment that returns the * `linerr` variant carrying \"no newline\" (10 chars). */ { "use bufio;\n" "fn main() i32 = {\n" " let raw: [11]u8;\n" " raw[0] = 102u8; raw[1] = 111u8; raw[2] = 111u8; raw[3] = 10u8;\n" " raw[4] = 98u8; raw[5] = 97u8; raw[6] = 114u8; raw[7] = 10u8;\n" " raw[8] = 98u8; raw[9] = 97u8; raw[10] = 122u8;\n" " let b: bufio.buf;\n" " b.s = nil; b.data = raw.ptr; b.cap = 11; b.r = 0; b.w = 11;\n" " let acc: i32 = 0;\n" " let l1: (str | bufio.linerr) = bufio.takeline(&b);\n" " match (l1) {\n" " case let s: str => acc += s.len: i32;\n" " case let e: bufio.linerr => acc += -100;\n" " };\n" " let l2: (str | bufio.linerr) = bufio.takeline(&b);\n" " match (l2) {\n" " case let s: str => acc += s.len: i32;\n" " case let e: bufio.linerr => acc += -100;\n" " };\n" " let l3: (str | bufio.linerr) = bufio.takeline(&b);\n" " match (l3) {\n" " case let s: str => acc += -100;\n" " case let e: bufio.linerr => acc += e.len: i32;\n" " };\n" " return acc;\n" "};", 16 }, /* 3 + 3 + len(\"no newline\")=10 */ /* `(scalar, str)` tuple return: AX:DX:CX convention extends the * tagged-union ABI. AX = scalar, DX = str.ptr, CX = str.len. * Receive sites destructure off the same regs regardless of * positional order. Without the fix, len was lost (only AX:DX * returned), every receive shape gave garbage. */ { "fn split() (i64, str) = { return 42, \"hello\"; };\n" "fn main() i32 = {\n" " let n, s = split();\n" " return (n: i32) + (s.len: i32);\n" "};", 47 }, { "fn split() (str, i64) = { return \"hello\", 42; };\n" "fn main() i32 = {\n" " let s, n = split();\n" " return (n: i32) + (s.len: i32);\n" "};", 47 }, { "fn split() (i64, str) = { return 42, \"hello\"; };\n" "fn main() i32 = {\n" " let t: (i64, str) = split();\n" " return (t.0: i32) + (t.1.len: i32);\n" "};", 47 }, /* Hare-style paren tuple-destructure with str element */ { "fn split() (i64, str) = { return 42, \"hello\"; };\n" "fn main() i32 = {\n" " let (n, s) = split();\n" " return (n: i32) + (s.len: i32);\n" "};", 47 }, /* Chained field write through a pointer field: `r.sym.flag = v` * where `r.sym: *T`. The cgen must evaluate the inner pointer, * then store at *(ptr + field.offset). Without the fix, the * single-level N_IDENT-base path doesn't fire (base is itself * an N_DOT) and the assignment silently emits no instructions. * Compound op + 1-byte field + 3-level chain all exercised. */ { "type inner = struct { tag: u8, pad: u8, flag: i32 };\n" "type outer = struct { sym: *inner };\n" "fn main() i32 = {\n" " let i: inner = inner { tag = 0u8, pad = 0u8, flag = 10 };\n" " let r: outer = outer { sym = &i };\n" " r.sym.flag += 32;\n" " r.sym.tag = 5u8;\n" " return r.sym.flag + (r.sym.tag: i32);\n" "};", 47 }, { "type leaf = struct { v: i32 };\n" "type mid = struct { l: *leaf };\n" "type top = struct { m: *mid };\n" "fn main() i32 = {\n" " let lf: leaf = leaf { v = 0 };\n" " let md: mid = mid { l = &lf };\n" " let tp: top = top { m = &md };\n" " tp.m.l.v = 99;\n" " return tp.m.l.v;\n" "};", 99 }, /* `def NAME: str = \"lit\"` field access. The Sdef has no stack * slot, so .len/.ptr must inline the literal length / strlit * address; without the fix, .len reads BP+8 (return-address slot) * as garbage. */ { "def MSG: str = \"hello world\";\n" "fn main() i32 = { return MSG.len: i32; };", 11 }, { "use os;\n" "def GREETING: str = \"hi\\n\";\n" "fn main() i32 = {\n" " os.write(1, GREETING.ptr, GREETING.len: u64);\n" " return GREETING.len: i32;\n" "};", 3 }, { NULL, 0 } }; int main(void) { const char *bin = getenv("BIN"); if (!bin) bin = "out/bin"; /* Resolve to absolute path: tests chdir into /tmp/... */ char absbin[1024]; if (bin[0] != '/') { char cwd[1024]; if (getcwd(cwd, sizeof cwd) == NULL) return 1; snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); bin = absbin; } int n = 0, fail = 0; for (int i = 0; rows[i].src; i++, n++) { char src[64], exe[64]; snprintf(src, sizeof src, "/tmp/wwe2e_%d_%d.ww", getpid(), i); snprintf(exe, sizeof exe, "/tmp/wwe2e_%d_%d", getpid(), i); FILE *f = fopen(src, "wb"); fputs(rows[i].src, f); fclose(f); char cmd[1024]; /* ww build writes the binary to the current working dir, * named after the source basename. We override by chdir. */ char tmpdir[64]; snprintf(tmpdir, sizeof tmpdir, "/tmp/wwe2e_%d_d_%d", getpid(), i); mkdir(tmpdir, 0755); snprintf(cmd, sizeof cmd, "cd %s && %s/ww build %s", tmpdir, bin, src); if (runwait(cmd) != 0) { fail++; continue; } char outbin[128]; const char *base = strrchr(src, '/'); base = base ? base + 1 : src; snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); char *dot = strrchr(outbin, '.'); if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; int got = runwait(outbin); if (got != rows[i].want_exit) { fprintf(stderr, "row %d: exit %d, want %d\n src: %s\n", i, got, rows[i].want_exit, rows[i].src); fail++; } unlink(src); unlink(outbin); rmdir(tmpdir); (void)exe; } if (fail) { fprintf(stderr, "%d/%d e2e tests failed\n", fail, n); return 1; } printf("e2e: %d/%d ok\n", n, n); return 0; }