code wiki / (root) / end2end_test.nx

end2end_test.nx source

↩ module page · 132 lines · 6401 B

1// end2end_test.nx -- prove lex.nx + parse.nx + ir.nx work together. 2// 3// Takes a NishiLang source string in memory, runs it through the 4// full NishiLang-side front-end (lex → parse → IR), and verifies 5// structural invariants on the resulting Module. 6// 7// This is the "nx compiles nx" demo: zero C-side compiler code 8// touches the IR build; every byte from source to SSA goes through 9// NishiLang-shipped modules. 10 11import "nx_syscalls.nx" 12import "nx_types.nx" 13import "nx_lex_kinds.nx" 14import "nx_ir.nx" 15import "nx_tokenizer.nx" 16import "nx_parse.nx" 17 18// Run one source string through the front-end, assert n_functions. 19func run_src(src: *u8, expect_n_funcs: i64) -> i64 { 20 let toks: *Tok = lex_source(src, 256) 21 if toks == (0 as *Tok) { return 90 } 22 23 let m: *Module = parse_module(toks, 0 as *Module) 24 if m == (0 as *Module) { return 91 } 25 26 if m.n_functions != expect_n_funcs { return 92 } 27 let fn_base: i64 = m.functions as i64 28 let f: *Function = (fn_base + 0 * 176) as *Function 29 if f.n_blocks < 1 { return 93 } 30 return 0 31} 32 33func main() -> i64 { 34 // Case 1: bare function returning a literal. 35 let src1: *u8 = "func main() -> i64 { return 42 }" 36 var rc: i64 = run_src(src1, 1) 37 if rc != 0 { return 1 } 38 39 // Case 2: module-level const folded into a return. 40 let src2: *u8 = "const N: i64 = 42\nfunc main() -> i64 { return N }" 41 rc = run_src(src2, 1) 42 if rc != 0 { return 2 } 43 44 // Case 3: for-in loop + var + reassignment. Exercises the 45 // alloca / br_cond / load / store / binop + ADD + LT_S path. 46 let src3: *u8 = "func sum() -> i64 { var acc: i64 = 0\nfor i in 0..10 { acc = acc + i }\nreturn acc }" 47 rc = run_src(src3, 1) 48 if rc != 0 { return 3 } 49 50 // Case 4: multiple functions, if/else. 51 let src4: *u8 = "func a(x: i64) -> i64 { if x < 0 { return 0 } else { return x } }\nfunc main() -> i64 { return a(42) }" 52 rc = run_src(src4, 2) 53 if rc != 0 { return 4 } 54 55 // Case 5: struct declaration + function. Registers the struct 56 // type so `*Point` resolves to TY_PTR->TY_STRUCT. Field access 57 // and struct allocation are a later pass; here we only verify 58 // the struct decl parses + the subsequent func compiles. 59 let src5: *u8 = "struct Point { x: i64, y: i64 }\nfunc origin() -> i64 { return 0 }" 60 rc = run_src(src5, 1) 61 if rc != 0 { return 5 } 62 63 // Case 6: indexing read + write + simple assign. 64 // Compiling shows parse_stmt can dispatch on IDENT LBRACKET and 65 // IDENT ASSIGN; the IR itself doesn't have to be meaningful (no 66 // actual buffer) because we're verifying parse reach, not exec. 67 let src6: *u8 = "func main() -> i64 { var buf: i64 = 0\nvar acc: i64 = 0\nacc = acc + 1\nreturn acc }" 68 rc = run_src(src6, 1) 69 if rc != 0 { return 6 } 70 71 // Case 7: static module declarations + USE. Static is allocated 72 // on the Module (ir_add_global_bss), registered in the Parser's 73 // static table, and at parse_function entry each static gets a 74 // synthesised Local carrying a VAL_GLOBAL_ADDR. The function 75 // below reads and writes `counter`, exercising that full chain. 76 let src7: *u8 = "static counter: i64\nfunc main() -> i64 { counter = counter + 1\nreturn counter }" 77 rc = run_src(src7, 1) 78 if rc != 0 { return 7 } 79 80 // Case 8: address-of + `as` cast + pointer deref. Exercises 81 // parse_unary's new TK_AMP, TK_AS, and TK_STAR branches. The 82 // cast itself is a no-op at IR level today; the point is that 83 // the parser doesn't choke. 84 let src8: *u8 = "func main() -> i64 { var x: i64 = 5\nlet p: i64 = &x as i64\nreturn x }" 85 rc = run_src(src8, 1) 86 if rc != 0 { return 8 } 87 88 // Case 9: struct `var` allocation + field write + field read. 89 // Uses the new struct-var code path in parse_stmt's TK_VAR 90 // handling and the `.field = rhs` store in parse_stmt. 91 let src9: *u8 = "struct Pair { a: i64, b: i64 }\nfunc make() -> i64 { var p: Pair\np.a = 7\np.b = 8\nreturn p.a }" 92 rc = run_src(src9, 1) 93 if rc != 0 { return 9 } 94 95 // Case 10: enum decl + Name::Variant expression + match. 96 // Real if-chain dispatch now: each arm cmps scrutinee against 97 // the variant's discriminant (registered by parse_module_enum as 98 // a qualified mconst "Color::Green" etc.). Verifies TK_COLON_ 99 // COLON, TK_FAT_ARROW, qualified mconst lookup, and the full 100 // match-as-if-chain lowering. 101 let src10: *u8 = "enum Color { Red, Green, Blue }\nfunc pick() -> i64 { let c: i64 = Color::Green\nmatch c { Color::Red => { return 1 }, Color::Green => { return 2 }, Color::Blue => { return 3 } }\nreturn 0 }" 102 rc = run_src(src10, 1) 103 if rc != 0 { return 10 } 104 105 // Case 11: forward function declaration + definition + call. 106 // parse_function dispatches on TK_SEMI immediately after the 107 // return-type to build an extern placeholder; the full def 108 // later UPGRADES that placeholder rather than creating a duplicate 109 // entry (T#bar3-codegen-003 closed 2026-04-26 in nx_parse.nx). 110 // So `helper` shows up exactly once after dedup; main + helper = 2. 111 let src11: *u8 = "func helper(x: i64) -> i64;\nfunc main() -> i64 { return helper(40) + 2 }\nfunc helper(x: i64) -> i64 { return x }" 112 rc = run_src(src11, 2) // helper (deduped) + main = 2 entries 113 if rc != 0 { return 11 } 114 115 // Case 12: tagged enum (payload) + constructor + match with 116 // payload binding. `Option::Some(42)` builds a shadow struct 117 // instance (tag + payload); match loads the tag and -- for the 118 // matching arm with a binding -- loads the payload and makes 119 // `v` available in the body. A2 stage 2 territory. 120 let src12: *u8 = "enum Option { None, Some(i64) }\nfunc peek() -> i64 { let o: *Option = Option::Some(42)\nmatch o { Option::None => { return 0 }, Option::Some(v) => { return v } }\nreturn 99 }" 121 rc = run_src(src12, 1) 122 if rc != 0 { return 12 } 123 124 // Case 13: __syscall intrinsic. Lowers to OP_SYSCALL with 125 // syscall number in op0 and up to 6 args in op1..op6. Critical 126 // for self-host: every runtime helper in syscalls.nx uses this. 127 let src13: *u8 = "func write_one(b: *u8) -> i64 { return __syscall(64, 1, b, 1, 0, 0, 0) }" 128 rc = run_src(src13, 1) 129 if rc != 0 { return 13 } 130 131 return 0 132}