code wiki / (root) / nx_gramdec_lib.nx

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}