nx_macro_v2_test.nx source
↩ module page · 112 lines · 3899 B
1// nx_macro_v2_test.nx -- exercise multi-line {} bodies + $$ident gensym.
2
3import "nx_kernel_v2.nx"
4
5// ===== Multi-line body: emits a whole function definition =====
6// The { ... } block can span as many lines as needed. Body parser
7// is depth-aware -- nested { } / strings / comments don't trip it up.
8@macro DEFINE_RECT_AREA(name, w, h) {
9 func name() -> nx_int {
10 let area: nx_int = w * h
11 return area
12 }
13}
14
15DEFINE_RECT_AREA(rect_2x3, 2, 3)
16DEFINE_RECT_AREA(rect_5x7, 5, 7)
17
18// ===== Hygienic gensym: $$ident gets a unique suffix per invocation
19// Two invocations of SAFE_DOUBLE produce __nx_g_tmp_N1 and __nx_g_tmp_N2,
20// so multiple expansions in the same scope don't collide.
21@macro SAFE_DOUBLE(x) {
22 let $$tmp: nx_int = x + x
23 return $$tmp
24}
25
26func double_three() -> nx_int { SAFE_DOUBLE(3) }
27func double_seven() -> nx_int { SAFE_DOUBLE(7) }
28
29// ===== Multi-line body that uses BOTH gensym AND parameter substitution
30@macro CHECK_EQ(actual_expr, expected) {
31 let $$got: nx_int = actual_expr
32 if $$got != expected { return 99 }
33}
34
35// ===== A more substantial macro: emit a kernel-axiom helper =====
36@macro EMIT_CONST_AXIOM(fname, ch, sym_id) {
37 func fname() -> nx_int {
38 let $$t: *Term = nx_term_const(sym_id)
39 return nx_k2_axiom(ch, $$t)
40 }
41}
42
43// ===== Tests =====
44
45func test_multiline_basic() -> nx_int {
46 let a: nx_int = rect_2x3()
47 if a != 6 { return 1 }
48 let b: nx_int = rect_5x7()
49 if b != 35 { return 1 }
50 return 0
51}
52
53func test_gensym_isolation() -> nx_int {
54 let a: nx_int = double_three()
55 if a != 6 { return 2 }
56 let b: nx_int = double_seven()
57 if b != 14 { return 2 }
58 return 0
59}
60
61func test_gensym_within_one_body() -> nx_int {
62 // CHECK_EQ uses $$got twice in the same body. Both must resolve
63 // to the same gensym'd identifier so the function compiles.
64 let three_doubled: nx_int = 6
65 CHECK_EQ(three_doubled, 6)
66 return 0
67}
68
69// ===== Macro that builds + registers an axiom in one call =====
70// $$t is gensym'd so concurrent uses can't collide with caller-scope.
71@macro REGISTER_CONST(ch, sym_id) {
72 let $$t: *Term = nx_term_const(sym_id)
73 let _idx: nx_int = nx_k2_axiom(ch, $$t)
74}
75
76func test_multiple_axiom_macros() -> nx_int {
77 let ch: *K2Chain = nx_k2_chain_new(8)
78 REGISTER_CONST(ch, 7777)
79 REGISTER_CONST(ch, 8888)
80 if ch.n != 2 { return 4 }
81 return 0
82}
83
84func main() -> nx_exit {
85 println("=== nx_macro_v2 -- multi-line bodies + $$ident gensym ===" as *u8)
86
87 let r1: nx_int = test_multiline_basic()
88 if r1 != 0 { println("T1 multiline_basic FAIL" as *u8); return r1 }
89 println("T1 multiline_basic PASS rect_2x3()=6, rect_5x7()=35 from {} macros" as *u8)
90
91 let r2: nx_int = test_gensym_isolation()
92 if r2 != 0 { println("T2 gensym_isolation FAIL" as *u8); return r2 }
93 println("T2 gensym_isolation PASS two SAFE_DOUBLE invocations get distinct $$tmp" as *u8)
94
95 let r3: nx_int = test_gensym_within_one_body()
96 if r3 != 0 { println("T3 gensym_within_one_body FAIL" as *u8); return r3 }
97 println("T3 gensym_one_body PASS $$got used twice in CHECK_EQ resolves to one ident" as *u8)
98
99 let r4: nx_int = test_multiple_axiom_macros()
100 if r4 != 0 { println("T4 multiple_axiom_macros FAIL" as *u8); return r4 }
101 println("T4 axiom_macro PASS caller-scope $$ident works alongside macro-scope" as *u8)
102
103 println("" as *u8)
104 println("=== Foundational metaprogramming v2 verified ===" as *u8)
105 println(" @macro NAME(args) { multi-line body" as *u8)
106 println(" let $$tmp: nx_int = ... hygienic local" as *u8)
107 println(" return $$tmp" as *u8)
108 println(" }" as *u8)
109 println(" Bodies can declare their own functions, locals, control flow." as *u8)
110 println(" $$ident produces __nx_g_ident_<N> per invocation -- collision-free." as *u8)
111 return 0
112}