code wiki / _hdl_build / nx_print_css_gate.nx
nx_print_css_gate.nx source
↩ module page · 194 lines · 12799 B
1// nx_print_css_gate.nx -- referee for sc_print_css (datavis DV7, 2026-09-15): the ONE print stylesheet every compare
2// generator emits last. Composes the emitter in-process into a scratch file and reads the bytes back: one balanced
3// print media rule, the light palette re-selected under the OS-preference selector's own specificity with its grounds
4// made white, figures and cards unbroken, tables paginating with a repeating header and unsplit rows, the filter bar
5// and the chips hidden, no bang byte and no hash byte (order is the specificity; the lexer forbids both), a
6// deterministic emit, a planted sheet without the rules REFUSED by the same reader, and the live search page witnessed
7// whenever it is reachable from the CWD. license_tier: ORIGINAL
8import "nx_swcompare_lib.nx"
9import "nx_syscalls.nx"
10import "nx_gate_verdict.nx"
11
12const G_DIR: *u8 = "/tmp/nx_print_css_gate/"
13const G_OUT: *u8 = "/tmp/nx_print_css_gate/print.css"
14const G_OUT2: *u8 = "/tmp/nx_print_css_gate/print2.css"
15const G_PLANT: *u8 = "/tmp/nx_print_css_gate/plant.css"
16const G_DIR_MODE: i64 = 493
17const G_FILE_MODE: i64 = 420
18const G_CH_BANG: i64 = 33
19const G_CH_HASH: i64 = 35
20const G_CH_LBRACE: i64 = 123
21const G_CH_RBRACE: i64 = 125
22const G_CH_NL: i64 = 10
23const G_CH_D0: i64 = 48
24const G_CH_D9: i64 = 57
25const G_LIVE_A: *u8 = "sites/nishifamily/compare/search/index.html"
26const G_LIVE_B: *u8 = "../sites/nishifamily/compare/search/index.html"
27
28func g_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
29// first offset of needle in buf[0,n), or -1
30func g_find(buf: *u8, n: i64, needle: *u8) -> i64 {
31 let m: i64 = g_len(needle)
32 if m == 0 { return -1 }
33 var i: i64 = 0
34 while i + m <= n {
35 var k: i64 = 0
36 while k < m { if buf[i + k] != needle[k] { break } k = k + 1 }
37 if k == m { return i }
38 i = i + 1
39 }
40 return -1
41}
42func g_count(buf: *u8, n: i64, needle: *u8) -> i64 {
43 let m: i64 = g_len(needle)
44 if m == 0 { return 0 }
45 var c: i64 = 0
46 var i: i64 = 0
47 while i + m <= n {
48 var k: i64 = 0
49 while k < m { if buf[i + k] != needle[k] { break } k = k + 1 }
50 if k == m { c = c + 1; i = i + m } else { i = i + 1 }
51 }
52 return c
53}
54func g_count_byte(buf: *u8, n: i64, b: i64) -> i64 { var c: i64 = 0; var i: i64 = 0; while i < n { if buf[i] == (b as u8) { c = c + 1 } i = i + 1 } return c }
55// THE READER THE TEETH SHARE. Locate the rule whose selector list starts with sel at a rule boundary (a line start
56// or straight after a closing brace -- a selector that is a suffix of another cannot answer for it), then return
57// rule start, body start and body end through out[0..2]. 1 found, 0 not found.
58func g_rule(buf: *u8, n: i64, sel: *u8, out: *i64) -> i64 {
59 let m: i64 = g_len(sel)
60 if m == 0 { return 0 }
61 var i: i64 = 0
62 while i + m <= n {
63 var at: i64 = 0
64 if i == 0 { at = 1 } else { if buf[i - 1] == (G_CH_NL as u8) { at = 1 } else { if buf[i - 1] == (G_CH_RBRACE as u8) { at = 1 } } }
65 if at == 1 {
66 var k: i64 = 0
67 while k < m { if buf[i + k] != sel[k] { break } k = k + 1 }
68 if k == m {
69 var b0: i64 = i + m - 1
70 while b0 < n { if buf[b0] == (G_CH_LBRACE as u8) { break } b0 = b0 + 1 }
71 var b1: i64 = b0
72 while b1 < n { if buf[b1] == (G_CH_RBRACE as u8) { break } b1 = b1 + 1 }
73 if b0 >= n { return 0 }
74 out[0] = i; out[1] = b0 + 1; out[2] = b1
75 return 1
76 }
77 }
78 i = i + 1
79 }
80 return 0
81}
82// does the rule starting with sel carry decl in its BODY? 1 yes, 0 no rule or no declaration.
83func g_rule_has(buf: *u8, n: i64, sel: *u8, decl: *u8) -> i64 {
84 let o: *i64 = sys_mmap(24) as *i64
85 if g_rule(buf, n, sel, o) == 0 { return 0 }
86 if o[2] <= o[1] { return 0 }
87 if g_find(buf + o[1], o[2] - o[1], decl) >= 0 { return 1 }
88 return 0
89}
90// does the rule starting with sel name `name` in its SELECTOR LIST? 1 yes, 0 no.
91func g_sel_has(buf: *u8, n: i64, sel: *u8, name: *u8) -> i64 {
92 let o: *i64 = sys_mmap(24) as *i64
93 if g_rule(buf, n, sel, o) == 0 { return 0 }
94 if g_find(buf + o[0], o[1] - o[0], name) >= 0 { return 1 }
95 return 0
96}
97func g_write(path: *u8, s: *u8) -> i64 { sys_unlinkat(path); let fd: i64 = sys_openat_wr(path, G_FILE_MODE); if fd < 0 { return -1 } sys_write(fd, s, g_len(s)); sys_close(fd); return 0 }
98func g_emit(path: *u8) -> i64 { sys_unlinkat(path); let fd: i64 = sys_openat_wr(path, G_FILE_MODE); if fd < 0 { return -1 } sc_print_css(fd); sys_close(fd); return 0 }
99func g_same(a: *u8, an: i64, b: *u8, bn: i64) -> i64 { if an != bn { return 0 } var i: i64 = 0; while i < an { if a[i] != b[i] { return 0 } i = i + 1 } return 1 }
100// the decimal that follows key, or -1 when the key is absent or no digit follows it
101func g_num_after(buf: *u8, n: i64, key: *u8) -> i64 {
102 let at: i64 = g_find(buf, n, key)
103 if at < 0 { return -1 }
104 var i: i64 = at + g_len(key)
105 var v: i64 = 0
106 var d: i64 = 0
107 while i < n { let c: i64 = buf[i] as i64; if c < G_CH_D0 { break } if c > G_CH_D9 { break } v = v * 10 + (c - G_CH_D0); d = d + 1; i = i + 1 }
108 if d == 0 { return -1 }
109 return v
110}
111func g_exists(path: *u8) -> i64 { let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 } sys_close(fd); return 1 }
112
113func main() -> i64 {
114 let ctr: *i64 = gv_ctr()
115 sys_mkdir(G_DIR, G_DIR_MODE)
116 gv_check_eq("T1-emit-into-scratch" as *u8, g_emit(G_OUT), 0, ctr)
117 let nb: *i64 = sys_mmap(8) as *i64
118 let css: *u8 = sys_read_file(G_OUT, nb)
119 let n: i64 = nb[0]
120 gv_check("T1a-the-sheet-has-bytes" as *u8, (n > 0) as i64, ctr)
121 // T2: one balanced print media rule, opened on byte 0 and closed on the last line
122 gv_check_eq("T2-opens-as-one-print-media-rule" as *u8, g_find(css, n, "@media print{\n" as *u8), 0, ctr)
123 gv_check_eq("T2a-braces-balance" as *u8, g_count_byte(css, n, G_CH_LBRACE), g_count_byte(css, n, G_CH_RBRACE), ctr)
124 gv_check("T2b-closes-on-its-last-line" as *u8, ((n >= 2) as i64) * ((css[n - 2] == (G_CH_RBRACE as u8)) as i64) * ((css[n - 1] == (G_CH_NL as u8)) as i64), ctr)
125 // T3: the light register is re-selected in FULL (count derived from the SSOT, never hand-counted) under the
126 // OS-preference selector's own specificity, and the grounds go white
127 let lb: *u8 = sys_mmap(SC_TOKCAP)
128 let bd: *u8 = sc_brand()
129 let lw: i64 = bt_emit_root_buf(bd, bt_len(bd), lb, SC_TOKCAP)
130 let light_rows: i64 = g_count(lb, lw, " --nx-" as *u8)
131 let print_rows: i64 = g_count(css, n, " --nx-" as *u8)
132 gv_check("T3-the-ssot-has-light-rows" as *u8, (light_rows > 0) as i64, ctr)
133 gv_check_eq("T3a-every-light-row-re-selected-on-paper" as *u8, print_rows, light_rows, ctr)
134 gv_check("T3b-under-the-os-preference-selectors-own-specificity" as *u8, (g_find(css, n, "html,html[data-theme],:root:not([data-theme]){\n --nx-" as *u8) >= 0) as i64, ctr)
135 gv_check("T3c-the-grounds-go-white" as *u8, ((g_find(css, n, "--nx-color-bg:rgb(255,255,255)" as *u8) >= 0) as i64) * ((g_find(css, n, "--nx-color-panel:rgb(255,255,255)" as *u8) >= 0) as i64) * ((g_find(css, n, "--nx-color-codebg:rgb(255,255,255)" as *u8) >= 0) as i64), ctr)
136 gv_check_eq("T3d-paper-declares-the-light-colour-scheme" as *u8, g_rule_has(css, n, "html{" as *u8, "color-scheme:light" as *u8), 1, ctr)
137 // T4: figures and cards unbroken
138 gv_check_eq("T4-figures-unbroken" as *u8, g_rule_has(css, n, "figure," as *u8, "break-inside:avoid" as *u8), 1, ctr)
139 gv_check_eq("T4a-the-position-map-figure-is-in-that-rule" as *u8, g_sel_has(css, n, "figure," as *u8, ".cdfig" as *u8), 1, ctr)
140 gv_check_eq("T4b-the-sota-card-is-in-that-rule" as *u8, g_sel_has(css, n, "figure," as *u8, ".cap" as *u8), 1, ctr)
141 gv_check_eq("T4c-images-and-svg-fit-the-page" as *u8, g_rule_has(css, n, "svg,img{" as *u8, "max-width:100%" as *u8), 1, ctr)
142 // T5: tables paginated -- the header repeats, a row never splits, the table itself may break
143 gv_check_eq("T5-table-header-repeats-on-every-page" as *u8, g_rule_has(css, n, "thead{" as *u8, "display:table-header-group" as *u8), 1, ctr)
144 gv_check_eq("T5a-a-row-never-splits" as *u8, g_rule_has(css, n, "tr{" as *u8, "break-inside:avoid" as *u8), 1, ctr)
145 gv_check_eq("T5b-a-table-may-paginate" as *u8, g_rule_has(css, n, "table{" as *u8, "break-inside:auto" as *u8), 1, ctr)
146 gv_check_eq("T5c-headings-keep-their-first-lines" as *u8, g_rule_has(css, n, "h1,h2,h3," as *u8, "break-after:avoid" as *u8), 1, ctr)
147 // T6: chips and filters hidden
148 gv_check_eq("T6-the-filter-bar-is-hidden" as *u8, g_rule_has(css, n, ".fbar," as *u8, "display:none" as *u8), 1, ctr)
149 gv_check_eq("T6a-filter-chips-are-in-the-hidden-rule" as *u8, g_sel_has(css, n, ".fbar," as *u8, ".fchip" as *u8), 1, ctr)
150 gv_check_eq("T6b-theme-chips-are-in-the-hidden-rule" as *u8, g_sel_has(css, n, ".fbar," as *u8, ".tchip" as *u8), 1, ctr)
151 gv_check_eq("T6c-the-header-nav-is-in-the-hidden-rule" as *u8, g_sel_has(css, n, ".fbar," as *u8, ".hd" as *u8), 1, ctr)
152 gv_check_eq("T6d-the-map-legend-stays-readable" as *u8, g_rule_has(css, n, ".cdleg," as *u8, "opacity:1" as *u8), 1, ctr)
153 // T7: no importance hacks and no hash byte -- order is the specificity and the lexer forbids both
154 gv_check_eq("T7-no-bang-byte" as *u8, g_count_byte(css, n, G_CH_BANG), 0, ctr)
155 gv_check_eq("T7a-no-hash-byte" as *u8, g_count_byte(css, n, G_CH_HASH), 0, ctr)
156 // T8: the page margin and the body size are the named constants, read back from the bytes
157 gv_check_eq("T8-page-margin-is-the-named-constant" as *u8, g_num_after(css, n, "@page{margin:" as *u8), SC_PRINT_MARGIN_MM, ctr)
158 gv_check_eq("T8a-body-size-is-the-named-constant" as *u8, g_num_after(css, n, "font-size:" as *u8), SC_PRINT_BODY_PT, ctr)
159 // T9: links print their target once, never inside the refs list that already prints it
160 gv_check_eq("T9-links-print-their-target" as *u8, g_rule_has(css, n, "a[href^='http']::after{" as *u8, "attr(href)" as *u8), 1, ctr)
161 gv_check_eq("T9a-not-inside-the-refs-list" as *u8, g_rule_has(css, n, ".refs a::after," as *u8, "content:none" as *u8), 1, ctr)
162 // T10: deterministic
163 gv_check_eq("T10-second-emit-succeeds" as *u8, g_emit(G_OUT2), 0, ctr)
164 let nb2: *i64 = sys_mmap(8) as *i64
165 let css2: *u8 = sys_read_file(G_OUT2, nb2)
166 gv_check_eq("T10a-two-emits-are-byte-identical" as *u8, g_same(css, n, css2, nb2[0]), 1, ctr)
167 // T11: NEG -- a planted sheet that hides nothing and breaks figures is refused by the SAME reader
168 gv_check_eq("T11-plant-written" as *u8, g_write(G_PLANT, "@media print{\n.fbar,.fchip{display:flex}\nfigure,.cdfig{margin:0}\ntr{break-after:auto}\n}\n" as *u8), 0, ctr)
169 let nbp: *i64 = sys_mmap(8) as *i64
170 let plant: *u8 = sys_read_file(G_PLANT, nbp)
171 let pn: i64 = nbp[0]
172 gv_check_eq("neg-control-T11a-a-visible-filter-bar-is-refused" as *u8, g_rule_has(plant, pn, ".fbar," as *u8, "display:none" as *u8), 0, ctr)
173 gv_check_eq("neg-control-T11b-a-breakable-figure-is-refused" as *u8, g_rule_has(plant, pn, "figure," as *u8, "break-inside:avoid" as *u8), 0, ctr)
174 gv_check_eq("neg-control-T11c-a-splittable-row-is-refused" as *u8, g_rule_has(plant, pn, "tr{" as *u8, "break-inside:avoid" as *u8), 0, ctr)
175 gv_check_eq("neg-control-T11d-the-reader-still-finds-the-planted-rule" as *u8, g_rule_has(plant, pn, ".fbar," as *u8, "display:flex" as *u8), 1, ctr)
176 // T12: the live search page, witnessed when reachable from this CWD -- announced, never silently skipped
177 var live: i64 = -1
178 var lp: *u8 = G_LIVE_A
179 if g_exists(G_LIVE_A) == 0 { lp = G_LIVE_B }
180 if g_exists(lp) == 1 {
181 let nbl: *i64 = sys_mmap(8) as *i64
182 let page: *u8 = sys_read_file(lp, nbl)
183 live = (g_find(page, nbl[0], "@media print{\n" as *u8) >= 0) as i64
184 gv_check_eq("T12-the-live-search-page-carries-the-print-sheet" as *u8, live, 1, ctr)
185 } else {
186 gv_puts("LIVE-WITNESS absent: sites/nishifamily/compare/search/index.html is not reachable from this CWD -- T12 not counted\n" as *u8)
187 }
188 gv_kv("print_bytes" as *u8, n)
189 gv_kv("light_rows" as *u8, light_rows)
190 gv_kv("print_rows" as *u8, print_rows)
191 gv_kv("page_margin_mm" as *u8, g_num_after(css, n, "@page{margin:" as *u8))
192 gv_kv("live_witness" as *u8, live)
193 return gv_verdict("nx_print_css_gate" as *u8, ctr, "the one print sheet: a balanced print media rule, the light register re-selected in full and made white, figures and cards unbroken, tables paginating with a repeating header, the filter bar and the chips hidden, no bang or hash byte, a deterministic emit, a planted sheet refused by the same reader, the live page witnessed when reachable" as *u8)
194}