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}