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}