nx_glsl_tokdiff_gate.nx source
↩ module page · 185 lines · 15112 B
1// nx_glsl_tokdiff_gate.nx -- referee for THE SHADER TOKEN RULER (nx_glsl_tok_lib), driven IN-PROCESS over planted
2// strings, both directions: every normalisation the ruler claims (whitespace, comments, numeric spelling, compound
3// assignment over ident / swizzle / index lvalues -- and, since S12c-2, THE CANONICAL FORM: precedence re-parenthesisation,
4// multi-declarator splitting, brace insertion, else-if nesting, counted-loop lowering, comparison inversion under not) must
5// read IDENTICAL, and every real difference (an operator, a constant, an identifier, an extra token, a parenthesis that
6// CHANGES precedence) must read DIFFERS naming the first differing token index -- plus the tokenizer's own arithmetic on a
7// string whose token count is known by hand, an unreadable input REFUSED by name, and the real subject (the hand MVS on the
8// board) canonicalising to itself with parentheses ADDED. Every number asserted AND emitted.
9// STATEMENT fixtures are wrapped in braces: module scope is copied token for token by design (a function HEADER `float
10// hsh(float s)` is `IDENT IDENT (` and must not be read as a declaration), so a statement-level normalisation is only
11// reachable inside a block -- exactly where every statement of a real shader lives. The first run of this gate measured that
12// (T9/T10/T12/T13 read DIFFERS at module scope, 35/40): the fixtures were wrong, the ruler was not.
13// license_tier: ORIGINAL No hw writes (Rule 26).
14import "nx_glsl_tok_lib.nx"
15import "nx_gate_verdict.nx"
16
17const C_TILDE: i64 = 126
18
19func g_diff(a: *u8, b: *u8, r: *i64) -> i64 { return gt_diff(a, gt_slen(a), b, gt_slen(b), r) }
20func g_cdiff(a: *u8, b: *u8, r: *i64) -> i64 { return gt_diff_canon(a, gt_slen(a), b, gt_slen(b), r) }
21// a planted fixture that needs a bang: nx_cc source carries none inside a literal, so the fixture is written with '~' (126)
22// and every tilde becomes '!' (33) at run time -- the byte the ruler will actually see.
23func g_bang(s: *u8) -> *u8 {
24 let n: i64 = gt_slen(s)
25 let o: *u8 = sys_mmap(n + 1)
26 var i: i64 = 0
27 while i < n {
28 var c: i64 = s[i] as i64
29 if c == C_TILDE { c = C_BANG }
30 o[i] = c as u8
31 i = i + 1
32 }
33 o[n] = 0 as u8
34 return o
35}
36
37func main() -> i64 {
38 let ctr: *i64 = gv_ctr()
39 let r: *i64 = sys_mmap(GT_R_SLOTS * 8) as *i64
40 // T1 whitespace + line comment + numeric spelling + compound `/=` on an ident lvalue
41 let a1: *u8 = "vec3 a=vec3(0.);\nfloat b=.5*a.x;\nb/=2.;\n" as *u8
42 let b1: *u8 = "vec3 a = vec3( 0.0 ) ; // the same program, spelled for people\n float b = 0.5 * a.x ;\n b = b / 2.0 ;" as *u8
43 let v1: i64 = g_diff(a1, b1, r)
44 // captured HERE, because r is refilled by every later diff and the value published at the end must be the one tested
45 let t1_tokens: i64 = r[GT_R_TOKA]
46 let t1_expanded: i64 = r[GT_R_EXPA]
47 gv_check_eq("T1-whitespace-comment-numeric-and-compound-normalise-to-IDENTICAL" as *u8, v1, GT_V_IDENTICAL, ctr)
48 // hand count (corrected on the first run: the ruler read 21 and it was right -- `.5*a.x` is FIVE tokens, not six):
49 // vec3 a = vec3 ( 0. ) ; | float b = .5 * a . x ; | b /= 2. ; -> 8 + 9 + 4 = 21 raw; the one compound adds 2 -> 23.
50 gv_check_eq("T1-raw-token-count-of-the-terse-spelling-is-known-by-hand (21)" as *u8, r[GT_R_TOKA], 21, ctr)
51 gv_check_eq("T1-expanded-streams-agree-in-length (23)" as *u8, r[GT_R_EXPA], r[GT_R_EXPB], ctr)
52 gv_check_eq("T1-expanded-length-is-raw-plus-two-per-compound (23)" as *u8, r[GT_R_EXPA], 23, ctr)
53 gv_check_eq("T1-no-compound-refused" as *u8, r[GT_R_REFUSED], 0, ctr)
54 // T2 swizzle lvalue `aP2.z+=` expands to `aP2.z=aP2.z+`
55 let v2: i64 = g_diff("aP2.z+=704.*w1;" as *u8, "aP2.z=aP2.z+704.0*w1;" as *u8, r)
56 gv_check_eq("T2-swizzle-lvalue-compound-expands-IDENTICAL" as *u8, v2, GT_V_IDENTICAL, ctr)
57 // T3 indexed lvalue with an expression index `uSB[ii*6+1]+=`
58 let v3: i64 = g_diff("uSB[ii*6+1]+=w;" as *u8, "uSB[ii*6+1]=uSB[ii*6+1]+w;" as *u8, r)
59 gv_check_eq("T3-indexed-lvalue-compound-expands-IDENTICAL" as *u8, v3, GT_V_IDENTICAL, ctr)
60 // T4 block comment + trailing-f literal
61 let v4: i64 = g_diff("/* c */ x=2.0f;" as *u8, "x=2.;" as *u8, r)
62 gv_check_eq("T4-block-comment-and-2.0f-vs-2.-IDENTICAL" as *u8, v4, GT_V_IDENTICAL, ctr)
63 // T5 multi-char operators are ONE token each
64 let v5: i64 = g_diff("a>=b<<c" as *u8, "a >= b << c" as *u8, r)
65 gv_check_eq("T5-multichar-ops-IDENTICAL" as *u8, v5, GT_V_IDENTICAL, ctr)
66 gv_check_eq("T5-multichar-ops-tokenize-as-five" as *u8, r[GT_R_TOKA], 5, ctr)
67 // NEG-CONTROLS: a real difference must fire, at the right token
68 let n1: i64 = g_diff("a=b+c;" as *u8, "a=b-c;" as *u8, r)
69 gv_check_eq("neg-control-operator-change-DIFFERS" as *u8, n1, GT_V_DIFFERS, ctr)
70 gv_check_eq("neg-control-operator-change-names-token-3" as *u8, r[GT_R_FIRSTDIFF], 3, ctr)
71 let n2: i64 = g_diff("float x=1e-6;" as *u8, "float x=1e-5;" as *u8, r)
72 gv_check_eq("neg-control-exponent-constant-change-DIFFERS" as *u8, n2, GT_V_DIFFERS, ctr)
73 let n3: i64 = g_diff("vec3 wp=uIP[ii];" as *u8, "vec3 wp=uIH[ii];" as *u8, r)
74 gv_check_eq("neg-control-identifier-change-DIFFERS" as *u8, n3, GT_V_DIFFERS, ctr)
75 let n4: i64 = g_diff("a=b;" as *u8, "a=b;c=d;" as *u8, r)
76 gv_check_eq("neg-control-extra-statement-DIFFERS-at-the-end-of-the-shorter" as *u8, n4, GT_V_DIFFERS, ctr)
77 gv_check_eq("neg-control-extra-statement-first-diff-is-the-shorter-length (4)" as *u8, r[GT_R_FIRSTDIFF], 4, ctr)
78 let n5: i64 = g_diff("x=.5;" as *u8, "x=.55;" as *u8, r)
79 gv_check_eq("neg-control-numeric-canon-does-not-collapse-different-values" as *u8, n5, GT_V_DIFFERS, ctr)
80 // a compound whose lvalue shape is not in the grammar is COUNTED, never guessed: `f(x)+=1` (a call is not an lvalue)
81 let v6: i64 = g_diff("f(x)+=1;" as *u8, "f(x)+=1;" as *u8, r)
82 gv_check_eq("T6-non-lvalue-compound-left-literal-still-IDENTICAL-to-itself" as *u8, v6, GT_V_IDENTICAL, ctr)
83 gv_check_eq("T6-non-lvalue-compound-is-COUNTED-as-refused (2 = both sides)" as *u8, r[GT_R_REFUSED], 2, ctr)
84
85 // ---- S12c-2: THE CANONICAL FORM -- each normalisation bitten in both directions ----
86 // T7 a redundant parenthesis vanishes: a+b*c and (a+(b*c)) are ONE tree
87 let v7: i64 = g_cdiff("x=a+b*c;" as *u8, "x=(a+(b*c));" as *u8, r)
88 gv_check_eq("T7-redundant-parentheses-canonicalise-IDENTICAL" as *u8, v7, GT_V_IDENTICAL, ctr)
89 gv_check_eq("T7-canonical-streams-agree-in-length" as *u8, r[GT_R_CANA], r[GT_R_CANB], ctr)
90 // NEG-CONTROL a parenthesis that CHANGES precedence survives: (a+b)*c is not a+b*c
91 let n7: i64 = g_cdiff("x=(a+b)*c;" as *u8, "x=a+b*c;" as *u8, r)
92 gv_check_eq("neg-control-meaningful-parenthesis-DIFFERS" as *u8, n7, GT_V_DIFFERS, ctr)
93 // T8 the precedence table: comparison binds tighter than logical and
94 let v8: i64 = g_cdiff("x=a<b&&c>d;" as *u8, "x=((a<b)&&(c>d));" as *u8, r)
95 gv_check_eq("T8-precedence-comparison-over-logical-and-IDENTICAL" as *u8, v8, GT_V_IDENTICAL, ctr)
96 // T9 one declarator per statement (inside a block, where declarations live)
97 let v9: i64 = g_cdiff("{float a=1.,b=a*2.;}" as *u8, "{float a=1.0;float b=(a*2.0);}" as *u8, r)
98 gv_check_eq("T9-multi-declarator-splits-IDENTICAL" as *u8, v9, GT_V_IDENTICAL, ctr)
99 // NEG-CONTROL the same two declarators at MODULE scope are NOT identified: module scope is copied, never restructured
100 let n9m: i64 = g_cdiff("float a=1.,b=a*2.;" as *u8, "float a=1.0;float b=(a*2.0);" as *u8, r)
101 gv_check_eq("neg-control-module-scope-is-copied-not-restructured-DIFFERS" as *u8, n9m, GT_V_DIFFERS, ctr)
102 // T10 brace-less branches are braced and `else if` nests
103 let v10: i64 = g_cdiff("{if(a)x=1.;else if(b)x=2.;else x=3.;}" as *u8, "{if(a){x=1.0;}else{if(b){x=2.0;}else{x=3.0;}}}" as *u8, r)
104 gv_check_eq("T10-braceless-if-else-if-chain-IDENTICAL" as *u8, v10, GT_V_IDENTICAL, ctr)
105 // T11 the ternary and unary minus print in the backend's own shape
106 let v11: i64 = g_cdiff("{float s=d<0.?-1.:1.;}" as *u8, "{float s=((d<0.0)?(-1.0):1.0);}" as *u8, r)
107 gv_check_eq("T11-ternary-and-unary-minus-IDENTICAL" as *u8, v11, GT_V_IDENTICAL, ctr)
108 // T12 a counted loop lowers to the IR's init;while(true){if(until){break;}body;step;} with i++ spelled i=(i+1)
109 let v12: i64 = g_cdiff("{for(int i=0;i<n;i++){s+=f(i);}}" as *u8, "{int i=0;while(true){if((i>=n)){break;}s=(s+f(i));i=(i+1);}}" as *u8, r)
110 gv_check_eq("T12-counted-loop-lowers-to-while-true-IDENTICAL" as *u8, v12, GT_V_IDENTICAL, ctr)
111 // T13 not-of-a-comparison prints as the inverted comparison; not-of-anything-else is preserved
112 let v13: i64 = g_cdiff(g_bang("{if(~(a<b))x=1.;}" as *u8), "{if((a>=b)){x=1.0;}}" as *u8, r)
113 gv_check_eq("T13-not-of-comparison-inverts-IDENTICAL" as *u8, v13, GT_V_IDENTICAL, ctr)
114 let v13b: i64 = g_cdiff(g_bang("{if(~q)x=1.;}" as *u8), g_bang("{if((~q)){x=1.0;}}" as *u8), r)
115 gv_check_eq("T13-not-of-identifier-is-preserved-IDENTICAL" as *u8, v13b, GT_V_IDENTICAL, ctr)
116 // NEG-CONTROL not-of-a-comparison is NOT identified with the un-inverted comparison
117 let n13: i64 = g_cdiff(g_bang("{if(~(a<b))x=1.;}" as *u8), "{if((a<b)){x=1.0;}}" as *u8, r)
118 gv_check_eq("neg-control-inverted-comparison-DIFFERS-from-the-original" as *u8, n13, GT_V_DIFFERS, ctr)
119 // T14 the canonical form is a fixed point: canon(canon(x)) == canon(x), on real statements of the hand shader
120 let a14: *u8 = "{vec3 sp=aP2+2.*cross(br.xyz,cross(br.xyz,aP2)+br.w*aP2);if(uGar==1){aP2+=texelFetch(uGD,ivec2(gl_VertexID,ii),0).xyz;}}" as *u8
121 let v14: i64 = g_cdiff(a14, a14, r)
122 gv_check_eq("T14-real-statements-self-canonical-IDENTICAL" as *u8, v14, GT_V_IDENTICAL, ctr)
123 let c14: *u8 = r[GT_R_CAPTR_A] as *u8
124 let l14: i64 = r[GT_R_CALEN_A]
125 let v14b: i64 = gt_diff_canon(c14, l14, a14, gt_slen(a14), r)
126 gv_check_eq("T14-canonical-form-is-a-fixed-point" as *u8, v14b, GT_V_IDENTICAL, ctr)
127 // T15 swizzle on a group and an expression index
128 let v15: i64 = g_cdiff("x=(a+b).x*u[i*6+1];" as *u8, "x=((a+b).x*u[((i*6)+1)]);" as *u8, r)
129 gv_check_eq("T15-swizzle-on-group-and-expression-index-IDENTICAL" as *u8, v15, GT_V_IDENTICAL, ctr)
130 // NEG-CONTROLS an unreadable input is REFUSED and named; a readable one carries no refusal; a moved parenthesis DIFFERS
131 let n8: i64 = g_cdiff("x=+;" as *u8, "x=+;" as *u8, r)
132 gv_check_eq("neg-control-unparseable-input-reads-UNREADABLE" as *u8, n8, GT_V_UNREADABLE, ctr)
133 gv_check_eq("neg-control-unparseable-input-names-the-refusing-byte (2)" as *u8, r[GT_R_ERRA], 2, ctr)
134 let n9: i64 = g_cdiff("x=a;" as *u8, "x=a;" as *u8, r)
135 gv_check_eq("neg-control-readable-input-carries-no-refusal (-1)" as *u8, r[GT_R_ERRA], 0 - 1, ctr)
136 gv_check_eq("neg-control-readable-input-IDENTICAL" as *u8, n9, GT_V_IDENTICAL, ctr)
137 let n10: i64 = g_cdiff("x=a-(b-c);" as *u8, "x=a-b-c;" as *u8, r)
138 gv_check_eq("neg-control-moved-parenthesis-DIFFERS" as *u8, n10, GT_V_DIFFERS, ctr)
139 // T17 the per-slice accept signal: a shorter canonical stream that is a PREFIX of the longer names first_diff at its own
140 // length; a shorter stream that DIVERGES names the diverging token, and the prefix flag stays 0 (computed as the report does)
141 // (measured on the first run: a braced slice is NOT a pure prefix -- its closing brace lands where the hand continues, so
142 // first_diff is the shorter length MINUS ONE and the shorter's token there is `}`; that is exactly what gt_slice_prefix reads)
143 let v17: i64 = g_cdiff("{a=b;c=d;}" as *u8, "{a=b;}" as *u8, r)
144 gv_check_eq("T17-prefix-slice-reads-DIFFERS" as *u8, v17, GT_V_DIFFERS, ctr)
145 gv_check_eq("T17-prefix-slice-first-diff-is-the-shorter-canonical-length-minus-its-closing-brace" as *u8, r[GT_R_FIRSTDIFF], r[GT_R_CANB] - 1, ctr)
146 gv_check_eq("T17-prefix-slice-slice_prefix-reads-1" as *u8, gt_slice_prefix(r), 1, ctr)
147 let v17b: i64 = g_cdiff("{a=b;c=d;}" as *u8, "{a=x;}" as *u8, r)
148 gv_check("T17-diverging-slice-first-diff-is-inside-the-shorter" as *u8, (r[GT_R_FIRSTDIFF] < r[GT_R_CANB]) as i64, ctr)
149 gv_check_eq("T17-diverging-slice-reads-DIFFERS" as *u8, v17b, GT_V_DIFFERS, ctr)
150 gv_check_eq("neg-control-diverging-slice-slice_prefix-reads-0" as *u8, gt_slice_prefix(r), 0, ctr)
151 let v17c: i64 = g_cdiff("a=b;c=d;" as *u8, "a=b;" as *u8, r)
152 gv_check_eq("T17-pure-prefix-first-diff-is-the-shorter-canonical-length" as *u8, r[GT_R_FIRSTDIFF], r[GT_R_CANB], ctr)
153 gv_check_eq("T17-pure-prefix-slice_prefix-reads-1" as *u8, gt_slice_prefix(r), 1, ctr)
154 gv_check_eq("T17-pure-prefix-reads-DIFFERS" as *u8, v17c, GT_V_DIFFERS, ctr)
155 // T18 a compound assignment is parsed STRUCTURALLY in the canonical form: `x*=a+b` is x=(x*(a+b)), and it is NOT the
156 // token-level expansion x=x*a+b (a different tree; measured on the hand MVS line `aP2.x*=1.+.08*w2` before this tooth existed)
157 let v18: i64 = g_cdiff("{x*=a+b;}" as *u8, "{x=(x*(a+b));}" as *u8, r)
158 gv_check_eq("T18-compound-assign-parses-structurally-IDENTICAL" as *u8, v18, GT_V_IDENTICAL, ctr)
159 let n18: i64 = g_cdiff("{x*=a+b;}" as *u8, "{x=x*a+b;}" as *u8, r)
160 gv_check_eq("neg-control-compound-assign-is-NOT-the-token-expansion-DIFFERS" as *u8, n18, GT_V_DIFFERS, ctr)
161 let v18b: i64 = g_cdiff("{aP2.z+=704.*w1;}" as *u8, "{aP2.z=(aP2.z+(704.0*w1));}" as *u8, r)
162 gv_check_eq("T18-compound-on-swizzle-lvalue-IDENTICAL" as *u8, v18b, GT_V_IDENTICAL, ctr)
163 let v18c: i64 = g_cdiff("{br/=nr;bd/=nr;}" as *u8, "{br=(br/nr);bd=(bd/nr);}" as *u8, r)
164 gv_check_eq("T18-two-compounds-in-sequence-IDENTICAL" as *u8, v18c, GT_V_IDENTICAL, ctr)
165 // T16 THE REAL SUBJECT: the hand MVS on the board canonicalises to itself, and canonicalising ADDED tokens (parentheses)
166 let lbm: *i64 = sys_mmap(16) as *i64
167 var mvs: *u8 = sys_read_file("buildroot/knowledge/compare/cast_mvs_hand.glsl" as *u8, lbm)
168 var mvs_root: i64 = 1
169 if (mvs as i64) == 0 { mvs = sys_read_file("knowledge/compare/cast_mvs_hand.glsl" as *u8, lbm); mvs_root = 2 }
170 var vm: i64 = 0 - 1
171 var mcan: i64 = 0
172 var mexp: i64 = 1
173 var mraw: i64 = 0
174 if (mvs as i64) != 0 { vm = gt_diff_canon(mvs, lbm[0], mvs, lbm[0], r); mcan = r[GT_R_CANA]; mexp = r[GT_R_EXPA]; mraw = r[GT_R_TOKA] } else { mvs_root = 0 }
175 gv_check("T16-hand-MVS-board-file-readable-from-the-nishihost-or-buildroot-root" as *u8, ((mvs as i64) != 0) as i64, ctr)
176 gv_check_eq("T16-hand-MVS-canonical-self-diff-IDENTICAL" as *u8, vm, GT_V_IDENTICAL, ctr)
177 gv_check("T16-hand-MVS-canonical-tokens-exceed-expanded (parentheses were added, never dropped)" as *u8, (mcan > mexp) as i64, ctr)
178 gv_kv("T1_tokens_a" as *u8, t1_tokens)
179 gv_kv("T1_expanded_a" as *u8, t1_expanded)
180 gv_kv("mvs_root" as *u8, mvs_root)
181 gv_kv("mvs_raw_tokens" as *u8, mraw)
182 gv_kv("mvs_expanded_tokens" as *u8, mexp)
183 gv_kv("mvs_canonical_tokens" as *u8, mcan)
184 return gv_verdict("nx_glsl_tokdiff_gate" as *u8, ctr, "the ruler the S12c accept rule is measured with, bitten in both directions" as *u8)
185}