code wiki / _hdl_build / nx_wat_compiler.nx
nx_wat_compiler.nx source
↩ module page · 695 lines · 35361 B
1// nx_wat_compiler.nx -- G-WASM-001 milestone 3: the SOVEREIGN WAT-text -> WASM-binary pipeline
2// end to end (lexer + parser + binary emit), reusing leb128.nx. Reads a .wat file (the dialect
3// nxc2 --target wat emits) and writes a .wasm. NO node/python/WABT in this organ; verified
4// byte-exact vs WABT used ONCE as an alignment oracle, then dropped.
5// Subset this milestone handles (the f.wat function): (module (memory (export "memory") N)
6// (func (export "name") (param i64)* (result i64)? (local i64)* <body of: local.get/local.set/
7// i64.const/i64.add> )). Remaining opcodes (control flow/call/load/store/etc) = next iterations.
8// usage: nx_wat_compiler <in.wat> <out.wasm>
9// STATUS: byte-exact vs WABT on REAL nxc2 output (g.wat: import + 2 funcs + call + br_table +
10// block/loop/br + mixed i32/i64 locals + arith). Handles: type-dedup, import section, multi-
11// function (function/code N entries), func symbol table + call, top-level & inline exports,
12// memory, named/numeric locals, 20+ opcodes, control flow + label depth, br_table.
13// GAMES: COMPLETE -- compiles nx_dungeon_alpha_wasm (a real game) byte-identical to WABT (9176B),
14// output instantiates + runs (GAME-LOGIC-OK). Handles flat if/else/end + i64.load/store/load8_u/store8.
15// GOTCHA fixed: i64.load8_u = 0x31 (0x30 is load8_s); buffers sized in BYTES not entries (overflow=corruption).
16// The full nx->wat->wasm path is sovereign; WABT/node only ever one-time align/verify oracles.
17// F618 wasm-SIMD FLIP (2026-07-21): + v128.load/store, i8x16.sub_sat_u, v128.or, extadd-pairwise
18// u8->u16->u32 (_u and the _s mutation twin), i32x4.extract_lane <lane>, and `(local $x v128)`
19// (valtype 0x7B). All 0xFD-prefixed, sub-opcodes spec-verified. Unknown mnemonics still REFUSE.
20// license_tier: ORIGINAL
21import "leb128.nx"
22import "nx_syscalls.nx"
23const K_MAGIC_65536: i64 = 65536
24const K_MAGIC_4194304: i64 = 4194304
25const K_MAGIC_2097152: i64 = 2097152
26const K_MAGIC_524288: i64 = 524288
27const K_MAGIC_8388608: i64 = 8388608
28
29func w8(b: *u8, p: i64, v: i64) -> i64 { b[p] = v as u8; return p + 1 }
30func wcopy(dst: *u8, dp: i64, src: *u8, n: i64) -> i64 {
31 var i: i64 = 0
32 while i < n { dst[dp + i] = src[i]; i = i + 1 }
33 return dp + n
34}
35func emit_section(out: *u8, p: i64, id: i64, sc: *u8, sclen: i64) -> i64 {
36 var q: i64 = w8(out, p, id)
37 q = q + uleb128_encode(sclen, out, q)
38 q = wcopy(out, q, sc, sclen)
39 return q
40}
41func is_ws(c: i64) -> i64 {
42 if c == 32 { return 1 }
43 if c == 9 { return 1 }
44 if c == 10 { return 1 }
45 if c == 13 { return 1 }
46 return 0
47}
48func is_delim(c: i64) -> i64 {
49 if is_ws(c) == 1 { return 1 }
50 if c == 40 { return 1 }
51 if c == 41 { return 1 }
52 return 0
53}
54// lexer state ls[]: 0=pos 1=kind(0 eof,1 '(',2 ')',3 atom,4 string) 2=tok_start 3=tok_len
55func lex_next(buf: *u8, n: i64, ls: *i64) -> i64 {
56 var pos: i64 = ls[0]
57 var skip: i64 = 1
58 while skip == 1 {
59 skip = 0
60 var ws_go: i64 = 1
61 while ws_go == 1 {
62 if pos >= n { ws_go = 0 } else {
63 if is_ws(buf[pos] as i64) == 1 { pos = pos + 1 } else { ws_go = 0 }
64 }
65 }
66 if pos < n {
67 if buf[pos] == (59 as u8) {
68 if pos + 1 < n {
69 if buf[pos + 1] == (59 as u8) {
70 var cm: i64 = 1
71 while cm == 1 {
72 if pos >= n { cm = 0 } else {
73 if buf[pos] == (10 as u8) { cm = 0 } else { pos = pos + 1 }
74 }
75 }
76 skip = 1
77 }
78 }
79 }
80 }
81 }
82 if pos >= n { ls[1] = 0; ls[0] = pos; return 0 }
83 let c: i64 = buf[pos] as i64
84 if c == 40 { ls[1] = 1; ls[2] = pos; ls[3] = 1; ls[0] = pos + 1; return 0 }
85 if c == 41 { ls[1] = 2; ls[2] = pos; ls[3] = 1; ls[0] = pos + 1; return 0 }
86 if c == 34 {
87 let st: i64 = pos + 1
88 var q: i64 = st
89 while buf[q] != (34 as u8) { q = q + 1 }
90 ls[1] = 4; ls[2] = st; ls[3] = q - st; ls[0] = q + 1; return 0
91 }
92 let st2: i64 = pos
93 var e: i64 = pos
94 var done: i64 = 0
95 while done == 0 {
96 if e >= n { done = 1 } else {
97 if is_delim(buf[e] as i64) == 1 { done = 1 } else { e = e + 1 }
98 }
99 }
100 ls[1] = 3; ls[2] = st2; ls[3] = e - st2; ls[0] = e
101 return 0
102}
103func atom_eq(buf: *u8, s: i64, l: i64, kw: *u8) -> i64 {
104 var i: i64 = 0
105 while i < l { if buf[s + i] != kw[i] { return 0 } i = i + 1 }
106 if kw[l] != (0 as u8) { return 0 }
107 return 1
108}
109func atom_int(buf: *u8, s: i64, l: i64) -> i64 {
110 var i: i64 = 0
111 var neg: i64 = 0
112 if l > 0 { if buf[s] == (45 as u8) { neg = 1; i = 1 } }
113 var v: i64 = 0
114 while i < l { v = v * 10 + ((buf[s + i] as i64) - 48); i = i + 1 }
115 if neg == 1 { return 0 - v }
116 return v
117}
118// classify a body mnemonic -> op code, or -1 unknown. Operand ops: 0/1=local.get/set
119// (uleb local idx), 2=i64.const / 16=i32.const (sleb). Rest are single-byte.
120func instr_op(buf: *u8, s: i64, l: i64) -> i64 {
121 if atom_eq(buf, s, l, "local.get" as *u8) == 1 { return 0 }
122 if atom_eq(buf, s, l, "local.set" as *u8) == 1 { return 1 }
123 if atom_eq(buf, s, l, "i64.const" as *u8) == 1 { return 2 }
124 if atom_eq(buf, s, l, "i64.add" as *u8) == 1 { return 3 }
125 if atom_eq(buf, s, l, "i64.sub" as *u8) == 1 { return 4 }
126 if atom_eq(buf, s, l, "i64.mul" as *u8) == 1 { return 5 }
127 if atom_eq(buf, s, l, "i64.and" as *u8) == 1 { return 6 }
128 if atom_eq(buf, s, l, "i64.shr_s" as *u8) == 1 { return 7 }
129 if atom_eq(buf, s, l, "i64.div_s" as *u8) == 1 { return 8 }
130 if atom_eq(buf, s, l, "i64.rem_s" as *u8) == 1 { return 9 }
131 if atom_eq(buf, s, l, "i64.eq" as *u8) == 1 { return 10 }
132 if atom_eq(buf, s, l, "i64.ne" as *u8) == 1 { return 11 }
133 if atom_eq(buf, s, l, "i64.lt_s" as *u8) == 1 { return 12 }
134 if atom_eq(buf, s, l, "i64.gt_s" as *u8) == 1 { return 13 }
135 if atom_eq(buf, s, l, "i64.le_s" as *u8) == 1 { return 14 }
136 if atom_eq(buf, s, l, "i64.ge_s" as *u8) == 1 { return 15 }
137 if atom_eq(buf, s, l, "i32.const" as *u8) == 1 { return 16 }
138 if atom_eq(buf, s, l, "i32.wrap_i64" as *u8) == 1 { return 17 }
139 if atom_eq(buf, s, l, "i64.extend_i32_u" as *u8) == 1 { return 18 }
140 if atom_eq(buf, s, l, "unreachable" as *u8) == 1 { return 19 }
141 if atom_eq(buf, s, l, "i64.or" as *u8) == 1 { return 20 }
142 if atom_eq(buf, s, l, "i64.shl" as *u8) == 1 { return 21 }
143 if atom_eq(buf, s, l, "i64.xor" as *u8) == 1 { return 22 }
144 if atom_eq(buf, s, l, "br" as *u8) == 1 { return 28 }
145 if atom_eq(buf, s, l, "br_table" as *u8) == 1 { return 29 }
146 if atom_eq(buf, s, l, "call" as *u8) == 1 { return 30 }
147 if atom_eq(buf, s, l, "if" as *u8) == 1 { return 25 }
148 if atom_eq(buf, s, l, "else" as *u8) == 1 { return 26 }
149 if atom_eq(buf, s, l, "end" as *u8) == 1 { return 27 }
150 if atom_eq(buf, s, l, "i64.load" as *u8) == 1 { return 31 }
151 if atom_eq(buf, s, l, "i64.store" as *u8) == 1 { return 32 }
152 if atom_eq(buf, s, l, "i64.load8_u" as *u8) == 1 { return 33 }
153 if atom_eq(buf, s, l, "i64.store8" as *u8) == 1 { return 34 }
154 // full subword-memory family (2026-07-10 sext debt fix): the emitter now picks _s/_u per the sext
155 // bit, and 16/32-bit widths were previously UNASSEMBLABLE here (emitter could emit them -> WATC
156 // REFUSED). Opcodes: load8_s=0x30 load16_s/u=0x32/0x33 load32_s/u=0x34/0x35 store16=0x3D store32=0x3E.
157 if atom_eq(buf, s, l, "i64.load8_s" as *u8) == 1 { return 35 }
158 if atom_eq(buf, s, l, "i64.load16_s" as *u8) == 1 { return 36 }
159 if atom_eq(buf, s, l, "i64.load16_u" as *u8) == 1 { return 37 }
160 if atom_eq(buf, s, l, "i64.load32_s" as *u8) == 1 { return 38 }
161 if atom_eq(buf, s, l, "i64.load32_u" as *u8) == 1 { return 39 }
162 if atom_eq(buf, s, l, "i64.store16" as *u8) == 1 { return 48 }
163 if atom_eq(buf, s, l, "i64.store32" as *u8) == 1 { return 49 }
164 if atom_eq(buf, s, l, "f32.add" as *u8) == 1 { return 40 }
165 if atom_eq(buf, s, l, "f32.sub" as *u8) == 1 { return 41 }
166 if atom_eq(buf, s, l, "f32.mul" as *u8) == 1 { return 42 }
167 if atom_eq(buf, s, l, "f32.div" as *u8) == 1 { return 43 }
168 if atom_eq(buf, s, l, "f32.convert_i64_s" as *u8) == 1 { return 44 }
169 if atom_eq(buf, s, l, "i64.trunc_f32_s" as *u8) == 1 { return 45 }
170 if atom_eq(buf, s, l, "i32.reinterpret_f32" as *u8) == 1 { return 46 }
171 if atom_eq(buf, s, l, "f32.reinterpret_i32" as *u8) == 1 { return 47 }
172 // F618 wasm-SIMD subset (0xFD prefix; sub-opcodes spec-verified)
173 if atom_eq(buf, s, l, "v128.load" as *u8) == 1 { return 50 }
174 if atom_eq(buf, s, l, "i8x16.sub_sat_u" as *u8) == 1 { return 51 }
175 if atom_eq(buf, s, l, "v128.or" as *u8) == 1 { return 52 }
176 if atom_eq(buf, s, l, "i16x8.extadd_pairwise_i8x16_u" as *u8) == 1 { return 53 }
177 if atom_eq(buf, s, l, "i32x4.extadd_pairwise_i16x8_u" as *u8) == 1 { return 54 }
178 if atom_eq(buf, s, l, "i32x4.extract_lane" as *u8) == 1 { return 55 }
179 if atom_eq(buf, s, l, "i16x8.extadd_pairwise_i8x16_s" as *u8) == 1 { return 56 }
180 if atom_eq(buf, s, l, "v128.store" as *u8) == 1 { return 57 }
181 // memory.grow (2026-07-29, the sys_mmap 0-stub root fix): the emitter's $nx_wasm_mmap helper is
182 // stateless grow-per-allocation, so the ONLY new opcode the language needed was grow itself.
183 if atom_eq(buf, s, l, "memory.grow" as *u8) == 1 { return 58 }
184 return 0 - 1
185}
186func emit_instr(body: *u8, bp: i64, op: i64, arg: i64) -> i64 {
187 var q: i64 = bp
188 if op == 0 { q = w8(body, q, 0x20); q = q + uleb128_encode(arg, body, q) }
189 if op == 1 { q = w8(body, q, 0x21); q = q + uleb128_encode(arg, body, q) }
190 if op == 2 { q = w8(body, q, 0x42); q = q + sleb128_encode(arg, body, q) }
191 if op == 3 { q = w8(body, q, 0x7C) }
192 if op == 4 { q = w8(body, q, 0x7D) }
193 if op == 5 { q = w8(body, q, 0x7E) }
194 if op == 6 { q = w8(body, q, 0x83) }
195 if op == 7 { q = w8(body, q, 0x87) }
196 if op == 8 { q = w8(body, q, 0x7F) }
197 if op == 9 { q = w8(body, q, 0x81) }
198 if op == 10 { q = w8(body, q, 0x51) }
199 if op == 11 { q = w8(body, q, 0x52) }
200 if op == 12 { q = w8(body, q, 0x53) }
201 if op == 13 { q = w8(body, q, 0x55) }
202 if op == 14 { q = w8(body, q, 0x57) }
203 if op == 15 { q = w8(body, q, 0x59) }
204 if op == 16 { q = w8(body, q, 0x41); q = q + sleb128_encode(arg, body, q) }
205 if op == 17 { q = w8(body, q, 0xA7) }
206 if op == 18 { q = w8(body, q, 0xAD) }
207 if op == 19 { q = w8(body, q, 0x00) }
208 if op == 20 { q = w8(body, q, 0x84) }
209 if op == 21 { q = w8(body, q, 0x86) }
210 if op == 22 { q = w8(body, q, 0x85) }
211 if op == 23 { q = w8(body, q, 0x02); q = w8(body, q, 0x40) }
212 if op == 24 { q = w8(body, q, 0x03); q = w8(body, q, 0x40) }
213 if op == 25 { q = w8(body, q, 0x04); q = w8(body, q, 0x40) }
214 if op == 26 { q = w8(body, q, 0x05) }
215 if op == 27 { q = w8(body, q, 0x0B) }
216 if op == 28 { q = w8(body, q, 0x0C); q = q + uleb128_encode(arg, body, q) }
217 if op == 30 { q = w8(body, q, 0x10); q = q + uleb128_encode(arg, body, q) }
218 if op == 31 { q = w8(body, q, 0x29); q = w8(body, q, 3); q = w8(body, q, 0) }
219 if op == 32 { q = w8(body, q, 0x37); q = w8(body, q, 3); q = w8(body, q, 0) }
220 if op == 33 { q = w8(body, q, 0x31); q = w8(body, q, 0); q = w8(body, q, 0) }
221 if op == 34 { q = w8(body, q, 0x3C); q = w8(body, q, 0); q = w8(body, q, 0) }
222 // subword-memory family (sext fix): align exponent = natural (0 for 8-bit, 1 for 16, 2 for 32)
223 if op == 35 { q = w8(body, q, 0x30); q = w8(body, q, 0); q = w8(body, q, 0) } // i64.load8_s
224 if op == 36 { q = w8(body, q, 0x32); q = w8(body, q, 1); q = w8(body, q, 0) } // i64.load16_s
225 if op == 37 { q = w8(body, q, 0x33); q = w8(body, q, 1); q = w8(body, q, 0) } // i64.load16_u
226 if op == 38 { q = w8(body, q, 0x34); q = w8(body, q, 2); q = w8(body, q, 0) } // i64.load32_s
227 if op == 39 { q = w8(body, q, 0x35); q = w8(body, q, 2); q = w8(body, q, 0) } // i64.load32_u
228 if op == 48 { q = w8(body, q, 0x3D); q = w8(body, q, 1); q = w8(body, q, 0) } // i64.store16
229 if op == 49 { q = w8(body, q, 0x3E); q = w8(body, q, 2); q = w8(body, q, 0) } // i64.store32
230 if op == 40 { q = w8(body, q, 0x92) } // f32.add
231 if op == 41 { q = w8(body, q, 0x93) } // f32.sub
232 if op == 42 { q = w8(body, q, 0x94) } // f32.mul
233 if op == 43 { q = w8(body, q, 0x95) } // f32.div
234 if op == 44 { q = w8(body, q, 0xB4) } // f32.convert_i64_s
235 if op == 45 { q = w8(body, q, 0xAE) } // i64.trunc_f32_s
236 if op == 46 { q = w8(body, q, 0xBC) } // i32.reinterpret_f32
237 if op == 47 { q = w8(body, q, 0xBE) } // f32.reinterpret_i32
238 // F618 wasm-SIMD subset: 0xFD prefix + LEB128 sub-opcode (all subs here < 0x80 = single byte).
239 // memarg for v128.load/store: align exponent 0 (byte-aligned hint -- SAD reads ARE unaligned at
240 // arbitrary motion offsets; unaligned v128 access is architecturally legal in wasm), offset 0.
241 if op == 50 { q = w8(body, q, 0xFD); q = w8(body, q, 0x00); q = w8(body, q, 0); q = w8(body, q, 0) } // v128.load
242 if op == 51 { q = w8(body, q, 0xFD); q = w8(body, q, 0x73) } // i8x16.sub_sat_u
243 if op == 52 { q = w8(body, q, 0xFD); q = w8(body, q, 0x50) } // v128.or
244 if op == 53 { q = w8(body, q, 0xFD); q = w8(body, q, 0x7D) } // i16x8.extadd_pairwise_i8x16_u
245 if op == 54 { q = w8(body, q, 0xFD); q = w8(body, q, 0x7F) } // i32x4.extadd_pairwise_i16x8_u
246 if op == 55 { q = w8(body, q, 0xFD); q = w8(body, q, 0x1B); q = w8(body, q, arg) } // i32x4.extract_lane <lane>
247 if op == 56 { q = w8(body, q, 0xFD); q = w8(body, q, 0x7C) } // i16x8.extadd_pairwise_i8x16_s (mutation-gate twin)
248 if op == 57 { q = w8(body, q, 0xFD); q = w8(body, q, 0x0B); q = w8(body, q, 0); q = w8(body, q, 0) } // v128.store
249 if op == 58 { q = w8(body, q, 0x40); q = w8(body, q, 0x00) } // memory.grow (memidx 0)
250 return q
251}
252// op takes an operand we read from the next atom? 0/1=local idx, 2/16=const.
253func op_has_operand(op: i64) -> i64 {
254 if op == 0 { return 1 }
255 if op == 1 { return 1 }
256 if op == 2 { return 1 }
257 if op == 16 { return 1 }
258 if op == 55 { return 1 } // i32x4.extract_lane <laneidx> (plain int atom, emitted as one raw byte)
259 return 0
260}
261func op_is_local(op: i64) -> i64 {
262 if op == 0 { return 1 }
263 if op == 1 { return 1 }
264 return 0
265}
266// resolve a local operand atom: "$name" -> index via the name table, else parse int
267func resolve_local(buf: *u8, s: i64, l: i64, lns: *i64, lnl: *i64, cnt: i64) -> i64 {
268 if buf[s] != (36 as u8) { return atom_int(buf, s, l) }
269 var i: i64 = 0
270 while i < cnt {
271 if lns[i] >= 0 { if lnl[i] == l {
272 var j: i64 = 0
273 var same: i64 = 1
274 while j < l { if buf[lns[i] + j] != buf[s + j] { same = 0 } j = j + 1 }
275 if same == 1 { return i }
276 } }
277 i = i + 1
278 }
279 return 0
280}
281func emit_name(sc: *u8, s: i64, buf: *u8, ns: i64, nl: i64) -> i64 {
282 var q: i64 = s + uleb128_encode(nl, sc, s)
283 q = wcopy(sc, q, ((buf as i64) + ns) as *u8, nl)
284 return q
285}
286
287// emit the code-body locals vec from ltype[np..ntot-1] as run-length groups by type
288func emit_locals(body: *u8, bp: i64, ltype: *i64, np: i64, ntot: i64) -> i64 {
289 var groups: i64 = 0
290 var gi: i64 = np
291 while gi < ntot {
292 let t: i64 = ltype[gi]
293 var go: i64 = 1
294 while go == 1 { if gi >= ntot { go = 0 } else { if ltype[gi] == t { gi = gi + 1 } else { go = 0 } } }
295 groups = groups + 1
296 }
297 var q: i64 = bp + uleb128_encode(groups, body, bp)
298 gi = np
299 while gi < ntot {
300 let t: i64 = ltype[gi]
301 var run: i64 = 0
302 var go2: i64 = 1
303 while go2 == 1 { if gi >= ntot { go2 = 0 } else { if ltype[gi] == t { run = run + 1; gi = gi + 1 } else { go2 = 0 } } }
304 q = q + uleb128_encode(run, body, q)
305 q = w8(body, q, t)
306 }
307 return q
308}
309// emit one functype (all-i64 params/results, matching nxc2 signatures) into sc at s
310func emit_functype(sc: *u8, s: i64, np: i64, nr: i64) -> i64 {
311 var q: i64 = w8(sc, s, 0x60)
312 q = q + uleb128_encode(np, sc, q)
313 var i: i64 = 0
314 while i < np { q = w8(sc, q, 0x7E); i = i + 1 }
315 q = q + uleb128_encode(nr, sc, q)
316 i = 0
317 while i < nr { q = w8(sc, q, 0x7E); i = i + 1 }
318 return q
319}
320// dedup a (nparams,nresults) signature into the type table; return its typeidx
321func sig_find_or_add(tnp: *i64, tnr: *i64, ntp: *i64, np: i64, nr: i64) -> i64 {
322 var i: i64 = 0
323 let nt: i64 = ntp[0]
324 while i < nt { if tnp[i] == np { if tnr[i] == nr { return i } } i = i + 1 }
325 tnp[nt] = np; tnr[nt] = nr; ntp[0] = nt + 1
326 return nt
327}
328func substr_eq(buf: *u8, a: i64, b: i64, l: i64) -> i64 {
329 var j: i64 = 0
330 while j < l { if buf[a + j] != buf[b + j] { return 0 } j = j + 1 }
331 return 1
332}
333// resolve a br target label name -> relative depth (innermost open block = 0)
334func resolve_label(buf: *u8, s: i64, l: i64, lks: *i64, lkl: *i64, lsp: i64) -> i64 {
335 var i: i64 = lsp - 1
336 while i >= 0 {
337 if lkl[i] == l { if substr_eq(buf, s, lks[i], l) == 1 { return (lsp - 1) - i } }
338 i = i - 1
339 }
340 return 0
341}
342
343// resolve a call target $name -> funcidx (ftab = imports first, then defined funcs, in order)
344func resolve_func(buf: *u8, s: i64, l: i64, fts: *i64, ftl: *i64, nf: i64) -> i64 {
345 var i: i64 = 0
346 while i < nf { if ftl[i] == l { if substr_eq(buf, s, fts[i], l) == 1 { return i } } i = i + 1 }
347 return 0
348}
349// pass 0: build the func symbol table by scanning every (func $name (incl import funcs, which
350// appear first in nxc2 output) so calls (even forward) resolve to a funcidx. nimp[0]=#imports.
351func prescan_ftab(buf: *u8, n: i64, fts: *i64, ftl: *i64, nimp: *i64) -> i64 {
352 let ls2: *i64 = sys_mmap(64) as *i64
353 ls2[0] = 0
354 var nf: i64 = 0
355 var ni: i64 = 0
356 var depth: i64 = 0
357 var in_export: i64 = 0
358 var exp_depth: i64 = 0
359 var go: i64 = 1
360 while go == 1 {
361 lex_next(buf, n, ls2)
362 if ls2[1] == 0 { go = 0 }
363 if ls2[1] == 1 { depth = depth + 1 }
364 if ls2[1] == 2 { depth = depth - 1; if in_export == 1 { if depth < exp_depth { in_export = 0 } } }
365 if ls2[1] == 3 {
366 if atom_eq(buf, ls2[2], ls2[3], "export" as *u8) == 1 { in_export = 1; exp_depth = depth }
367 if atom_eq(buf, ls2[2], ls2[3], "import" as *u8) == 1 { ni = ni + 1 }
368 if atom_eq(buf, ls2[2], ls2[3], "func" as *u8) == 1 {
369 if in_export == 0 {
370 lex_next(buf, n, ls2)
371 if ls2[1] == 3 { fts[nf] = ls2[2]; ftl[nf] = ls2[3]; nf = nf + 1 }
372 }
373 }
374 }
375 }
376 nimp[0] = ni
377 return nf
378}
379
380func wc_compile_file(inp: *u8, outp: *u8) -> i64 {
381 let lenp: *i64 = sys_mmap(16) as *i64
382 let buf: *u8 = sys_read_file(inp, lenp)
383 if buf == (0 as i64) as *u8 { sys_write(1, "WATC REFUSED: cannot read input\n" as *u8, 32); sys_exit(1); return 1 }
384 let n: i64 = lenp[0]
385 let ls: *i64 = sys_mmap(64) as *i64
386 ls[0] = 0
387 // pass 0: func symbol table (imports first, then defined funcs, by $name)
388 // CAPACITY HARDENING 2026-07-05: the video-client module with the sovereign codec = 382 funcs /
389 // 46K lines / 192 exports -- the old caps (256 funcs, 64 per-func slots, 32K instrs) silently
390 // OVERFLOWED and the wraparound surfaced as "unknown mnemonic" at line 1. All pools sized 16-64x
391 // with the biggest real module in mind; a dev-tool's mmap is free.
392 let fts: *i64 = sys_mmap(K_MAGIC_65536) as *i64
393 let ftl: *i64 = sys_mmap(K_MAGIC_65536) as *i64
394 let nimp: *i64 = sys_mmap(16) as *i64
395 let nftot: i64 = prescan_ftab(buf, n, fts, ftl, nimp)
396 let num_imports: i64 = nimp[0]
397 // module-level state
398 var has_import: i64 = 0
399 var imp_mod_s: i64 = 0
400 var imp_mod_l: i64 = 0
401 var imp_fld_s: i64 = 0
402 var imp_fld_l: i64 = 0
403 var imp_np: i64 = 0
404 var imp_nr: i64 = 0
405 var has_mem: i64 = 0
406 var mem_min: i64 = 1
407 var mem_ns: i64 = 0
408 var mem_nl: i64 = 0
409 // shared pools across all functions
410 let in_op: *i64 = sys_mmap(K_MAGIC_4194304) as *i64
411 let in_arg: *i64 = sys_mmap(K_MAGIC_4194304) as *i64
412 var ninstr: i64 = 0
413 let lns: *i64 = sys_mmap(K_MAGIC_2097152) as *i64
414 let lnl: *i64 = sys_mmap(K_MAGIC_2097152) as *i64
415 let ltype: *i64 = sys_mmap(K_MAGIC_2097152) as *i64
416 var ntot: i64 = 0
417 var pend_s: i64 = 0 - 1
418 var pend_l: i64 = 0
419 let lks: *i64 = sys_mmap(K_MAGIC_524288) as *i64
420 let lkl: *i64 = sys_mmap(K_MAGIC_524288) as *i64
421 var lsp: i64 = 0
422 let bt_pool: *i64 = sys_mmap(K_MAGIC_2097152) as *i64
423 var btp: i64 = 0
424 // per-defined-function arrays (sig, export, local pool range, instr range)
425 let fexp_s: *i64 = sys_mmap(K_MAGIC_65536) as *i64
426 let fexp_l: *i64 = sys_mmap(K_MAGIC_65536) as *i64
427 let fnp: *i64 = sys_mmap(K_MAGIC_65536) as *i64
428 let fnr: *i64 = sys_mmap(K_MAGIC_65536) as *i64
429 let fnl: *i64 = sys_mmap(K_MAGIC_65536) as *i64
430 let flbase: *i64 = sys_mmap(K_MAGIC_65536) as *i64
431 let fist: *i64 = sys_mmap(K_MAGIC_65536) as *i64
432 let fic: *i64 = sys_mmap(K_MAGIC_65536) as *i64
433 var NF: i64 = 0
434 // ( module ... NOTE: a `handled` flag stops a consumed inner group's ')' from
435 // prematurely closing the parent loop (NishiLang has no break / else-if).
436 lex_next(buf, n, ls) // (
437 lex_next(buf, n, ls) // module
438 var top: i64 = 1
439 while top == 1 {
440 lex_next(buf, n, ls)
441 var h1: i64 = 0
442 if ls[1] == 1 {
443 h1 = 1
444 lex_next(buf, n, ls) // section keyword atom
445 if atom_eq(buf, ls[2], ls[3], "import" as *u8) == 1 {
446 has_import = 1
447 lex_next(buf, n, ls); imp_mod_s = ls[2]; imp_mod_l = ls[3] // "env"
448 lex_next(buf, n, ls); imp_fld_s = ls[2]; imp_fld_l = ls[3] // "nx_syscall"
449 lex_next(buf, n, ls) // ( of (func ...)
450 lex_next(buf, n, ls) // func
451 lex_next(buf, n, ls) // $name (import func name)
452 var fg: i64 = 1
453 while fg > 0 {
454 lex_next(buf, n, ls)
455 var hi: i64 = 0
456 if ls[1] == 1 {
457 hi = 1
458 lex_next(buf, n, ls) // param|result
459 if atom_eq(buf, ls[2], ls[3], "param" as *u8) == 1 {
460 var pi: i64 = 1
461 while pi > 0 { lex_next(buf, n, ls); if ls[1] == 2 { pi = 0 } if ls[1] == 3 { imp_np = imp_np + 1 } if ls[1] == 0 { pi = 0 } }
462 }
463 if atom_eq(buf, ls[2], ls[3], "result" as *u8) == 1 {
464 var ri: i64 = 1
465 while ri > 0 { lex_next(buf, n, ls); if ls[1] == 2 { ri = 0 } if ls[1] == 3 { imp_nr = imp_nr + 1 } if ls[1] == 0 { ri = 0 } }
466 }
467 }
468 if hi == 0 { if ls[1] == 2 { fg = 0 } } // func's )
469 if ls[1] == 0 { fg = 0 }
470 }
471 lex_next(buf, n, ls) // ) closing (import ...)
472 }
473 if atom_eq(buf, ls[2], ls[3], "memory" as *u8) == 1 {
474 has_mem = 1
475 var md: i64 = 1
476 while md > 0 {
477 lex_next(buf, n, ls)
478 var h2: i64 = 0
479 if ls[1] == 1 {
480 h2 = 1
481 lex_next(buf, n, ls) // export
482 lex_next(buf, n, ls) // "memory" string
483 mem_ns = ls[2]; mem_nl = ls[3]
484 lex_next(buf, n, ls) // )
485 }
486 if h2 == 0 { if ls[1] == 3 { mem_min = atom_int(buf, ls[2], ls[3]) } }
487 if h2 == 0 { if ls[1] == 2 { md = 0 } }
488 if ls[1] == 0 { md = 0 }
489 }
490 }
491 if atom_eq(buf, ls[2], ls[3], "func" as *u8) == 1 {
492 let F: i64 = NF
493 let fbase: i64 = ntot
494 let fist0: i64 = ninstr
495 lsp = 0
496 fexp_s[F] = 0; fexp_l[F] = 0; fnp[F] = 0; fnr[F] = 0; fnl[F] = 0
497 lex_next(buf, n, ls) // optional $name or first group/atom
498 var have_tok: i64 = 1
499 if ls[1] == 3 { have_tok = 0 }
500 var fd: i64 = 1
501 while fd > 0 {
502 if have_tok == 0 { lex_next(buf, n, ls) }
503 have_tok = 0
504 var h3: i64 = 0
505 if ls[1] == 1 {
506 h3 = 1
507 lex_next(buf, n, ls) // export|param|result|local
508 if atom_eq(buf, ls[2], ls[3], "export" as *u8) == 1 {
509 lex_next(buf, n, ls); fexp_s[F] = ls[2]; fexp_l[F] = ls[3]
510 lex_next(buf, n, ls) // )
511 }
512 if atom_eq(buf, ls[2], ls[3], "param" as *u8) == 1 {
513 pend_s = 0 - 1
514 var pp: i64 = 1
515 while pp > 0 { lex_next(buf, n, ls); if ls[1] == 2 { pp = 0 }
516 if ls[1] == 3 {
517 if buf[ls[2]] == (36 as u8) { pend_s = ls[2]; pend_l = ls[3] } else { lns[ntot] = pend_s; lnl[ntot] = pend_l; ltype[ntot] = 126; if atom_eq(buf, ls[2], ls[3], "i32" as *u8) == 1 { ltype[ntot] = 127 } ntot = ntot + 1; fnp[F] = fnp[F] + 1; pend_s = 0 - 1 }
518 }
519 if ls[1] == 0 { pp = 0 } }
520 }
521 if atom_eq(buf, ls[2], ls[3], "result" as *u8) == 1 {
522 var rr: i64 = 1
523 while rr > 0 { lex_next(buf, n, ls); if ls[1] == 2 { rr = 0 } if ls[1] == 3 { fnr[F] = fnr[F] + 1 } if ls[1] == 0 { rr = 0 } }
524 }
525 if atom_eq(buf, ls[2], ls[3], "local" as *u8) == 1 {
526 pend_s = 0 - 1
527 var lc: i64 = 1
528 while lc > 0 { lex_next(buf, n, ls); if ls[1] == 2 { lc = 0 }
529 if ls[1] == 3 {
530 if buf[ls[2]] == (36 as u8) { pend_s = ls[2]; pend_l = ls[3] } else { lns[ntot] = pend_s; lnl[ntot] = pend_l; ltype[ntot] = 126; if atom_eq(buf, ls[2], ls[3], "i32" as *u8) == 1 { ltype[ntot] = 127 } if atom_eq(buf, ls[2], ls[3], "v128" as *u8) == 1 { ltype[ntot] = 123 } ntot = ntot + 1; fnl[F] = fnl[F] + 1; pend_s = 0 - 1 }
531 }
532 if ls[1] == 0 { lc = 0 } }
533 }
534 if atom_eq(buf, ls[2], ls[3], "block" as *u8) == 1 {
535 in_op[ninstr] = 23; in_arg[ninstr] = 0; ninstr = ninstr + 1
536 lex_next(buf, n, ls); lks[lsp] = ls[2]; lkl[lsp] = ls[3]; lsp = lsp + 1
537 }
538 if atom_eq(buf, ls[2], ls[3], "loop" as *u8) == 1 {
539 in_op[ninstr] = 24; in_arg[ninstr] = 0; ninstr = ninstr + 1
540 lex_next(buf, n, ls); lks[lsp] = ls[2]; lkl[lsp] = ls[3]; lsp = lsp + 1
541 }
542 }
543 if h3 == 0 { if ls[1] == 3 {
544 let op: i64 = instr_op(buf, ls[2], ls[3])
545 if op < 0 { sys_write(1, "WATC REFUSED: unknown mnemonic: " as *u8, 31); sys_write(1, ((buf as i64) + ls[2]) as *u8, ls[3]); sys_write(1, "\n" as *u8, 1); sys_exit(1); return 1 }
546 if op == 29 {
547 let btoff: i64 = btp
548 var nlab: i64 = 0
549 var btgo: i64 = 1
550 while btgo == 1 {
551 lex_next(buf, n, ls)
552 if ls[1] == 3 { bt_pool[btoff + 1 + nlab] = resolve_label(buf, ls[2], ls[3], lks, lkl, lsp); nlab = nlab + 1 }
553 if ls[1] == 2 { btgo = 0 }
554 if ls[1] == 0 { btgo = 0 }
555 }
556 bt_pool[btoff] = nlab - 1
557 btp = btoff + 1 + nlab
558 in_op[ninstr] = 29; in_arg[ninstr] = btoff; ninstr = ninstr + 1
559 }
560 if op == 25 { in_op[ninstr] = 25; in_arg[ninstr] = 0; ninstr = ninstr + 1; lks[lsp] = 0; lkl[lsp] = 0; lsp = lsp + 1 }
561 if op == 27 { if lsp > 0 { lsp = lsp - 1 } in_op[ninstr] = 27; in_arg[ninstr] = 0; ninstr = ninstr + 1 }
562 if op != 29 { if op != 25 { if op != 27 {
563 var arg: i64 = 0
564 if op == 28 { lex_next(buf, n, ls); arg = resolve_label(buf, ls[2], ls[3], lks, lkl, lsp) }
565 if op == 30 { lex_next(buf, n, ls); arg = resolve_func(buf, ls[2], ls[3], fts, ftl, nftot) }
566 if op_has_operand(op) == 1 {
567 lex_next(buf, n, ls)
568 if op_is_local(op) == 1 { arg = resolve_local(buf, ls[2], ls[3], ((lns as i64) + fbase * 8) as *i64, ((lnl as i64) + fbase * 8) as *i64, ntot - fbase) } else { arg = atom_int(buf, ls[2], ls[3]) }
569 }
570 in_op[ninstr] = op; in_arg[ninstr] = arg; ninstr = ninstr + 1
571 } } }
572 } }
573 if h3 == 0 { if ls[1] == 2 {
574 if lsp > 0 { lsp = lsp - 1; in_op[ninstr] = 27; in_arg[ninstr] = 0; ninstr = ninstr + 1 } else { fd = 0 }
575 } }
576 if ls[1] == 0 { fd = 0 }
577 }
578 flbase[F] = fbase; fist[F] = fist0; fic[F] = ninstr - fist0
579 NF = NF + 1
580 }
581 if atom_eq(buf, ls[2], ls[3], "export" as *u8) == 1 {
582 lex_next(buf, n, ls) // "name"
583 let ename_s: i64 = ls[2]
584 let ename_l: i64 = ls[3]
585 lex_next(buf, n, ls) // (
586 lex_next(buf, n, ls) // func|memory|...
587 var is_func: i64 = 0
588 if atom_eq(buf, ls[2], ls[3], "func" as *u8) == 1 { is_func = 1 }
589 lex_next(buf, n, ls) // $name or index
590 if is_func == 1 {
591 let fidx: i64 = resolve_func(buf, ls[2], ls[3], fts, ftl, nftot)
592 let defidx: i64 = fidx - num_imports
593 if defidx >= 0 { fexp_s[defidx] = ename_s; fexp_l[defidx] = ename_l }
594 }
595 lex_next(buf, n, ls) // ) of (func ...)
596 lex_next(buf, n, ls) // ) of (export ...)
597 }
598 }
599 if h1 == 0 { if ls[1] == 2 { top = 0 } }
600 if ls[1] == 0 { top = 0 }
601 }
602 // ---- emit ----
603 let out: *u8 = sys_mmap(K_MAGIC_8388608)
604 let sc: *u8 = sys_mmap(K_MAGIC_8388608)
605 let body: *u8 = sys_mmap(K_MAGIC_2097152)
606 var p: i64 = 0
607 p = w8(out, p, 0); p = w8(out, p, 0x61); p = w8(out, p, 0x73); p = w8(out, p, 0x6D)
608 p = w8(out, p, 1); p = w8(out, p, 0); p = w8(out, p, 0); p = w8(out, p, 0)
609 // type table dedup (import + every func sig; nxc2 sigs all-i64)
610 let tnp: *i64 = sys_mmap(K_MAGIC_65536) as *i64
611 let tnr: *i64 = sys_mmap(K_MAGIC_65536) as *i64
612 let ntp: *i64 = sys_mmap(16) as *i64
613 ntp[0] = 0
614 let ftypeidx: *i64 = sys_mmap(K_MAGIC_65536) as *i64
615 var imp_tidx: i64 = 0
616 if has_import == 1 { imp_tidx = sig_find_or_add(tnp, tnr, ntp, imp_np, imp_nr) }
617 var fj: i64 = 0
618 while fj < NF { ftypeidx[fj] = sig_find_or_add(tnp, tnr, ntp, fnp[fj], fnr[fj]); fj = fj + 1 }
619 // type section
620 var s: i64 = 0
621 s = s + uleb128_encode(ntp[0], sc, s)
622 var ti: i64 = 0
623 while ti < ntp[0] { s = emit_functype(sc, s, tnp[ti], tnr[ti]); ti = ti + 1 }
624 p = emit_section(out, p, 1, sc, s)
625 // import section
626 if has_import == 1 {
627 s = 0
628 s = s + uleb128_encode(1, sc, s)
629 s = emit_name(sc, s, buf, imp_mod_s, imp_mod_l)
630 s = emit_name(sc, s, buf, imp_fld_s, imp_fld_l)
631 s = w8(sc, s, 0)
632 s = s + uleb128_encode(imp_tidx, sc, s)
633 p = emit_section(out, p, 2, sc, s)
634 }
635 // function section: NF entries
636 s = 0
637 s = s + uleb128_encode(NF, sc, s)
638 fj = 0
639 while fj < NF { s = s + uleb128_encode(ftypeidx[fj], sc, s); fj = fj + 1 }
640 p = emit_section(out, p, 3, sc, s)
641 // memory section
642 if has_mem == 1 {
643 s = 0; s = w8(sc, s, 1); s = w8(sc, s, 0); s = s + uleb128_encode(mem_min, sc, s); p = emit_section(out, p, 5, sc, s)
644 }
645 // export section: memory (if any) + every func that has an export name
646 s = 0
647 var nexp: i64 = 0
648 if has_mem == 1 { nexp = nexp + 1 }
649 fj = 0
650 while fj < NF { if fexp_l[fj] > 0 { nexp = nexp + 1 } fj = fj + 1 }
651 s = s + uleb128_encode(nexp, sc, s)
652 if has_mem == 1 { s = emit_name(sc, s, buf, mem_ns, mem_nl); s = w8(sc, s, 2); s = w8(sc, s, 0) }
653 fj = 0
654 while fj < NF { if fexp_l[fj] > 0 { s = emit_name(sc, s, buf, fexp_s[fj], fexp_l[fj]); s = w8(sc, s, 0); s = s + uleb128_encode(num_imports + fj, sc, s) } fj = fj + 1 }
655 p = emit_section(out, p, 7, sc, s)
656 // code section: NF entries (each func's locals vec + body + end)
657 s = 0
658 s = s + uleb128_encode(NF, sc, s)
659 fj = 0
660 while fj < NF {
661 var bp: i64 = 0
662 bp = emit_locals(body, bp, ltype, flbase[fj] + fnp[fj], flbase[fj] + fnp[fj] + fnl[fj])
663 var ii: i64 = fist[fj]
664 let iend: i64 = fist[fj] + fic[fj]
665 while ii < iend {
666 if in_op[ii] == 29 {
667 let off2: i64 = in_arg[ii]
668 let cnt2: i64 = bt_pool[off2]
669 bp = w8(body, bp, 0x0E)
670 bp = bp + uleb128_encode(cnt2, body, bp)
671 var k2: i64 = 0
672 while k2 < cnt2 { bp = bp + uleb128_encode(bt_pool[off2 + 1 + k2], body, bp); k2 = k2 + 1 }
673 bp = bp + uleb128_encode(bt_pool[off2 + 1 + cnt2], body, bp)
674 } else { bp = emit_instr(body, bp, in_op[ii], in_arg[ii]) }
675 ii = ii + 1
676 }
677 bp = w8(body, bp, 0x0B)
678 s = s + uleb128_encode(bp, sc, s)
679 s = wcopy(sc, s, body, bp)
680 fj = fj + 1
681 }
682 p = emit_section(out, p, 10, sc, s)
683 // write
684 let fd: i64 = sys_openat_wr(outp, 0x1a4)
685 if fd < 0 { sys_write(1, "WATC REFUSED: cannot write output\n" as *u8, 34); sys_exit(1); return 1 }
686 sys_write(fd, out, p); sys_close(fd)
687 sys_write(1, "WATC GREEN: wat->wasm\n" as *u8, 22)
688 sys_exit(0)
689 return 0
690}
691
692func main(argc: i64, argv: *i64) -> i64 {
693 if argc < 3 { sys_write(1, "usage: nx_wat_compiler <in.wat> <out.wasm>\n" as *u8, 43); sys_exit(2); return 2 }
694 return wc_compile_file(argv[1] as *u8, argv[2] as *u8)
695}