code wiki / (root) / nx_glsl_tok_lib.nx

nx_glsl_tok_lib.nx source

↩ module page · 1013 lines · 46704 B

1// nx_glsl_tok_lib.nx -- THE SHADER TOKEN RULER (gameengine S12c accept-rule instrument, 2026-09-05). 2// 3// WHY: the cast port's pre-declared accept rule says the IR-emitted GLSL must match the hand-written MVS/MFS at ZERO 4// normalised-token distance before its WGSL twin is trusted. A byte diff cannot be that ruler (the hand shader has 5// formatting freedom, comments, `.5` for `0.5`, `br/=nr` for `br=br/nr`) and a string-run ruler cannot either (it 6// has no grammar). This lib is ONE tokenizer + ONE canonicaliser + ONE compound-assignment expander + ONE differ, 7// composed in-process by nx_glsl_tokdiff (the program) and nx_glsl_tokdiff_gate (planted fixtures, both directions). 8// NORMALISATIONS, each named: whitespace and comments dropped; numeric literals canonicalised (`.5`->`0.5`, `1.`->`1.0`, 9// trailing f/F/u/U dropped); `x op= y` expanded to `x = x op y` for op in + - * / where x is an lvalue of the grammar 10// ident ( '.' ident | '[' ... ']' )* -- any other shape is left literal and COUNTED (expand_refused=), never guessed. 11// Everything else must match token for token: a renamed identifier, a moved parenthesis, a changed constant is a DIFF, 12// and the first differing token is named with both spellings. 13// Token = 3 words (start, len, kind); kinds 1 ident, 2 number, 3 operator/punct. Buffers are sized from the input 14// (one token cannot be shorter than one byte, so 3*n words is a bound that cannot overflow -- no guessed cap). 15// license_tier: ORIGINAL No hw writes (Rule 26). 16import "nx_syscalls.nx" 17 18const GT_K_IDENT: i64 = 1 19const GT_K_NUM: i64 = 2 20const GT_K_OP: i64 = 3 21const GT_TW: i64 = 3 22const GT_R_TOKA: i64 = 0 23const GT_R_TOKB: i64 = 1 24const GT_R_EXPA: i64 = 2 25const GT_R_EXPB: i64 = 3 26const GT_R_FIRSTDIFF: i64 = 4 27const GT_R_VERDICT: i64 = 5 28const GT_R_REFUSED: i64 = 6 29const GT_R_SLOTS: i64 = 20 30const GT_V_IDENTICAL: i64 = 0 31const GT_V_DIFFERS: i64 = 1 32const GT_V_UNREADABLE: i64 = 2 33const GT_CANON_CAP: i64 = 512 34const GT_NUMBUF: i64 = 32 35const C_TAB: i64 = 9 36const C_NL: i64 = 10 37const C_CR: i64 = 13 38const C_SP: i64 = 32 39const C_BANG: i64 = 33 40const C_AMP: i64 = 38 41const C_LPAR: i64 = 40 42const C_RPAR: i64 = 41 43const C_STAR: i64 = 42 44const C_PLUS: i64 = 43 45const C_MINUS: i64 = 45 46const C_DOT: i64 = 46 47const C_SLASH: i64 = 47 48const C_0: i64 = 48 49const C_9: i64 = 57 50const C_LT: i64 = 60 51const C_EQ: i64 = 61 52const C_GT: i64 = 62 53const C_A: i64 = 65 54const C_E: i64 = 69 55const C_F: i64 = 70 56const C_U: i64 = 85 57const C_Z: i64 = 90 58const C_LBR: i64 = 91 59const C_RBR: i64 = 93 60const C_USCORE: i64 = 95 61const C_a: i64 = 97 62const C_e: i64 = 101 63const C_f: i64 = 102 64const C_u: i64 = 117 65const C_z: i64 = 122 66const C_BAR: i64 = 124 67 68func gt_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 69func gt_fputs(fd: i64, s: *u8) -> i64 { sys_write(fd, s, gt_slen(s)); return 0 } 70func gt_fputn(fd: i64, v: i64) -> i64 { 71 let t: *u8 = sys_mmap(GT_NUMBUF) 72 let o: *u8 = sys_mmap(GT_NUMBUF) 73 var m: i64 = v 74 if m < 0 { sys_write(fd, "-" as *u8, 1); m = 0 - m } 75 var k: i64 = 0 76 if m == 0 { t[0] = C_0 as u8; k = 1 } 77 while m > 0 { t[k] = (C_0 + (m % 10)) as u8; m = m / 10; k = k + 1 } 78 var i: i64 = 0 79 while i < k { o[i] = t[k - 1 - i]; i = i + 1 } 80 sys_write(fd, o, k) 81 return 0 82} 83func gt_isalpha(c: i64) -> i64 { 84 if c >= C_A { if c <= C_Z { return 1 } } 85 if c >= C_a { if c <= C_z { return 1 } } 86 if c == C_USCORE { return 1 } 87 return 0 88} 89func gt_isdigit(c: i64) -> i64 { if c >= C_0 { if c <= C_9 { return 1 } } return 0 } 90func gt_isspace(c: i64) -> i64 { 91 if c == C_SP { return 1 } 92 if c == C_NL { return 1 } 93 if c == C_TAB { return 1 } 94 if c == C_CR { return 1 } 95 return 0 96} 97// is the two-char sequence at i one operator? (<< >> <= >= == != && || += -= *= /= ++ --) 98func gt_op2(a: i64, b: i64) -> i64 { 99 if b == C_EQ { 100 if a == C_LT { return 1 } 101 if a == C_GT { return 1 } 102 if a == C_EQ { return 1 } 103 if a == C_BANG { return 1 } 104 if a == C_PLUS { return 1 } 105 if a == C_MINUS { return 1 } 106 if a == C_STAR { return 1 } 107 if a == C_SLASH { return 1 } 108 } 109 if a == C_LT { if b == C_LT { return 1 } } 110 if a == C_GT { if b == C_GT { return 1 } } 111 if a == C_AMP { if b == C_AMP { return 1 } } 112 if a == C_BAR { if b == C_BAR { return 1 } } 113 if a == C_PLUS { if b == C_PLUS { return 1 } } 114 if a == C_MINUS { if b == C_MINUS { return 1 } } 115 return 0 116} 117 118// tokenize src[0,n) into toks (3 words each). Returns the token count. toks must hold 3*n words (bound, not a guess). 119func gt_tokenize(src: *u8, n: i64, toks: *i64) -> i64 { 120 var i: i64 = 0 121 var k: i64 = 0 122 while i < n { 123 let c: i64 = src[i] as i64 124 var done: i64 = 0 125 if gt_isspace(c) == 1 { i = i + 1; done = 1 } 126 if done == 0 { if c == C_SLASH { if i + 1 < n { 127 let c2: i64 = src[i + 1] as i64 128 if c2 == C_SLASH { 129 var go: i64 = 1 130 while go == 1 { if i >= n { go = 0 } else { if (src[i] as i64) == C_NL { go = 0 } else { i = i + 1 } } } 131 done = 1 132 } 133 if done == 0 { if c2 == C_STAR { 134 i = i + 2 135 var go2: i64 = 1 136 while go2 == 1 { 137 if i + 1 >= n { i = n; go2 = 0 } else { 138 if (src[i] as i64) == C_STAR { if (src[i + 1] as i64) == C_SLASH { i = i + 2; go2 = 0 } else { i = i + 1 } } else { i = i + 1 } 139 } 140 } 141 done = 1 142 } } 143 } } } 144 if done == 0 { if gt_isalpha(c) == 1 { 145 let s: i64 = i 146 var go3: i64 = 1 147 while go3 == 1 { 148 if i >= n { go3 = 0 } else { 149 let cc: i64 = src[i] as i64 150 if gt_isalpha(cc) == 1 { i = i + 1 } else { if gt_isdigit(cc) == 1 { i = i + 1 } else { go3 = 0 } } 151 } 152 } 153 toks[k * GT_TW] = s; toks[k * GT_TW + 1] = i - s; toks[k * GT_TW + 2] = GT_K_IDENT; k = k + 1 154 done = 1 155 } } 156 if done == 0 { 157 var isnum: i64 = gt_isdigit(c) 158 if isnum == 0 { if c == C_DOT { if i + 1 < n { if gt_isdigit(src[i + 1] as i64) == 1 { isnum = 1 } } } } 159 if isnum == 1 { 160 let s: i64 = i 161 var go4: i64 = 1 162 while go4 == 1 { 163 if i >= n { go4 = 0 } else { 164 let cc: i64 = src[i] as i64 165 if gt_isdigit(cc) == 1 { i = i + 1 } else { if cc == C_DOT { i = i + 1 } else { 166 if cc == C_e { i = i + 1; if i < n { if (src[i] as i64) == C_MINUS { i = i + 1 } else { if (src[i] as i64) == C_PLUS { i = i + 1 } } } } else { 167 if cc == C_E { i = i + 1; if i < n { if (src[i] as i64) == C_MINUS { i = i + 1 } else { if (src[i] as i64) == C_PLUS { i = i + 1 } } } } else { 168 if cc == C_f { i = i + 1 } else { if cc == C_F { i = i + 1 } else { if cc == C_u { i = i + 1 } else { if cc == C_U { i = i + 1 } else { go4 = 0 } } } } } } } } 169 } 170 } 171 toks[k * GT_TW] = s; toks[k * GT_TW + 1] = i - s; toks[k * GT_TW + 2] = GT_K_NUM; k = k + 1 172 done = 1 173 } 174 } 175 if done == 0 { 176 var ln: i64 = 1 177 if i + 1 < n { if gt_op2(c, src[i + 1] as i64) == 1 { ln = 2 } } 178 toks[k * GT_TW] = i; toks[k * GT_TW + 1] = ln; toks[k * GT_TW + 2] = GT_K_OP; k = k + 1 179 i = i + ln 180 } 181 } 182 return k 183} 184 185// canonical spelling of token t of src into out; returns its length. Numbers: strip f/F/u/U, `.5`->`0.5`, `1.`->`1.0`. 186func gt_canon(src: *u8, toks: *i64, t: i64, out: *u8, cap: i64) -> i64 { 187 let s: i64 = toks[t * GT_TW] 188 let ln: i64 = toks[t * GT_TW + 1] 189 let kd: i64 = toks[t * GT_TW + 2] 190 var o: i64 = 0 191 if kd != GT_K_NUM { 192 var i: i64 = 0 193 while i < ln { if o < cap { out[o] = src[s + i]; o = o + 1 } i = i + 1 } 194 return o 195 } 196 var e: i64 = ln 197 var go: i64 = 1 198 while go == 1 { 199 if e <= 0 { go = 0 } else { 200 let cc: i64 = src[s + e - 1] as i64 201 if cc == C_f { e = e - 1 } else { if cc == C_F { e = e - 1 } else { if cc == C_u { e = e - 1 } else { if cc == C_U { e = e - 1 } else { go = 0 } } } } 202 } 203 } 204 if (src[s] as i64) == C_DOT { out[o] = C_0 as u8; o = o + 1 } 205 var j: i64 = 0 206 while j < e { if o < cap { out[o] = src[s + j]; o = o + 1 } j = j + 1 } 207 if e > 0 { if (src[s + e - 1] as i64) == C_DOT { if o < cap { out[o] = C_0 as u8; o = o + 1 } } } 208 return o 209} 210 211// is token t of src the op `X=` for X in + - * / (never == <= >= !=)? 212func gt_is_compound(src: *u8, toks: *i64, t: i64) -> i64 { 213 if toks[t * GT_TW + 2] != GT_K_OP { return 0 } 214 if toks[t * GT_TW + 1] != 2 { return 0 } 215 let s: i64 = toks[t * GT_TW] 216 if (src[s + 1] as i64) != C_EQ { return 0 } 217 let a: i64 = src[s] as i64 218 if a == C_PLUS { return 1 } 219 if a == C_MINUS { return 1 } 220 if a == C_STAR { return 1 } 221 if a == C_SLASH { return 1 } 222 return 0 223} 224func gt_tok_is(src: *u8, toks: *i64, t: i64, ch: i64) -> i64 { 225 if toks[t * GT_TW + 2] != GT_K_OP { return 0 } 226 if toks[t * GT_TW + 1] != 1 { return 0 } 227 if (src[toks[t * GT_TW]] as i64) == ch { return 1 } 228 return 0 229} 230// the start index of the lvalue ending at token `last` (an ident, or `]` closing an index) under the grammar 231// ident ( '.' ident | '[' ... ']' )*; -1 when the shape is not an lvalue. 232func gt_lvalue_start(src: *u8, toks: *i64, last: i64) -> i64 { 233 var j: i64 = last 234 var go: i64 = 1 235 while go == 1 { 236 if j < 0 { return 0 - 1 } 237 if gt_tok_is(src, toks, j, C_RBR) == 1 { 238 var depth: i64 = 0 239 var found: i64 = 0 - 1 240 var q: i64 = j 241 var go2: i64 = 1 242 while go2 == 1 { 243 if q < 0 { go2 = 0 } else { 244 if gt_tok_is(src, toks, q, C_RBR) == 1 { depth = depth + 1 } 245 if gt_tok_is(src, toks, q, C_LBR) == 1 { depth = depth - 1; if depth == 0 { found = q; go2 = 0 } } 246 if go2 == 1 { q = q - 1 } 247 } 248 } 249 if found < 1 { return 0 - 1 } 250 j = found - 1 251 } 252 if j < 0 { return 0 - 1 } 253 if toks[j * GT_TW + 2] != GT_K_IDENT { return 0 - 1 } 254 if j >= 1 { if gt_tok_is(src, toks, j - 1, C_DOT) == 1 { j = j - 2 } else { go = 0 } } else { go = 0 } 255 } 256 return j 257} 258 259// expand compound assignments: xt receives the rewritten token list (3 words each; a synthesised `=` or op points INTO 260// the compound token's own bytes, so every token still names a byte range of src). Returns the new count; box[0] 261// receives the number of compound tokens whose lvalue shape was refused (left literal). xt must hold 3*(3*k) words. 262func gt_expand(src: *u8, toks: *i64, k: i64, xt: *i64, box: *i64) -> i64 { 263 var t: i64 = 0 264 var o: i64 = 0 265 box[0] = 0 266 while t < k { 267 var handled: i64 = 0 268 if gt_is_compound(src, toks, t) == 1 { if o >= 1 { 269 let ls: i64 = gt_lvalue_start(src, xt, o - 1) 270 if ls >= 0 { 271 let cs: i64 = toks[t * GT_TW] 272 xt[o * GT_TW] = cs + 1; xt[o * GT_TW + 1] = 1; xt[o * GT_TW + 2] = GT_K_OP; o = o + 1 273 var c: i64 = ls 274 let hi: i64 = o - 1 275 while c < hi { xt[o * GT_TW] = xt[c * GT_TW]; xt[o * GT_TW + 1] = xt[c * GT_TW + 1]; xt[o * GT_TW + 2] = xt[c * GT_TW + 2]; o = o + 1; c = c + 1 } 276 xt[o * GT_TW] = cs; xt[o * GT_TW + 1] = 1; xt[o * GT_TW + 2] = GT_K_OP; o = o + 1 277 handled = 1 278 } else { box[0] = box[0] + 1 } 279 } } 280 if handled == 0 { xt[o * GT_TW] = toks[t * GT_TW]; xt[o * GT_TW + 1] = toks[t * GT_TW + 1]; xt[o * GT_TW + 2] = toks[t * GT_TW + 2]; o = o + 1 } 281 t = t + 1 282 } 283 return o 284} 285 286// the ruler: fills r (GT_R_SLOTS words) and returns the verdict code. 287func gt_diff(a: *u8, na: i64, b: *u8, nb: i64, r: *i64) -> i64 { 288 let ta: *i64 = sys_mmap(GT_TW * 8 * (na + 1)) as *i64 289 let tb: *i64 = sys_mmap(GT_TW * 8 * (nb + 1)) as *i64 290 let ka: i64 = gt_tokenize(a, na, ta) 291 let kb: i64 = gt_tokenize(b, nb, tb) 292 let xa: *i64 = sys_mmap(GT_TW * 8 * (3 * ka + 3)) as *i64 293 let xb: *i64 = sys_mmap(GT_TW * 8 * (3 * kb + 3)) as *i64 294 let boxa: *i64 = sys_mmap(16) as *i64 295 let boxb: *i64 = sys_mmap(16) as *i64 296 let ea: i64 = gt_expand(a, ta, ka, xa, boxa) 297 let eb: i64 = gt_expand(b, tb, kb, xb, boxb) 298 r[GT_R_TOKA] = ka; r[GT_R_TOKB] = kb; r[GT_R_EXPA] = ea; r[GT_R_EXPB] = eb 299 r[GT_R_REFUSED] = boxa[0] + boxb[0] 300 r[GT_R_FIRSTDIFF] = 0 - 1 301 let ca: *u8 = sys_mmap(GT_CANON_CAP) 302 let cb: *u8 = sys_mmap(GT_CANON_CAP) 303 var i: i64 = 0 304 var lim: i64 = ea 305 if eb < lim { lim = eb } 306 var go: i64 = 1 307 while go == 1 { 308 if i >= lim { go = 0 } else { 309 let la: i64 = gt_canon(a, xa, i, ca, GT_CANON_CAP) 310 let lb: i64 = gt_canon(b, xb, i, cb, GT_CANON_CAP) 311 var same: i64 = 1 312 if la != lb { same = 0 } else { 313 var q: i64 = 0 314 while q < la { if ca[q] != cb[q] { same = 0; q = la } else { q = q + 1 } } 315 } 316 if same == 0 { r[GT_R_FIRSTDIFF] = i; go = 0 } else { i = i + 1 } 317 } 318 } 319 if r[GT_R_FIRSTDIFF] < 0 { if ea != eb { r[GT_R_FIRSTDIFF] = lim } } 320 if r[GT_R_FIRSTDIFF] < 0 { r[GT_R_VERDICT] = GT_V_IDENTICAL } else { r[GT_R_VERDICT] = GT_V_DIFFERS } 321 return r[GT_R_VERDICT] 322} 323 324// print the canonical token i of the EXPANDED stream of src (re-derived; the report never trusts a cached string) 325func gt_put_expanded_tok(fd: i64, src: *u8, n: i64, i: i64) -> i64 { 326 let t: *i64 = sys_mmap(GT_TW * 8 * (n + 1)) as *i64 327 let k: i64 = gt_tokenize(src, n, t) 328 let x: *i64 = sys_mmap(GT_TW * 8 * (3 * k + 3)) as *i64 329 let bx: *i64 = sys_mmap(16) as *i64 330 let e: i64 = gt_expand(src, t, k, x, bx) 331 if i < 0 { gt_fputs(fd, "-" as *u8); return 0 } 332 if i >= e { gt_fputs(fd, "<end>" as *u8); return 0 } 333 let c: *u8 = sys_mmap(GT_CANON_CAP) 334 let l: i64 = gt_canon(src, x, i, c, GT_CANON_CAP) 335 sys_write(fd, c, l) 336 return 0 337} 338func gt_report(fd: i64, a: *u8, na: i64, b: *u8, nb: i64, r: *i64) -> i64 { 339 gt_fputs(fd, "tokens_a=" as *u8); gt_fputn(fd, r[GT_R_TOKA]) 340 gt_fputs(fd, " tokens_b=" as *u8); gt_fputn(fd, r[GT_R_TOKB]) 341 gt_fputs(fd, " expanded_a=" as *u8); gt_fputn(fd, r[GT_R_EXPA]) 342 gt_fputs(fd, " expanded_b=" as *u8); gt_fputn(fd, r[GT_R_EXPB]) 343 gt_fputs(fd, " expand_refused=" as *u8); gt_fputn(fd, r[GT_R_REFUSED]) 344 gt_fputs(fd, " first_diff=" as *u8); gt_fputn(fd, r[GT_R_FIRSTDIFF]) 345 if r[GT_R_FIRSTDIFF] >= 0 { 346 gt_fputs(fd, " a_tok=" as *u8); gt_put_expanded_tok(fd, a, na, r[GT_R_FIRSTDIFF]) 347 gt_fputs(fd, " b_tok=" as *u8); gt_put_expanded_tok(fd, b, nb, r[GT_R_FIRSTDIFF]) 348 } 349 gt_fputs(fd, "\n" as *u8) 350 if r[GT_R_VERDICT] == GT_V_IDENTICAL { gt_fputs(fd, "verdict=IDENTICAL\n" as *u8) } else { gt_fputs(fd, "verdict=DIFFERS\n" as *u8) } 351 return 0 352} 353 354// ---- S12c-2 (2026-09-05): THE CANONICAL FORM -- redundant parentheses, multi-declarators, brace-less branches, counted loops ---- 355// WHY: the IR backends parenthesise EVERY binary, negation and select node ((a+b)*c is spelled ((a+b)*c), -x is (-x), 356// c?a:b is (c?a:b)), spell one declarator per statement, brace every branch and lower a counted loop to 357// init;while(true){if(until){break;}body;step;} -- while a person writes the minimal form. Token identity between the two 358// is therefore unreachable BY SPELLING while the programs are identical BY STRUCTURE. The honest ruler is structural: both 359// sides are parsed with the GLSL ES 3.00 precedence table into one expression tree per statement and RE-PRINTED in the 360// backend's own fully-parenthesised form, so a redundant parenthesis vanishes and a meaningful one survives ((a+b)*c and 361// a+b*c print differently because they ARE different). The emitters are untouched: a live shader's bytes never move for a 362// ruler's convenience, and the world shader's pinned hand text stays pinned. 363// NORMALISATIONS ADDED, each named and each bitten in the gate: (1) precedence re-parenthesisation of every expression; 364// (2) `T a=x,b=y;` -> `T a=x;T b=y;`; (3) a brace-less if/else/while body is braced and `else if` becomes `else{if ...}`; 365// (4) `for(init;cond;step)body` -> `init;while(true){if(not-cond){break;}body;step;}` with `i++`/`i--`/`++i` spelled 366// `i=(i+1)` / `i=(i-1)`; (5) not-of-a-comparison prints as the inverted comparison (`not-(a<b)` -> `(a>=b)`, six ops) 367// because the IR carries no unary not and its authors spell the inverted comparison -- the ONE semantic caveat of this 368// ruler: it identifies two forms that differ only on NaN, declared here rather than hidden. An input the grammar cannot 369// parse is REFUSED and the refusing token is named by byte offset and spelling (verdict UNREADABLE), never passed through. 370// GRAMMAR COVERED: literals, identifiers, calls/constructors/casts (an identifier followed by '('), [index], .member, 371// unary - and not, prefix/postfix ++ --, the binary table || && | ^ & == != < > <= >= << >> + - * / %, ?:, =, declarations 372// (T name[N]=expr with const/highp/mediump/lowp qualifiers), if/else, while, for, return, discard, break, continue, blocks; 373// module scope is copied token for token except that the right side of an `=` is canonicalised. 374// SHAPE: ONE recursive-descent function per layer that only ever calls ITSELF (gp_parse over modes, gp_stmt over wrap), so 375// no function is referenced before its definition and there is exactly one parser. 376const GN_KIND: i64 = 0 377const GN_TOK: i64 = 1 378const GN_A: i64 = 2 379const GN_B: i64 = 3 380const GN_C: i64 = 4 381const GN_NEXT: i64 = 5 382const GN_SLOTS: i64 = 6 383const GN_LEAF: i64 = 1 384const GN_BIN: i64 = 2 385const GN_UN: i64 = 3 386const GN_TERN: i64 = 4 387const GN_CALL: i64 = 5 388const GN_INDEX: i64 = 6 389const GN_SWZ: i64 = 7 390const GN_ASSIGN: i64 = 8 391const GN_INCDEC: i64 = 9 392const GP_POS: i64 = 0 393const GP_NTOK: i64 = 1 394const GP_OUT: i64 = 2 395const GP_ERR: i64 = 3 396const GP_NCOUNT: i64 = 4 397const GP_SRC: i64 = 5 398const GP_TOKS: i64 = 6 399const GP_NODES: i64 = 7 400const GP_OUTBUF: i64 = 8 401const GP_CAP: i64 = 9 402const GP_OVER: i64 = 10 403const GP_TMP: i64 = 11 404const GP_SLOTS: i64 = 12 405const GP_M_ASSIGN: i64 = 1 406const GP_M_TERN: i64 = 2 407const GP_M_BIN: i64 = 3 408const GP_M_UNARY: i64 = 4 409const GP_P_LOR: i64 = 1 410const GP_P_LAND: i64 = 2 411const GP_P_BOR: i64 = 3 412const GP_P_BXOR: i64 = 4 413const GP_P_BAND: i64 = 5 414const GP_P_EQ: i64 = 6 415const GP_P_REL: i64 = 7 416const GP_P_SHIFT: i64 = 8 417const GP_P_ADD: i64 = 9 418const GP_P_MUL: i64 = 10 419const GP_INV_NONE: i64 = 0 420const GP_INV_GE: i64 = 1 421const GP_INV_LE: i64 = 2 422const GP_INV_GT: i64 = 3 423const GP_INV_LT: i64 = 4 424const GP_INV_NE: i64 = 5 425const GP_INV_EQ: i64 = 6 426const C_QMARK: i64 = 63 427const C_COLON: i64 = 58 428const C_SEMI: i64 = 59 429const C_COMMA: i64 = 44 430const C_LBRACE: i64 = 123 431const C_RBRACE: i64 = 125 432const C_CARET: i64 = 94 433const C_PCT: i64 = 37 434const C_1: i64 = 49 435// canonical text bound, derived: a token of one byte prints as at most "( x ) " (6 bytes) under wrapping and a `for` 436// lowering adds a fixed head, so MULT bytes per source byte plus HEAD is a bound, not a guess 437const GT_CANON_OUT_MULT: i64 = 8 438const GT_CANON_OUT_HEAD: i64 = 4096 439// result slots past the incumbent's 7 (GT_R_SLOTS is 20, declared at the head of this file) 440const GT_R_CANA: i64 = 7 441const GT_R_CANB: i64 = 8 442const GT_R_ERRA: i64 = 9 443const GT_R_ERRB: i64 = 10 444const GT_R_ERRLA: i64 = 11 445const GT_R_ERRLB: i64 = 12 446const GT_R_CAPTR_A: i64 = 13 447const GT_R_CALEN_A: i64 = 14 448const GT_R_CAPTR_B: i64 = 15 449const GT_R_CALEN_B: i64 = 16 450const GT_R_SRCA: i64 = 17 451const GT_R_SRCB: i64 = 18 452const GT_R_OVER: i64 = 19 453 454func gp_tk(ps: *i64, t: i64, w: i64) -> i64 { let tk: *i64 = ps[GP_TOKS] as *i64; return tk[t * GT_TW + w] } 455func gp_byte(ps: *i64, t: i64, i: i64) -> i64 { let s: *u8 = ps[GP_SRC] as *u8; return s[gp_tk(ps, t, 0) + i] as i64 } 456func gp_kind(ps: *i64, t: i64) -> i64 { 457 if t < 0 { return 0 } 458 if t >= ps[GP_NTOK] { return 0 } 459 return gp_tk(ps, t, 2) 460} 461func gp_is1(ps: *i64, t: i64, ch: i64) -> i64 { 462 if gp_kind(ps, t) != GT_K_OP { return 0 } 463 if gp_tk(ps, t, 1) != 1 { return 0 } 464 if gp_byte(ps, t, 0) == ch { return 1 } 465 return 0 466} 467func gp_is2(ps: *i64, t: i64, a: i64, b: i64) -> i64 { 468 if gp_kind(ps, t) != GT_K_OP { return 0 } 469 if gp_tk(ps, t, 1) != 2 { return 0 } 470 if gp_byte(ps, t, 0) != a { return 0 } 471 if gp_byte(ps, t, 1) == b { return 1 } 472 return 0 473} 474func gp_isword(ps: *i64, t: i64, w: *u8) -> i64 { 475 if gp_kind(ps, t) != GT_K_IDENT { return 0 } 476 let ln: i64 = gp_tk(ps, t, 1) 477 if gt_slen(w) != ln { return 0 } 478 var i: i64 = 0 479 while i < ln { if gp_byte(ps, t, i) != (w[i] as i64) { return 0 } i = i + 1 } 480 return 1 481} 482func gp_fail(ps: *i64, t: i64) -> i64 { if ps[GP_ERR] < 0 { ps[GP_ERR] = t } return 0 } 483func gp_n(ps: *i64, id: i64, w: i64) -> i64 { let nd: *i64 = ps[GP_NODES] as *i64; return nd[id * GN_SLOTS + w] } 484func gp_nset(ps: *i64, id: i64, w: i64, v: i64) -> i64 { let nd: *i64 = ps[GP_NODES] as *i64; nd[id * GN_SLOTS + w] = v; return 0 } 485func gp_node(ps: *i64, kind: i64, tok: i64, a: i64, b: i64, c: i64) -> i64 { 486 let id: i64 = ps[GP_NCOUNT] 487 ps[GP_NCOUNT] = id + 1 488 gp_nset(ps, id, GN_KIND, kind); gp_nset(ps, id, GN_TOK, tok); gp_nset(ps, id, GN_A, a); gp_nset(ps, id, GN_B, b); gp_nset(ps, id, GN_C, c); gp_nset(ps, id, GN_NEXT, 0) 489 return id 490} 491func gp_putc(ps: *i64, ch: i64) -> i64 { 492 if ps[GP_OUT] + 1 >= ps[GP_CAP] { ps[GP_OVER] = 1; return 0 } 493 let o: *u8 = ps[GP_OUTBUF] as *u8 494 o[ps[GP_OUT]] = ch as u8 495 ps[GP_OUT] = ps[GP_OUT] + 1 496 return 0 497} 498func gp_emit_c(ps: *i64, ch: i64) -> i64 { gp_putc(ps, ch); gp_putc(ps, C_SP); return 0 } 499func gp_emit_s(ps: *i64, s: *u8) -> i64 { 500 var i: i64 = 0 501 while s[i] != (0 as u8) { gp_putc(ps, s[i] as i64); i = i + 1 } 502 gp_putc(ps, C_SP) 503 return 0 504} 505// the canonical spelling of token t (numbers canonicalised by gt_canon), then one space so re-tokenising cannot merge tokens 506func gp_emit_tok(ps: *i64, t: i64) -> i64 { 507 let tmp: *u8 = ps[GP_TMP] as *u8 508 let l: i64 = gt_canon(ps[GP_SRC] as *u8, ps[GP_TOKS] as *i64, t, tmp, GT_CANON_CAP) 509 var i: i64 = 0 510 while i < l { gp_putc(ps, tmp[i] as i64); i = i + 1 } 511 gp_putc(ps, C_SP) 512 return 0 513} 514func gp_take(ps: *i64) -> i64 { ps[GP_POS] = ps[GP_POS] + 1; return 0 } 515// the one-char op ch at the cursor: emit + consume, or refuse there 516func gp_expect(ps: *i64, ch: i64) -> i64 { 517 if gp_is1(ps, ps[GP_POS], ch) == 0 { return gp_fail(ps, ps[GP_POS]) } 518 gp_emit_c(ps, ch); gp_take(ps) 519 return 1 520} 521// the binary precedence of token t (GLSL ES 3.00 table, higher binds tighter), 0 when t is not a binary operator 522func gp_binprec(ps: *i64, t: i64) -> i64 { 523 if gp_kind(ps, t) != GT_K_OP { return 0 } 524 let ln: i64 = gp_tk(ps, t, 1) 525 let a: i64 = gp_byte(ps, t, 0) 526 if ln == 2 { 527 let b: i64 = gp_byte(ps, t, 1) 528 if a == C_BAR { if b == C_BAR { return GP_P_LOR } } 529 if a == C_AMP { if b == C_AMP { return GP_P_LAND } } 530 if b == C_EQ { 531 if a == C_EQ { return GP_P_EQ } 532 if a == C_BANG { return GP_P_EQ } 533 if a == C_LT { return GP_P_REL } 534 if a == C_GT { return GP_P_REL } 535 } 536 if a == C_LT { if b == C_LT { return GP_P_SHIFT } } 537 if a == C_GT { if b == C_GT { return GP_P_SHIFT } } 538 return 0 539 } 540 if a == C_BAR { return GP_P_BOR } 541 if a == C_CARET { return GP_P_BXOR } 542 if a == C_AMP { return GP_P_BAND } 543 if a == C_LT { return GP_P_REL } 544 if a == C_GT { return GP_P_REL } 545 if a == C_PLUS { return GP_P_ADD } 546 if a == C_MINUS { return GP_P_ADD } 547 if a == C_STAR { return GP_P_MUL } 548 if a == C_SLASH { return GP_P_MUL } 549 if a == C_PCT { return GP_P_MUL } 550 return 0 551} 552// ONE recursive descent over the expanded token stream; `mode` selects the layer (assignment > ternary > binary(minp) > 553// unary+postfix) so the function only ever calls ITSELF. Returns a node id, 0 on refusal (the refusing token is recorded once). 554func gp_parse(ps: *i64, mode: i64, minp: i64) -> i64 { 555 if mode == GP_M_ASSIGN { 556 let l: i64 = gp_parse(ps, GP_M_TERN, 0) 557 if l == 0 { return 0 } 558 // a compound assignment is parsed STRUCTURALLY: `x op= y` is ASSIGN(x, BIN(op, x, y)) with y a whole assignment- 559 // expression -- so `x*=a+b` is x=(x*(a+b)), never the token-level x=x*a+b the raw expander spells (that expander cannot 560 // see where y ends; this parser can -- measured 2026-09-05 on the hand MVS line `aP2.x*=1.+.08*w2`, which the raw 561 // expansion would have identified with the WRONG tree). The BIN carries GN_C=1 so its printer spells the single- 562 // character op of the compound token. 563 let tc: i64 = ps[GP_POS] 564 if gt_is_compound(ps[GP_SRC] as *u8, ps[GP_TOKS] as *i64, tc) == 1 { 565 gp_take(ps) 566 let rc: i64 = gp_parse(ps, GP_M_ASSIGN, 0) 567 if rc == 0 { return 0 } 568 return gp_node(ps, GN_ASSIGN, 0, l, gp_node(ps, GN_BIN, tc, l, rc, 1), 0) 569 } 570 if gp_is1(ps, ps[GP_POS], C_EQ) == 1 { 571 gp_take(ps) 572 let r: i64 = gp_parse(ps, GP_M_ASSIGN, 0) 573 if r == 0 { return 0 } 574 return gp_node(ps, GN_ASSIGN, 0, l, r, 0) 575 } 576 return l 577 } 578 if mode == GP_M_TERN { 579 let c: i64 = gp_parse(ps, GP_M_BIN, GP_P_LOR) 580 if c == 0 { return 0 } 581 if gp_is1(ps, ps[GP_POS], C_QMARK) == 1 { 582 gp_take(ps) 583 let a: i64 = gp_parse(ps, GP_M_TERN, 0) 584 if a == 0 { return 0 } 585 if gp_is1(ps, ps[GP_POS], C_COLON) == 0 { return gp_fail(ps, ps[GP_POS]) } 586 gp_take(ps) 587 let b: i64 = gp_parse(ps, GP_M_TERN, 0) 588 if b == 0 { return 0 } 589 return gp_node(ps, GN_TERN, 0, c, a, b) 590 } 591 return c 592 } 593 if mode == GP_M_BIN { 594 var left: i64 = gp_parse(ps, GP_M_UNARY, 0) 595 if left == 0 { return 0 } 596 var go: i64 = 1 597 while go == 1 { 598 let tb: i64 = ps[GP_POS] 599 let pr: i64 = gp_binprec(ps, tb) 600 if pr < minp { go = 0 } else { if pr == 0 { go = 0 } else { 601 gp_take(ps) 602 let right: i64 = gp_parse(ps, GP_M_BIN, pr + 1) 603 if right == 0 { return 0 } 604 left = gp_node(ps, GN_BIN, tb, left, right, 0) 605 } } 606 } 607 return left 608 } 609 // GP_M_UNARY: prefix operators, then a primary with its postfix chain 610 let t0: i64 = ps[GP_POS] 611 if gp_is1(ps, t0, C_MINUS) == 1 { gp_take(ps); let e1: i64 = gp_parse(ps, GP_M_UNARY, 0); if e1 == 0 { return 0 } return gp_node(ps, GN_UN, C_MINUS, e1, 0, 0) } 612 if gp_is1(ps, t0, C_BANG) == 1 { gp_take(ps); let e2: i64 = gp_parse(ps, GP_M_UNARY, 0); if e2 == 0 { return 0 } return gp_node(ps, GN_UN, C_BANG, e2, 0, 0) } 613 if gp_is2(ps, t0, C_PLUS, C_PLUS) == 1 { gp_take(ps); let e3: i64 = gp_parse(ps, GP_M_UNARY, 0); if e3 == 0 { return 0 } return gp_node(ps, GN_INCDEC, t0, e3, 0, 0) } 614 if gp_is2(ps, t0, C_MINUS, C_MINUS) == 1 { gp_take(ps); let e4: i64 = gp_parse(ps, GP_M_UNARY, 0); if e4 == 0 { return 0 } return gp_node(ps, GN_INCDEC, t0, e4, 0, 0) } 615 var base: i64 = 0 616 let k: i64 = gp_kind(ps, t0) 617 if k == GT_K_NUM { gp_take(ps); base = gp_node(ps, GN_LEAF, t0, 0, 0, 0) } 618 if k == GT_K_IDENT { 619 gp_take(ps) 620 if gp_is1(ps, t0 + 1, C_LPAR) == 1 { 621 gp_take(ps) 622 base = gp_node(ps, GN_CALL, t0, 0, 0, 0) 623 if gp_is1(ps, ps[GP_POS], C_RPAR) == 1 { gp_take(ps) } else { 624 var last: i64 = 0 625 var ga: i64 = 1 626 while ga == 1 { 627 let ea: i64 = gp_parse(ps, GP_M_ASSIGN, 0) 628 if ea == 0 { return 0 } 629 if last == 0 { gp_nset(ps, base, GN_A, ea) } else { gp_nset(ps, last, GN_NEXT, ea) } 630 last = ea 631 if gp_is1(ps, ps[GP_POS], C_COMMA) == 1 { gp_take(ps) } else { 632 if gp_is1(ps, ps[GP_POS], C_RPAR) == 1 { gp_take(ps); ga = 0 } else { return gp_fail(ps, ps[GP_POS]) } 633 } 634 } 635 } 636 } else { base = gp_node(ps, GN_LEAF, t0, 0, 0, 0) } 637 } 638 if base == 0 { if gp_is1(ps, t0, C_LPAR) == 1 { 639 gp_take(ps) 640 let g: i64 = gp_parse(ps, GP_M_ASSIGN, 0) 641 if g == 0 { return 0 } 642 if gp_is1(ps, ps[GP_POS], C_RPAR) == 0 { return gp_fail(ps, ps[GP_POS]) } 643 gp_take(ps) 644 base = g 645 } } 646 if base == 0 { return gp_fail(ps, t0) } 647 var go2: i64 = 1 648 while go2 == 1 { 649 let tp: i64 = ps[GP_POS] 650 if gp_is1(ps, tp, C_LBR) == 1 { 651 gp_take(ps) 652 let ix: i64 = gp_parse(ps, GP_M_ASSIGN, 0) 653 if ix == 0 { return 0 } 654 if gp_is1(ps, ps[GP_POS], C_RBR) == 0 { return gp_fail(ps, ps[GP_POS]) } 655 gp_take(ps) 656 base = gp_node(ps, GN_INDEX, 0, base, ix, 0) 657 } else { if gp_is1(ps, tp, C_DOT) == 1 { 658 if gp_kind(ps, tp + 1) != GT_K_IDENT { return gp_fail(ps, tp + 1) } 659 gp_take(ps); gp_take(ps) 660 base = gp_node(ps, GN_SWZ, tp + 1, base, 0, 0) 661 } else { if gp_is2(ps, tp, C_PLUS, C_PLUS) == 1 { gp_take(ps); base = gp_node(ps, GN_INCDEC, tp, base, 0, 0) } 662 else { if gp_is2(ps, tp, C_MINUS, C_MINUS) == 1 { gp_take(ps); base = gp_node(ps, GN_INCDEC, tp, base, 0, 0) } 663 else { go2 = 0 } } } } 664 } 665 return base 666} 667// the inverted spelling of a comparison operator token, or GP_INV_NONE 668func gp_inv(ps: *i64, t: i64) -> i64 { 669 if gp_kind(ps, t) != GT_K_OP { return GP_INV_NONE } 670 let ln: i64 = gp_tk(ps, t, 1) 671 let a: i64 = gp_byte(ps, t, 0) 672 if ln == 1 { 673 if a == C_LT { return GP_INV_GE } 674 if a == C_GT { return GP_INV_LE } 675 return GP_INV_NONE 676 } 677 if gp_byte(ps, t, 1) != C_EQ { return GP_INV_NONE } 678 if a == C_LT { return GP_INV_GT } 679 if a == C_GT { return GP_INV_LT } 680 if a == C_EQ { return GP_INV_NE } 681 if a == C_BANG { return GP_INV_EQ } 682 return GP_INV_NONE 683} 684func gp_emit_inv(ps: *i64, inv: i64) -> i64 { 685 if inv == GP_INV_GE { gp_putc(ps, C_GT); gp_putc(ps, C_EQ) } 686 if inv == GP_INV_LE { gp_putc(ps, C_LT); gp_putc(ps, C_EQ) } 687 if inv == GP_INV_GT { gp_putc(ps, C_GT) } 688 if inv == GP_INV_LT { gp_putc(ps, C_LT) } 689 if inv == GP_INV_NE { gp_putc(ps, C_BANG); gp_putc(ps, C_EQ) } 690 if inv == GP_INV_EQ { gp_putc(ps, C_EQ); gp_putc(ps, C_EQ) } 691 gp_putc(ps, C_SP) 692 return 0 693} 694// print a tree in the backend's own shape: every binary / unary / select node wrapped, calls and postfix bare 695func gp_print(ps: *i64, id: i64) -> i64 { 696 if id == 0 { return 0 } 697 let k: i64 = gp_n(ps, id, GN_KIND) 698 let a: i64 = gp_n(ps, id, GN_A) 699 let b: i64 = gp_n(ps, id, GN_B) 700 if k == GN_LEAF { gp_emit_tok(ps, gp_n(ps, id, GN_TOK)); return 0 } 701 if k == GN_BIN { 702 gp_emit_c(ps, C_LPAR); gp_print(ps, a) 703 // GN_C=1 marks the BIN synthesised from a compound assignment: spell the op's first byte, never the `op=` token 704 if gp_n(ps, id, GN_C) == 1 { gp_emit_c(ps, gp_byte(ps, gp_n(ps, id, GN_TOK), 0)) } else { gp_emit_tok(ps, gp_n(ps, id, GN_TOK)) } 705 gp_print(ps, b); gp_emit_c(ps, C_RPAR) 706 return 0 707 } 708 if k == GN_UN { 709 let ch: i64 = gp_n(ps, id, GN_TOK) 710 if ch == C_BANG { if gp_n(ps, a, GN_KIND) == GN_BIN { 711 let inv: i64 = gp_inv(ps, gp_n(ps, a, GN_TOK)) 712 if inv != GP_INV_NONE { 713 gp_emit_c(ps, C_LPAR); gp_print(ps, gp_n(ps, a, GN_A)); gp_emit_inv(ps, inv); gp_print(ps, gp_n(ps, a, GN_B)); gp_emit_c(ps, C_RPAR) 714 return 0 715 } 716 } } 717 gp_emit_c(ps, C_LPAR); gp_emit_c(ps, ch); gp_print(ps, a); gp_emit_c(ps, C_RPAR) 718 return 0 719 } 720 if k == GN_TERN { gp_emit_c(ps, C_LPAR); gp_print(ps, a); gp_emit_c(ps, C_QMARK); gp_print(ps, b); gp_emit_c(ps, C_COLON); gp_print(ps, gp_n(ps, id, GN_C)); gp_emit_c(ps, C_RPAR); return 0 } 721 if k == GN_CALL { 722 gp_emit_tok(ps, gp_n(ps, id, GN_TOK)); gp_emit_c(ps, C_LPAR) 723 var e: i64 = a 724 var first: i64 = 1 725 while e != 0 { 726 if first == 0 { gp_emit_c(ps, C_COMMA) } 727 first = 0 728 gp_print(ps, e) 729 e = gp_n(ps, e, GN_NEXT) 730 } 731 gp_emit_c(ps, C_RPAR) 732 return 0 733 } 734 if k == GN_INDEX { gp_print(ps, a); gp_emit_c(ps, C_LBR); gp_print(ps, b); gp_emit_c(ps, C_RBR); return 0 } 735 if k == GN_SWZ { gp_print(ps, a); gp_emit_c(ps, C_DOT); gp_emit_tok(ps, gp_n(ps, id, GN_TOK)); return 0 } 736 if k == GN_ASSIGN { gp_print(ps, a); gp_emit_c(ps, C_EQ); gp_print(ps, b); return 0 } 737 if k == GN_INCDEC { 738 gp_print(ps, a); gp_emit_c(ps, C_EQ); gp_emit_c(ps, C_LPAR); gp_print(ps, a); gp_emit_c(ps, gp_byte(ps, gp_n(ps, id, GN_TOK), 0)); gp_emit_c(ps, C_1); gp_emit_c(ps, C_RPAR) 739 return 0 740 } 741 return 0 742} 743// one statement; wrap=1 braces a statement that is not itself a block (an if / else / while body). Returns 1, or 0 refused. 744func gp_stmt(ps: *i64, wrap: i64) -> i64 { 745 let t: i64 = ps[GP_POS] 746 if t >= ps[GP_NTOK] { return gp_fail(ps, t) } 747 if gp_is1(ps, t, C_LBRACE) == 1 { 748 gp_take(ps); gp_emit_c(ps, C_LBRACE) 749 var gb0: i64 = 1 750 while gb0 == 1 { 751 if ps[GP_POS] >= ps[GP_NTOK] { return gp_fail(ps, ps[GP_POS]) } 752 if gp_is1(ps, ps[GP_POS], C_RBRACE) == 1 { gb0 = 0 } else { if gp_stmt(ps, 0) == 0 { return 0 } } 753 } 754 gp_take(ps); gp_emit_c(ps, C_RBRACE) 755 return 1 756 } 757 if wrap == 1 { gp_emit_c(ps, C_LBRACE) } 758 var handled: i64 = 0 759 if gp_isword(ps, t, "if" as *u8) == 1 { 760 handled = 1 761 gp_take(ps); gp_emit_s(ps, "if" as *u8) 762 if gp_expect(ps, C_LPAR) == 0 { return 0 } 763 let c: i64 = gp_parse(ps, GP_M_ASSIGN, 0) 764 if c == 0 { return 0 } 765 gp_print(ps, c) 766 if gp_expect(ps, C_RPAR) == 0 { return 0 } 767 if gp_stmt(ps, 1) == 0 { return 0 } 768 if gp_isword(ps, ps[GP_POS], "else" as *u8) == 1 { 769 gp_take(ps); gp_emit_s(ps, "else" as *u8) 770 if gp_stmt(ps, 1) == 0 { return 0 } 771 } 772 } 773 if handled == 0 { if gp_isword(ps, t, "while" as *u8) == 1 { 774 handled = 1 775 gp_take(ps); gp_emit_s(ps, "while" as *u8) 776 if gp_expect(ps, C_LPAR) == 0 { return 0 } 777 let c2: i64 = gp_parse(ps, GP_M_ASSIGN, 0) 778 if c2 == 0 { return 0 } 779 gp_print(ps, c2) 780 if gp_expect(ps, C_RPAR) == 0 { return 0 } 781 if gp_stmt(ps, 1) == 0 { return 0 } 782 } } 783 if handled == 0 { if gp_isword(ps, t, "for" as *u8) == 1 { 784 handled = 1 785 gp_take(ps) 786 if gp_is1(ps, ps[GP_POS], C_LPAR) == 0 { return gp_fail(ps, ps[GP_POS]) } 787 gp_take(ps) 788 if gp_is1(ps, ps[GP_POS], C_SEMI) == 1 { gp_take(ps) } else { if gp_stmt(ps, 0) == 0 { return 0 } } 789 gp_emit_s(ps, "while" as *u8); gp_emit_c(ps, C_LPAR); gp_emit_s(ps, "true" as *u8); gp_emit_c(ps, C_RPAR); gp_emit_c(ps, C_LBRACE) 790 if gp_is1(ps, ps[GP_POS], C_SEMI) == 1 { gp_take(ps) } else { 791 let cnd: i64 = gp_parse(ps, GP_M_ASSIGN, 0) 792 if cnd == 0 { return 0 } 793 if gp_is1(ps, ps[GP_POS], C_SEMI) == 0 { return gp_fail(ps, ps[GP_POS]) } 794 gp_take(ps) 795 let nt: i64 = gp_node(ps, GN_UN, C_BANG, cnd, 0, 0) 796 gp_emit_s(ps, "if" as *u8); gp_emit_c(ps, C_LPAR); gp_print(ps, nt); gp_emit_c(ps, C_RPAR) 797 gp_emit_c(ps, C_LBRACE); gp_emit_s(ps, "break" as *u8); gp_emit_c(ps, C_SEMI); gp_emit_c(ps, C_RBRACE) 798 } 799 var step: i64 = 0 800 if gp_is1(ps, ps[GP_POS], C_RPAR) == 1 { gp_take(ps) } else { 801 step = gp_parse(ps, GP_M_ASSIGN, 0) 802 if step == 0 { return 0 } 803 if gp_is1(ps, ps[GP_POS], C_RPAR) == 0 { return gp_fail(ps, ps[GP_POS]) } 804 gp_take(ps) 805 } 806 if gp_is1(ps, ps[GP_POS], C_LBRACE) == 1 { 807 gp_take(ps) 808 var gb: i64 = 1 809 while gb == 1 { 810 if ps[GP_POS] >= ps[GP_NTOK] { return gp_fail(ps, ps[GP_POS]) } 811 if gp_is1(ps, ps[GP_POS], C_RBRACE) == 1 { gb = 0 } else { if gp_stmt(ps, 0) == 0 { return 0 } } 812 } 813 gp_take(ps) 814 } else { if gp_stmt(ps, 0) == 0 { return 0 } } 815 if step != 0 { gp_print(ps, step); gp_emit_c(ps, C_SEMI) } 816 gp_emit_c(ps, C_RBRACE) 817 } } 818 if handled == 0 { if gp_isword(ps, t, "return" as *u8) == 1 { 819 handled = 1 820 gp_take(ps); gp_emit_s(ps, "return" as *u8) 821 if gp_is1(ps, ps[GP_POS], C_SEMI) == 0 { 822 let re: i64 = gp_parse(ps, GP_M_ASSIGN, 0) 823 if re == 0 { return 0 } 824 gp_print(ps, re) 825 } 826 if gp_expect(ps, C_SEMI) == 0 { return 0 } 827 } } 828 if handled == 0 { 829 var bare: i64 = 0 830 if gp_isword(ps, t, "discard" as *u8) == 1 { bare = 1 } 831 if gp_isword(ps, t, "break" as *u8) == 1 { bare = 1 } 832 if gp_isword(ps, t, "continue" as *u8) == 1 { bare = 1 } 833 if bare == 1 { 834 handled = 1 835 gp_emit_tok(ps, t); gp_take(ps) 836 if gp_expect(ps, C_SEMI) == 0 { return 0 } 837 } 838 } 839 if handled == 0 { if gp_kind(ps, t) == GT_K_IDENT { if gp_kind(ps, t + 1) == GT_K_IDENT { 840 // a declaration: [qualifiers] T name[N] [= expr] (, name[N] [= expr])* ; -- one declarator per emitted statement 841 handled = 1 842 var ty: i64 = t 843 var gq: i64 = 1 844 while gq == 1 { 845 var isq: i64 = 0 846 if gp_isword(ps, ty, "const" as *u8) == 1 { isq = 1 } 847 if gp_isword(ps, ty, "highp" as *u8) == 1 { isq = 1 } 848 if gp_isword(ps, ty, "mediump" as *u8) == 1 { isq = 1 } 849 if gp_isword(ps, ty, "lowp" as *u8) == 1 { isq = 1 } 850 if isq == 1 { gp_emit_tok(ps, ty); ty = ty + 1 } else { gq = 0 } 851 } 852 if gp_kind(ps, ty) != GT_K_IDENT { return gp_fail(ps, ty) } 853 gp_emit_tok(ps, ty) 854 ps[GP_POS] = ty + 1 855 var gd: i64 = 1 856 while gd == 1 { 857 let nm: i64 = ps[GP_POS] 858 if gp_kind(ps, nm) != GT_K_IDENT { return gp_fail(ps, nm) } 859 gp_emit_tok(ps, nm); gp_take(ps) 860 if gp_is1(ps, ps[GP_POS], C_LBR) == 1 { 861 gp_take(ps); gp_emit_c(ps, C_LBR) 862 let an: i64 = gp_parse(ps, GP_M_ASSIGN, 0) 863 if an == 0 { return 0 } 864 gp_print(ps, an) 865 if gp_expect(ps, C_RBR) == 0 { return 0 } 866 } 867 if gp_is1(ps, ps[GP_POS], C_EQ) == 1 { 868 gp_take(ps); gp_emit_c(ps, C_EQ) 869 let iv: i64 = gp_parse(ps, GP_M_ASSIGN, 0) 870 if iv == 0 { return 0 } 871 gp_print(ps, iv) 872 } 873 if gp_is1(ps, ps[GP_POS], C_COMMA) == 1 { gp_take(ps); gp_emit_c(ps, C_SEMI); gp_emit_tok(ps, ty) } else { 874 if gp_expect(ps, C_SEMI) == 0 { return 0 } 875 gd = 0 876 } 877 } 878 } } } 879 if handled == 0 { 880 let ex: i64 = gp_parse(ps, GP_M_ASSIGN, 0) 881 if ex == 0 { return 0 } 882 gp_print(ps, ex) 883 if gp_expect(ps, C_SEMI) == 0 { return 0 } 884 } 885 if wrap == 1 { gp_emit_c(ps, C_RBRACE) } 886 return 1 887} 888// module scope: tokens copied (numbers canonicalised); a block is parsed as statements; the right side of `=` canonicalised 889func gp_toplevel(ps: *i64) -> i64 { 890 while ps[GP_POS] < ps[GP_NTOK] { 891 let t: i64 = ps[GP_POS] 892 if gp_is1(ps, t, C_LBRACE) == 1 { if gp_stmt(ps, 0) == 0 { return 0 } } else { 893 if gp_is1(ps, t, C_EQ) == 1 { 894 gp_take(ps); gp_emit_c(ps, C_EQ) 895 let e: i64 = gp_parse(ps, GP_M_TERN, 0) 896 if e == 0 { return 0 } 897 gp_print(ps, e) 898 } else { gp_emit_tok(ps, t); gp_take(ps) } 899 } 900 if ps[GP_ERR] >= 0 { return 0 } 901 } 902 return 1 903} 904// canonical text of src[0,n) into out (cap bytes). Returns the canonical length, or -1 refused: box[0] = byte offset of the 905// refusing token (n when the input ended early), box[1] = its length, box[2] = raw tokens, box[3] = expanded tokens, 906// box[4] = compounds refused by the expander, box[5] = 1 when the output bound was hit (never a silent truncation). 907func gt_canon_text(src: *u8, n: i64, out: *u8, cap: i64, box: *i64) -> i64 { 908 let toks: *i64 = sys_mmap(GT_TW * 8 * (n + 1)) as *i64 909 let k: i64 = gt_tokenize(src, n, toks) 910 let xt: *i64 = sys_mmap(GT_TW * 8 * (3 * k + 3)) as *i64 911 let bx: *i64 = sys_mmap(16) as *i64 912 let e: i64 = gt_expand(src, toks, k, xt, bx) 913 box[0] = 0 - 1; box[1] = 0; box[2] = k; box[3] = e; box[4] = bx[0]; box[5] = 0 914 let ps: *i64 = sys_mmap(GP_SLOTS * 8) as *i64 915 // the parser reads the RAW tokens: compound assignment is parsed structurally (gp_parse), never token-expanded, so the 916 // right-hand side keeps its own tree; the expander runs only to report expanded_a/expanded_b beside the raw count 917 ps[GP_POS] = 0; ps[GP_NTOK] = k; ps[GP_OUT] = 0; ps[GP_ERR] = 0 - 1; ps[GP_NCOUNT] = 1 918 ps[GP_SRC] = src as i64; ps[GP_TOKS] = toks as i64 919 ps[GP_NODES] = sys_mmap(GN_SLOTS * 8 * (3 * k + 16)) as i64 920 ps[GP_OUTBUF] = out as i64; ps[GP_CAP] = cap; ps[GP_OVER] = 0 921 ps[GP_TMP] = sys_mmap(GT_CANON_CAP) as i64 922 let ok: i64 = gp_toplevel(ps) 923 if ps[GP_OVER] == 1 { box[5] = 1; return 0 - 1 } 924 if ok == 0 { 925 var et: i64 = ps[GP_ERR] 926 if et < 0 { et = ps[GP_POS] } 927 if et >= k { box[0] = n; box[1] = 0 } else { box[0] = toks[et * GT_TW]; box[1] = toks[et * GT_TW + 1] } 928 return 0 - 1 929 } 930 return ps[GP_OUT] 931} 932// THE CANONICAL RULER: canonicalise both sides, then run the incumbent token differ over the two canonical texts. 933func gt_diff_canon(a: *u8, na: i64, b: *u8, nb: i64, r: *i64) -> i64 { 934 let capa: i64 = GT_CANON_OUT_MULT * na + GT_CANON_OUT_HEAD 935 let capb: i64 = GT_CANON_OUT_MULT * nb + GT_CANON_OUT_HEAD 936 let ca: *u8 = sys_mmap(capa) 937 let cb: *u8 = sys_mmap(capb) 938 let ba: *i64 = sys_mmap(64) as *i64 939 let bb: *i64 = sys_mmap(64) as *i64 940 let la: i64 = gt_canon_text(a, na, ca, capa, ba) 941 let lb: i64 = gt_canon_text(b, nb, cb, capb, bb) 942 r[GT_R_TOKA] = ba[2]; r[GT_R_TOKB] = bb[2]; r[GT_R_EXPA] = ba[3]; r[GT_R_EXPB] = bb[3] 943 r[GT_R_REFUSED] = ba[4] + bb[4] 944 r[GT_R_ERRA] = ba[0]; r[GT_R_ERRLA] = ba[1]; r[GT_R_ERRB] = bb[0]; r[GT_R_ERRLB] = bb[1] 945 r[GT_R_OVER] = ba[5] + bb[5] 946 r[GT_R_SRCA] = a as i64; r[GT_R_SRCB] = b as i64 947 r[GT_R_CAPTR_A] = ca as i64; r[GT_R_CAPTR_B] = cb as i64 948 r[GT_R_CALEN_A] = 0; r[GT_R_CALEN_B] = 0 949 if la >= 0 { r[GT_R_CALEN_A] = la } 950 if lb >= 0 { r[GT_R_CALEN_B] = lb } 951 r[GT_R_CANA] = 0; r[GT_R_CANB] = 0; r[GT_R_FIRSTDIFF] = 0 - 1 952 if la < 0 { r[GT_R_VERDICT] = GT_V_UNREADABLE; return GT_V_UNREADABLE } 953 if lb < 0 { r[GT_R_VERDICT] = GT_V_UNREADABLE; return GT_V_UNREADABLE } 954 let r2: *i64 = sys_mmap(GT_R_SLOTS * 8) as *i64 955 let v: i64 = gt_diff(ca, la, cb, lb, r2) 956 r[GT_R_CANA] = r2[GT_R_EXPA]; r[GT_R_CANB] = r2[GT_R_EXPB] 957 r[GT_R_FIRSTDIFF] = r2[GT_R_FIRSTDIFF] 958 r[GT_R_VERDICT] = v 959 return v 960} 961// THE PER-SLICE ACCEPT SIGNAL. A transcribed slice is emitted as a WHOLE function, so its canonical stream ends with the closing 962// brace of main while the hand text continues: the shorter side is a prefix of the longer UP TO that brace. slice_prefix=1 when 963// IDENTICAL, when the shorter is a pure prefix (first_diff at its length), or when the first difference is the shorter's LAST 964// token and that token is `}`; 0 for every real divergence and for an unreadable side. Derived from the canonical texts the 965// ruler already holds, so the report and the gate read the same predicate. 966func gt_slice_prefix(r: *i64) -> i64 { 967 if r[GT_R_VERDICT] == GT_V_UNREADABLE { return 0 } 968 if r[GT_R_VERDICT] == GT_V_IDENTICAL { return 1 } 969 if r[GT_R_CANA] == r[GT_R_CANB] { return 0 } 970 var mn: i64 = r[GT_R_CANA] 971 var sp: i64 = r[GT_R_CAPTR_A] 972 var sl: i64 = r[GT_R_CALEN_A] 973 if r[GT_R_CANB] < mn { mn = r[GT_R_CANB]; sp = r[GT_R_CAPTR_B]; sl = r[GT_R_CALEN_B] } 974 if r[GT_R_FIRSTDIFF] == mn { return 1 } 975 if r[GT_R_FIRSTDIFF] != mn - 1 { return 0 } 976 if sl < 2 { return 0 } 977 let s: *u8 = sp as *u8 978 if (s[sl - 1] as i64) != C_SP { return 0 } 979 if (s[sl - 2] as i64) != C_RBRACE { return 0 } 980 return 1 981} 982func gt_report_canon(fd: i64, r: *i64) -> i64 { 983 gt_fputs(fd, "tokens_a=" as *u8); gt_fputn(fd, r[GT_R_TOKA]) 984 gt_fputs(fd, " tokens_b=" as *u8); gt_fputn(fd, r[GT_R_TOKB]) 985 gt_fputs(fd, " expanded_a=" as *u8); gt_fputn(fd, r[GT_R_EXPA]) 986 gt_fputs(fd, " expanded_b=" as *u8); gt_fputn(fd, r[GT_R_EXPB]) 987 gt_fputs(fd, " expand_refused=" as *u8); gt_fputn(fd, r[GT_R_REFUSED]) 988 gt_fputs(fd, " canon_a=" as *u8); gt_fputn(fd, r[GT_R_CANA]) 989 gt_fputs(fd, " canon_b=" as *u8); gt_fputn(fd, r[GT_R_CANB]) 990 gt_fputs(fd, " canon_refused_a=" as *u8); gt_fputn(fd, r[GT_R_ERRA]) 991 gt_fputs(fd, " canon_refused_b=" as *u8); gt_fputn(fd, r[GT_R_ERRB]) 992 gt_fputs(fd, " canon_overflow=" as *u8); gt_fputn(fd, r[GT_R_OVER]) 993 gt_fputs(fd, " first_diff=" as *u8); gt_fputn(fd, r[GT_R_FIRSTDIFF]) 994 var pre: i64 = 0 995 if r[GT_R_FIRSTDIFF] >= 0 { 996 var mn: i64 = r[GT_R_CANA] 997 if r[GT_R_CANB] < mn { mn = r[GT_R_CANB] } 998 if r[GT_R_FIRSTDIFF] == mn { if r[GT_R_CANA] != r[GT_R_CANB] { pre = 1 } } 999 } 1000 gt_fputs(fd, " shorter_is_prefix=" as *u8); gt_fputn(fd, pre) 1001 gt_fputs(fd, " slice_prefix=" as *u8); gt_fputn(fd, gt_slice_prefix(r)) 1002 if r[GT_R_FIRSTDIFF] >= 0 { 1003 gt_fputs(fd, " a_tok=" as *u8); gt_put_expanded_tok(fd, r[GT_R_CAPTR_A] as *u8, r[GT_R_CALEN_A], r[GT_R_FIRSTDIFF]) 1004 gt_fputs(fd, " b_tok=" as *u8); gt_put_expanded_tok(fd, r[GT_R_CAPTR_B] as *u8, r[GT_R_CALEN_B], r[GT_R_FIRSTDIFF]) 1005 } 1006 if r[GT_R_ERRA] >= 0 { gt_fputs(fd, " canon_refused_a_tok=" as *u8); sys_write(fd, ((r[GT_R_SRCA] + r[GT_R_ERRA]) as *u8), r[GT_R_ERRLA]) } 1007 if r[GT_R_ERRB] >= 0 { gt_fputs(fd, " canon_refused_b_tok=" as *u8); sys_write(fd, ((r[GT_R_SRCB] + r[GT_R_ERRB]) as *u8), r[GT_R_ERRLB]) } 1008 gt_fputs(fd, "\n" as *u8) 1009 if r[GT_R_VERDICT] == GT_V_IDENTICAL { gt_fputs(fd, "verdict=IDENTICAL\n" as *u8) } else { 1010 if r[GT_R_VERDICT] == GT_V_UNREADABLE { gt_fputs(fd, "verdict=UNREADABLE\n" as *u8) } else { gt_fputs(fd, "verdict=DIFFERS\n" as *u8) } 1011 } 1012 return 0 1013}