nx_gramdec_lib.nx source
↩ module page · 460 lines · 17095 B
1// nx_gramdec_lib.nx -- NB10 GRAMMAR-CONSTRAINED DECODE (neuro-symbolic builder lane, 2026-07-20).
2// Makes the small maker STRUCTURALLY UNABLE to emit ungrammatical NishiLang candidates: a token is
3// sampled, its decoded bytes are run through a NishiLang function-fragment prefix automaton, and a
4// rejected token is LOGIT-MASKED and re-sampled (renormalized constrained decode). EOS is masked
5// until the function is brace-complete (the model cannot stop mid-function); once complete, decode
6// stops (accept-state). Greedy steps that the grammar accepts ride the UNCHANGED head_argmax hero
7// path -- the mask only engages on a rejection, so an already-grammatical generation is BIT-EXACT
8// with nsv_generate over its prefix.
9// HONEST SCOPE (v1): a regular+counter APPROXIMATION of NishiLang for SELF-CONTAINED integer
10// functions (the v1 judge's declared scope) -- brace/paren balance, `else` only as `} else {`
11// (kills the else-if build-break class), NO calls in the body (ident '(' reject + known-helper
12// denylist: kills abs()/min() undefined-fn breaks), unary minus on an identifier rejected while
13// negative literals stay legal, charset fence. Grammar-valid != correct: the fresh compile+run
14// grader and nx_symjudge property sweep still judge EVERY candidate (the union stays sound; the
15// mask only shrinks the candidate space). Fail-closed: a mask deadlock stops generation (honest
16// MISS), never lets an ungrammatical token through.
17// license_tier: ORIGINAL No hw writes (Rule 26).
18import "nx_nofloat_serve_core.nx"
19const GD_MAGIC_88172645463325252: i64 = 88172645463325252
20
21// masking + budget consts (pinned: part of the NB10 decode contract, not per-run knobs)
22const GD_MASK_LOGIT: i64 = 0 - 1099511627776
23const GD_MAX_TRIES: i64 = 256
24const GD_WORD_CAP: i64 = 48
25const GD_TOKTXT_CAP: i64 = 1024
26// NEGATIVE CONTROLS (ship 0/0). GD_NEG_ALLOW_ELSEIF=1 lets the else-if token class through the
27// automaton (the mutation-proof lever). GD_NEG_MASK_OFF=1 disables the whole mask (raw decode).
28const GD_NEG_ALLOW_ELSEIF: i64 = 0
29const GD_NEG_MASK_OFF: i64 = 0
30// data-driven word lists (space-delimited, leading+trailing space required by gd_word_in)
31const GD_DENY_HELPERS: *u8 = " abs min max pow sqrt fabs floor ceil "
32const GD_KEYWORDS: *u8 = " if while return let var else func as "
33
34// automaton state slot indices (gd_st / gd_sh)
35const GDI_PHASE: i64 = 0
36const GDI_LIT: i64 = 1
37const GDI_BRACE: i64 = 2
38const GDI_PAREN: i64 = 3
39const GDI_WLEN: i64 = 4
40const GDI_LASTSIG: i64 = 5
41const GDI_PMINUS: i64 = 6
42const GDI_PELSE: i64 = 7
43const GDI_PARROW: i64 = 8
44const GDI_LASTIDENT: i64 = 9
45const GDI_PREWORD: i64 = 10
46const GDI_N: i64 = 11
47
48static gd_st: *i64
49static gd_sh: *i64
50static gd_wb: *u8
51static gd_wsh: *u8
52static gdx_rej: i64
53static gdx_tb: *u8
54
55func gd_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
56
57func gd_mem() -> i64 {
58 if (gd_st as i64) == 0 {
59 gd_st = sys_mmap(GDI_N * 8) as *i64
60 gd_sh = sys_mmap(GDI_N * 8) as *i64
61 gd_wb = sys_mmap(GD_WORD_CAP)
62 gd_wsh = sys_mmap(GD_WORD_CAP)
63 }
64 return 0
65}
66func gd_reset() -> i64 {
67 gd_mem()
68 var i: i64 = 0
69 while i < GDI_N { gd_st[i] = 0; i = i + 1 }
70 return 0
71}
72func gd_isid(c: i64) -> i64 {
73 if c >= 97 { if c <= 122 { return 1 } }
74 if c >= 65 { if c <= 90 { return 1 } }
75 if c >= 48 { if c <= 57 { return 1 } }
76 if c == 95 { return 1 }
77 return 0
78}
79func gd_isdig(c: i64) -> i64 { if c >= 48 { if c <= 57 { return 1 } } return 0 }
80func gd_isws(c: i64) -> i64 {
81 if c == 32 { return 1 }
82 if c == 10 { return 1 }
83 if c == 9 { return 1 }
84 if c == 13 { return 1 }
85 return 0
86}
87// is the current word (gd_wb[0..GDI_WLEN)) present in the space-delimited list?
88func gd_word_in(list: *u8) -> i64 {
89 let wl: i64 = gd_st[GDI_WLEN]
90 if wl < 1 { return 0 }
91 var i: i64 = 0
92 var done: i64 = 0
93 var found: i64 = 0
94 while done == 0 {
95 if list[i] == (0 as u8) { done = 1 }
96 else {
97 if list[i] == (32 as u8) {
98 var j: i64 = 0
99 var ok: i64 = 1
100 while j < wl {
101 let lc: i64 = list[i + 1 + j] as i64
102 if lc != (gd_wb[j] as i64) { ok = 0; j = wl } else { j = j + 1 }
103 }
104 if ok == 1 { if (list[i + 1 + wl] as i64) == 32 { found = 1; done = 1 } }
105 }
106 if done == 0 { i = i + 1 }
107 }
108 }
109 return found
110}
111// close the current word; enforce word-level rules. 1 ok / 0 reject.
112func gd_word_end() -> i64 {
113 let wl: i64 = gd_st[GDI_WLEN]
114 if wl < 1 { return 1 }
115 var iselse: i64 = 0
116 if wl == 4 {
117 if (gd_wb[0] as i64) == 101 { if (gd_wb[1] as i64) == 108 { if (gd_wb[2] as i64) == 115 { if (gd_wb[3] as i64) == 101 { iselse = 1 } } } }
118 }
119 var ok: i64 = 1
120 if iselse == 1 {
121 if GD_NEG_ALLOW_ELSEIF == 1 {
122 gd_st[GDI_LASTIDENT] = 0
123 } else {
124 if gd_st[GDI_PREWORD] != 125 { ok = 0 }
125 gd_st[GDI_PELSE] = 1
126 gd_st[GDI_LASTIDENT] = 0
127 }
128 } else {
129 if gd_word_in(GD_DENY_HELPERS) == 1 { ok = 0 }
130 else {
131 if gd_word_in(GD_KEYWORDS) == 1 { gd_st[GDI_LASTIDENT] = 0 }
132 else { gd_st[GDI_LASTIDENT] = 1 }
133 }
134 }
135 gd_st[GDI_WLEN] = 0
136 return ok
137}
138// one character through the automaton. 1 accept / 0 reject (caller restores on reject).
139func gd_step(c: i64) -> i64 {
140 let ph: i64 = gd_st[GDI_PHASE]
141 if ph == 3 {
142 if gd_isws(c) == 1 { return 1 }
143 return 0
144 }
145 if ph == 0 {
146 if gd_st[GDI_LIT] == 0 { if gd_isws(c) == 1 { return 1 } }
147 if gd_st[GDI_LIT] < 4 {
148 let fl: *u8 = "func" as *u8
149 let want: i64 = fl[gd_st[GDI_LIT]] as i64
150 if c == want { gd_st[GDI_LIT] = gd_st[GDI_LIT] + 1; return 1 }
151 return 0
152 }
153 if c == 32 { gd_st[GDI_PHASE] = 1; return 1 }
154 return 0
155 }
156 if ph == 1 {
157 if gd_st[GDI_PARROW] == 1 {
158 if c == 62 { gd_st[GDI_PARROW] = 0; return 1 }
159 return 0
160 }
161 if c == 32 { return 1 }
162 if c == 9 { return 1 }
163 if gd_isid(c) == 1 { return 1 }
164 if c == 40 { gd_st[GDI_PAREN] = gd_st[GDI_PAREN] + 1; return 1 }
165 if c == 41 { gd_st[GDI_PAREN] = gd_st[GDI_PAREN] - 1; if gd_st[GDI_PAREN] < 0 { return 0 } return 1 }
166 if c == 44 { return 1 }
167 if c == 58 { return 1 }
168 if c == 42 { return 1 }
169 if c == 45 { gd_st[GDI_PARROW] = 1; return 1 }
170 if c == 123 {
171 if gd_st[GDI_PAREN] != 0 { return 0 }
172 gd_st[GDI_PHASE] = 2
173 gd_st[GDI_BRACE] = 1
174 gd_st[GDI_LASTSIG] = 123
175 return 1
176 }
177 return 0
178 }
179 // ph == 2: body
180 if gd_isws(c) == 1 {
181 if gd_st[GDI_WLEN] > 0 { return gd_word_end() }
182 return 1
183 }
184 if gd_st[GDI_PMINUS] == 1 {
185 if gd_isdig(c) == 1 { gd_st[GDI_PMINUS] = 0 } else { return 0 }
186 }
187 if gd_st[GDI_PELSE] == 1 {
188 if gd_st[GDI_WLEN] == 0 { if c != 123 { return 0 } gd_st[GDI_PELSE] = 0 }
189 }
190 if gd_isid(c) == 1 {
191 if gd_st[GDI_WLEN] == 0 { gd_st[GDI_PREWORD] = gd_st[GDI_LASTSIG] }
192 if gd_st[GDI_WLEN] >= GD_WORD_CAP - 1 { return 0 }
193 gd_wb[gd_st[GDI_WLEN]] = c as u8
194 gd_st[GDI_WLEN] = gd_st[GDI_WLEN] + 1
195 gd_st[GDI_LASTSIG] = c
196 return 1
197 }
198 // non-ident significant char: close any open word first (may set PELSE for `else{`)
199 if gd_st[GDI_WLEN] > 0 { if gd_word_end() == 0 { return 0 } }
200 if gd_st[GDI_PELSE] == 1 {
201 if c != 123 { return 0 }
202 gd_st[GDI_PELSE] = 0
203 }
204 if c == 40 {
205 if gd_st[GDI_LASTIDENT] == 1 { return 0 }
206 if gd_st[GDI_LASTSIG] == 41 { return 0 }
207 gd_st[GDI_PAREN] = gd_st[GDI_PAREN] + 1
208 gd_st[GDI_LASTSIG] = 40
209 return 1
210 }
211 if c == 41 {
212 gd_st[GDI_PAREN] = gd_st[GDI_PAREN] - 1
213 if gd_st[GDI_PAREN] < 0 { return 0 }
214 gd_st[GDI_LASTIDENT] = 0
215 gd_st[GDI_LASTSIG] = 41
216 return 1
217 }
218 if c == 123 {
219 if gd_st[GDI_PAREN] != 0 { return 0 }
220 gd_st[GDI_BRACE] = gd_st[GDI_BRACE] + 1
221 gd_st[GDI_LASTIDENT] = 0
222 gd_st[GDI_LASTSIG] = 123
223 return 1
224 }
225 if c == 125 {
226 if gd_st[GDI_PAREN] != 0 { return 0 }
227 gd_st[GDI_BRACE] = gd_st[GDI_BRACE] - 1
228 if gd_st[GDI_BRACE] < 0 { return 0 }
229 gd_st[GDI_LASTIDENT] = 0
230 gd_st[GDI_LASTSIG] = 125
231 if gd_st[GDI_BRACE] == 0 { gd_st[GDI_PHASE] = 3 }
232 return 1
233 }
234 if c == 45 {
235 var opnd: i64 = 0
236 if gd_st[GDI_LASTIDENT] == 1 { opnd = 1 }
237 if gd_st[GDI_LASTSIG] == 41 { opnd = 1 }
238 if opnd == 0 { gd_st[GDI_PMINUS] = 1 }
239 gd_st[GDI_LASTIDENT] = 0
240 gd_st[GDI_LASTSIG] = 45
241 return 1
242 }
243 if c == 43 { gd_st[GDI_LASTIDENT] = 0; gd_st[GDI_LASTSIG] = c; return 1 }
244 if c == 42 { gd_st[GDI_LASTIDENT] = 0; gd_st[GDI_LASTSIG] = c; return 1 }
245 if c == 47 { gd_st[GDI_LASTIDENT] = 0; gd_st[GDI_LASTSIG] = c; return 1 }
246 if c == 37 { gd_st[GDI_LASTIDENT] = 0; gd_st[GDI_LASTSIG] = c; return 1 }
247 if c == 60 { gd_st[GDI_LASTIDENT] = 0; gd_st[GDI_LASTSIG] = c; return 1 }
248 if c == 62 { gd_st[GDI_LASTIDENT] = 0; gd_st[GDI_LASTSIG] = c; return 1 }
249 if c == 61 { gd_st[GDI_LASTIDENT] = 0; gd_st[GDI_LASTSIG] = c; return 1 }
250 if c == 33 { gd_st[GDI_LASTIDENT] = 0; gd_st[GDI_LASTSIG] = c; return 1 }
251 if c == 44 { gd_st[GDI_LASTIDENT] = 0; gd_st[GDI_LASTSIG] = c; return 1 }
252 if c == 59 { gd_st[GDI_LASTIDENT] = 0; gd_st[GDI_LASTSIG] = c; return 1 }
253 if c == 58 { gd_st[GDI_LASTIDENT] = 0; gd_st[GDI_LASTSIG] = c; return 1 }
254 return 0
255}
256// feed n bytes ATOMICALLY: commit iff every byte accepts, else restore state + return 0.
257func gd_feed(s: *u8, n: i64) -> i64 {
258 gd_mem()
259 var i: i64 = 0
260 while i < GDI_N { gd_sh[i] = gd_st[i]; i = i + 1 }
261 var w: i64 = 0
262 while w < gd_st[GDI_WLEN] { gd_wsh[w] = gd_wb[w]; w = w + 1 }
263 var ok: i64 = 1
264 var j: i64 = 0
265 while j < n {
266 if ok == 1 {
267 let c: i64 = s[j] as i64
268 if gd_step(c) == 0 { ok = 0 }
269 }
270 j = j + 1
271 }
272 if ok == 0 {
273 var k: i64 = 0
274 while k < GDI_N { gd_st[k] = gd_sh[k]; k = k + 1 }
275 var w2: i64 = 0
276 while w2 < gd_st[GDI_WLEN] { gd_wb[w2] = gd_wsh[w2]; w2 = w2 + 1 }
277 return 0
278 }
279 return 1
280}
281func gd_accepting() -> i64 {
282 gd_mem()
283 if gd_st[GDI_PHASE] == 3 { return 1 }
284 return 0
285}
286// whole-text ruler (clobbers automaton state): reset + feed + accepting.
287func gd_validate(s: *u8, n: i64) -> i64 {
288 gd_reset()
289 var i: i64 = 0
290 var ok: i64 = 1
291 while i < n {
292 if ok == 1 { let c: i64 = s[i] as i64; if gd_step(c) == 0 { ok = 0 } }
293 i = i + 1
294 }
295 if ok == 0 { return 0 }
296 return gd_accepting()
297}
298// decode token id -> text bytes (same table walk as nsv_emit_piece). returns byte count.
299func gdx_tok_text(tok: i64, tb: *u8) -> i64 {
300 let off: i64 = tk_decode_off(g_nsv_buf, g_nsv_mt[2], tok)
301 let pl: i64 = nx_gguf_meta_read_string_len(g_nsv_buf, off)
302 if pl < 1 { return 0 }
303 let pp: *u8 = nx_gguf_meta_read_string_ptr(g_nsv_buf, off)
304 return td_piece_decode(pp, pl, tb, 0, GD_TOKTXT_CAP - 8)
305}
306func gdx_argmax() -> i64 {
307 let vocab: i64 = g_nsv_mt[3]
308 var best: i64 = 0
309 var bl: i64 = g_nsv_lgv[0]
310 var v: i64 = 1
311 while v < vocab { if g_nsv_lgv[v] > bl { bl = g_nsv_lgv[v]; best = v } v = v + 1 }
312 return best
313}
314// pick ONE grammar-legal token for the current hidden state. np = [temp_pm, top_p_pm, top_k, seedp].
315// Greedy first try = the UNCHANGED argmax hero path (bit-exact when the grammar accepts). On any
316// rejection the full logits are materialized, the token is masked, and the next-best is drawn.
317// Returns the token id, or -1 on mask deadlock (fail-closed). EOS is never returned (masked) --
318// except under GD_NEG_MASK_OFF, where raw behavior incl. EOS-stop is preserved.
319func gdx_pick(np: *i64) -> i64 {
320 if (gdx_tb as i64) == 0 { gdx_tb = sys_mmap(GD_TOKTXT_CAP) }
321 var lgv_ready: i64 = 0
322 var cand: i64 = 0 - 1
323 if np[0] == 0 { cand = head_argmax_cached_i32(g_nsv_hcp) }
324 else { cand = nsv_next_token(np); lgv_ready = 1 }
325 var tries: i64 = 0
326 var picked: i64 = 0 - 1
327 var done: i64 = 0
328 while done == 0 {
329 var iseos: i64 = 0
330 if cand == NSV_EOS1 { iseos = 1 }
331 if cand == NSV_EOS2 { iseos = 1 }
332 var okc: i64 = 0
333 if GD_NEG_MASK_OFF == 1 { okc = 1 }
334 else {
335 if iseos == 0 {
336 let tn: i64 = gdx_tok_text(cand, gdx_tb)
337 if tn > 0 { okc = gd_feed(gdx_tb, tn) }
338 }
339 }
340 if okc == 1 { picked = cand; done = 1 }
341 else {
342 gdx_rej = gdx_rej + 1
343 if lgv_ready == 0 {
344 let hlp: *i64 = sys_mmap(6 * 8) as *i64
345 hlp[0] = g_nsv_hcp[0]
346 hlp[1] = g_nsv_normed as i64
347 hlp[2] = g_nsv_mt[3]
348 hlp[3] = g_nsv_cfgA[1]
349 hlp[4] = g_nsv_lgv as i64
350 head_logits_cached_i32(hlp)
351 lgv_ready = 1
352 let chk: i64 = gdx_argmax()
353 if chk != cand { gd_w("GDX-LOGIT-DIVERGE (argmax head vs logits head disagree)\n" as *u8) }
354 }
355 g_nsv_lgv[cand] = GD_MASK_LOGIT
356 tries = tries + 1
357 if tries >= GD_MAX_TRIES { done = 1 }
358 else {
359 if np[0] == 0 { cand = gdx_argmax() }
360 else {
361 let sp: *i64 = sys_mmap(6 * 8) as *i64
362 sp[0] = g_nsv_lgv as i64
363 sp[1] = g_nsv_mt[3]
364 sp[2] = np[0]
365 sp[3] = np[1]
366 sp[4] = np[2]
367 sp[5] = np[3]
368 cand = nsv_sample(sp)
369 }
370 }
371 }
372 }
373 return picked
374}
375// grammar-constrained twin of nsv_generate (raw prompt path; ChatML not supported here).
376// gp layout identical. meta ADDITIVE: [6]=grammar rejections, [7]=1 accept-stop / 0 budget / 2 deadlock.
377func gdx_generate(gp: *i64) -> i64 {
378 let prompt: *u8 = gp[0] as *u8
379 let plen: i64 = gp[1]
380 var max_new: i64 = gp[2]
381 let mode: i64 = gp[3]
382 let out: *u8 = gp[4] as *u8
383 let ocap: i64 = gp[5]
384 let meta: *i64 = gp[6] as *i64
385 meta[0] = 0
386 meta[1] = 0
387 meta[2] = 0
388 meta[3] = 0
389 meta[4] = 0
390 meta[5] = 0
391 meta[6] = 0
392 meta[7] = 0
393 if g_nsv_mt[6] != 1 { meta[5] = 9; return 0 - 1 }
394 if plen < 1 { meta[5] = 1; return 0 - 1 }
395 if max_new < 1 { max_new = 24 }
396 if max_new > NSV_MAXNEW { max_new = NSV_MAXNEW }
397 let nprompt: i64 = tk_bpe_encode(g_nsv_buf, g_nsv_mt[0], g_nsv_mt[1], g_nsv_mt[2], g_nsv_mt[3], prompt, plen, g_nsv_tokp, g_nsv_tokl, g_nsv_ids)
398 if nprompt < 1 { meta[5] = 2; return 0 - 1 }
399 if nprompt >= NSV_MAXT - 2 { meta[5] = 3; return 0 - 1 }
400 if nprompt + max_new >= NSV_MAXT { max_new = NSV_MAXT - 1 - nprompt }
401 meta[0] = nprompt
402 let ne: i64 = g_nsv_cfgA[1]
403 let np: *i64 = sys_mmap(4 * 8) as *i64
404 let sd: *i64 = sys_mmap(8) as *i64
405 var seed: i64 = gp[11]
406 if seed == 0 { seed = GD_MAGIC_88172645463325252 }
407 sd[0] = seed
408 np[0] = gp[8]
409 np[1] = gp[9]
410 np[2] = gp[10]
411 np[3] = sd as i64
412 let ep: *i64 = sys_mmap(4 * 8) as *i64
413 ep[0] = out as i64
414 ep[1] = 0
415 ep[2] = ocap
416 ep[3] = 0 - 1
417 let t0: i64 = sys_now_ms()
418 gd_reset()
419 gdx_rej = 0
420 var i: i64 = 0
421 while i < nprompt {
422 dequant_row(g_nsv_buf, g_nsv_mt[4], g_nsv_mt[5], g_nsv_ids[i], ne, g_nsv_x1, g_nsv_tmp)
423 nsv_step(i, mode)
424 i = i + 1
425 }
426 rmsnorm_gamma_row_q24(g_nsv_h1, g_nsv_gout, 0, ne, g_nsv_normed, 0)
427 var T: i64 = nprompt
428 var ngen: i64 = 0
429 var stop: i64 = 0
430 while stop == 0 {
431 if ngen >= max_new { stop = 1 }
432 if T >= NSV_MAXT { stop = 1 }
433 if stop == 0 { if gd_accepting() == 1 { meta[4] = 1; meta[7] = 1; stop = 1 } }
434 if stop == 0 {
435 let nx2: i64 = gdx_pick(np)
436 if nx2 < 0 { meta[7] = 2; stop = 1 }
437 else {
438 var iseos2: i64 = 0
439 if nx2 == NSV_EOS1 { iseos2 = 1 }
440 if nx2 == NSV_EOS2 { iseos2 = 1 }
441 if iseos2 == 1 { meta[4] = 1; stop = 1 }
442 else {
443 g_nsv_ids[T] = nx2
444 ep[1] = nsv_emit_piece(nx2, ep)
445 dequant_row(g_nsv_buf, g_nsv_mt[4], g_nsv_mt[5], g_nsv_ids[T], ne, g_nsv_x1, g_nsv_tmp)
446 nsv_step(T, mode)
447 rmsnorm_gamma_row_q24(g_nsv_h1, g_nsv_gout, 0, ne, g_nsv_normed, 0)
448 T = T + 1
449 ngen = ngen + 1
450 }
451 }
452 }
453 }
454 let t1: i64 = sys_now_ms()
455 meta[1] = ngen
456 meta[2] = t1 - t0
457 if ngen > 0 { meta[3] = (t1 - t0) / (nprompt + ngen) }
458 meta[6] = gdx_rej
459 return ep[1]
460}