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}