code wiki / _hdl_build / nx_chartlay_adoption_gate.nx

nx_chartlay_adoption_gate.nx source

↩ module page · 505 lines · 27220 B

1// nx_chartlay_adoption_gate.nx -- datavis DV9 (2026-09-15): THE FLEET RATCHET for the one chart-layout ruler. 2// Names every SVG-emitting function in the compare generators (the sources are DATA in cl_adoption.conf) and refuses 3// a chart that scales, places or colours by hand. An emitter is an ADOPTER when it, or its prefix family (the helpers 4// sharing its name up to the first underscore -- the estate's one-figure-one-prefix convention), calls a ruler verb 5// (the verbs are DATA too); HAND when none does; HAND-COLOUR when the family paints an SVG fill or stroke with a 6// literal rgb() instead of a token. A decorative icon (aria-hidden on the tag) is counted, never judged; an SVG named 7// only in a comment is nothing. Named allow rows exempt one function WITH a reason and can only shrink: an allow row 8// whose function is no longer an offender is STALE and reads RED until deleted, so the ratchet tightens by 9// construction. Planted fixtures prove every class both ways before the live census runs, and the census prints one 10// line per emitter -- a count without a worklist is not actionable. The imprecision lived with: attribute literals 11// are matched in their single-quoted spelling only (the family's emitters use no other), and nx_lineconf_lib is not 12// composed because it is a first-key reader while this file is a ROW LIST with repeated keys. license_tier: ORIGINAL 13import "nx_syscalls.nx" 14import "nx_gate_verdict.nx" 15 16const CA_CONF: *u8 = "knowledge/compare/cl_adoption.conf" 17const CA_CONF_BR: *u8 = "buildroot/knowledge/compare/cl_adoption.conf" 18const CA_BR_PFX: *u8 = "buildroot/" 19const CA_DIR: *u8 = "/tmp/nx_chartlay_adoption_gate/" 20const CA_FX_CONF: *u8 = "/tmp/nx_chartlay_adoption_gate/fixture.conf" 21const CA_FX_CONF_ALLOW: *u8 = "/tmp/nx_chartlay_adoption_gate/fixture_allow.conf" 22const CA_FX_CONF_STALE: *u8 = "/tmp/nx_chartlay_adoption_gate/fixture_stale.conf" 23const CA_FX_A: *u8 = "/tmp/nx_chartlay_adoption_gate/adopter.nx" 24const CA_FX_H: *u8 = "/tmp/nx_chartlay_adoption_gate/hand.nx" 25const CA_FX_C: *u8 = "/tmp/nx_chartlay_adoption_gate/colour.nx" 26const CA_FX_I: *u8 = "/tmp/nx_chartlay_adoption_gate/icon.nx" 27const CA_DIR_MODE: i64 = 493 28const CA_FILE_MODE: i64 = 420 29const CA_MAX_FUNCS: i64 = 4096 30const CA_MAX_ROWS: i64 = 256 31const CA_NAME_W: i64 = 96 32const CA_PATH_W: i64 = 256 33const CA_REASON_W: i64 = 320 34const CA_MAX_EMIT: i64 = 512 35const CA_TAG_SPAN: i64 = 400 36const CA_NUM_W: i64 = 24 37const CA_I64: i64 = 8 38const CA_CH_NL: i64 = 10 39const CA_CH_QUOTE: i64 = 34 40const CA_CH_SLASH: i64 = 47 41const CA_CH_BSLASH: i64 = 92 42const CA_CH_PIPE: i64 = 124 43const CA_CH_USCORE: i64 = 95 44const CA_CH_LPAREN: i64 = 40 45const CA_CH_GT: i64 = 62 46const CA_CH_D0: i64 = 48 47const CA_CH_MINUS: i64 = 45 48// per-emitter verdicts 49const CA_NONE: i64 = -1 50const CA_ADOPTER: i64 = 1 51const CA_HAND: i64 = 2 52const CA_HAND_COLOUR: i64 = 3 53// census slots (the partition emitters = adopters + hand + hand_colour is asserted, never assumed) 54const CA_S_SOURCES: i64 = 0 55const CA_S_UNREADABLE: i64 = 1 56const CA_S_FUNCS: i64 = 2 57const CA_S_EMITTERS: i64 = 3 58const CA_S_ADOPTERS: i64 = 4 59const CA_S_HAND: i64 = 5 60const CA_S_HAND_COLOUR: i64 = 6 61const CA_S_ICONS: i64 = 7 62const CA_S_ALLOWED: i64 = 8 63const CA_S_STALE_ALLOW: i64 = 9 64const CA_S_OFFENDERS: i64 = 10 65const CA_S_RULERS: i64 = 11 66const CA_S_EMIT_TRUNC: i64 = 12 67const CA_S_W: i64 = 13 68 69func ca_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 70func ca_put(fd: i64, s: *u8) -> i64 { sys_write(fd, s, ca_len(s)); return 0 } 71func ca_putn(fd: i64, v: i64) -> i64 { 72 let b: *u8 = sys_mmap(CA_NUM_W) 73 var x: i64 = v 74 var neg: i64 = 0 75 if x < 0 { neg = 1; x = 0 - x } 76 var i: i64 = CA_NUM_W 77 if x == 0 { i = i - 1; b[i] = CA_CH_D0 as u8 } 78 while x > 0 { i = i - 1; b[i] = (CA_CH_D0 + (x - (x / 10) * 10)) as u8; x = x / 10 } 79 if neg == 1 { i = i - 1; b[i] = CA_CH_MINUS as u8 } 80 sys_write(fd, b + i, CA_NUM_W - i) 81 return 0 82} 83func ca_q(fd: i64) -> i64 { let b: *u8 = sys_mmap(2); b[0] = CA_CH_QUOTE as u8; sys_write(fd, b, 1); return 0 } 84// first offset of needle in buf[0,n), or -1 85func ca_find(buf: *u8, n: i64, needle: *u8) -> i64 { 86 let m: i64 = ca_len(needle) 87 if m == 0 { return -1 } 88 var i: i64 = 0 89 while i + m <= n { 90 var k: i64 = 0 91 while k < m { if buf[i + k] != needle[k] { break } k = k + 1 } 92 if k == m { return i } 93 i = i + 1 94 } 95 return -1 96} 97func ca_starts(buf: *u8, n: i64, needle: *u8) -> i64 { 98 let m: i64 = ca_len(needle) 99 if m > n { return 0 } 100 var k: i64 = 0 101 while k < m { if buf[k] != needle[k] { return 0 } k = k + 1 } 102 return 1 103} 104func ca_eq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while a[i] == b[i] { if a[i] == (0 as u8) { return 1 } i = i + 1 } return 0 } 105// copy [s, s+n) into dst as a nul-terminated string, bounded by cap 106func ca_copy(dst: *u8, cap: i64, s: *u8, n: i64) -> i64 { 107 var m: i64 = n 108 if m > cap - 1 { m = cap - 1 } 109 var i: i64 = 0 110 while i < m { dst[i] = s[i]; i = i + 1 } 111 dst[m] = 0 as u8 112 return m 113} 114func ca_cat(dst: *u8, cap: i64, a: *u8, b: *u8) -> i64 { 115 let la: i64 = ca_len(a) 116 let lb: i64 = ca_len(b) 117 var i: i64 = 0 118 while i < la { if i < cap - 1 { dst[i] = a[i] } i = i + 1 } 119 var j: i64 = 0 120 while j < lb { if la + j < cap - 1 { dst[la + j] = b[j] } j = j + 1 } 121 var e: i64 = la + lb 122 if e > cap - 1 { e = cap - 1 } 123 dst[e] = 0 as u8 124 return e 125} 126func ca_write(path: *u8, s: *u8) -> i64 { sys_unlinkat(path); let fd: i64 = sys_openat_wr(path, CA_FILE_MODE); if fd < 0 { return -1 } sys_write(fd, s, ca_len(s)); sys_close(fd); return 0 } 127func ca_exists(path: *u8) -> i64 { let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 } sys_close(fd); return 1 } 128// STRIP COMMENTS OUTSIDE STRING LITERALS. A // inside a literal (a URL before an svg tag on the same line) is 129// content, so quote state is tracked and a backslash escape is honoured; the newline that ends a comment is kept so 130// line anchoring survives. Returns the stripped length. 131func ca_strip(src: *u8, n: i64, dst: *u8) -> i64 { 132 var i: i64 = 0 133 var o: i64 = 0 134 var in_str: i64 = 0 135 while i < n { 136 let c: i64 = src[i] as i64 137 if in_str == 1 { 138 dst[o] = src[i]; o = o + 1 139 if c == CA_CH_BSLASH { if i + 1 < n { dst[o] = src[i + 1]; o = o + 1; i = i + 1 } } 140 else { if c == CA_CH_QUOTE { in_str = 0 } } 141 i = i + 1 142 } else { 143 if c == CA_CH_QUOTE { in_str = 1; dst[o] = src[i]; o = o + 1; i = i + 1 } 144 else { 145 var skip: i64 = 0 146 if c == CA_CH_SLASH { if i + 1 < n { if src[i + 1] == (CA_CH_SLASH as u8) { skip = 1 } } } 147 if skip == 1 { while i < n { if src[i] == (CA_CH_NL as u8) { break } i = i + 1 } } 148 else { dst[o] = src[i]; o = o + 1; i = i + 1 } 149 } 150 } 151 } 152 return o 153} 154// the k-th row (0-based) whose line starts with key followed by a pipe: returns the line start, or -1 155func ca_row(buf: *u8, n: i64, key: *u8, k: i64) -> i64 { 156 let kl: i64 = ca_len(key) 157 var seen: i64 = 0 158 var i: i64 = 0 159 while i < n { 160 var le: i64 = i 161 while le < n { if buf[le] == (CA_CH_NL as u8) { break } le = le + 1 } 162 if le - i > kl { 163 if ca_starts(buf + i, le - i, key) == 1 { 164 if buf[i + kl] == (CA_CH_PIPE as u8) { 165 if seen == k { return i } 166 seen = seen + 1 167 } 168 } 169 } 170 i = le + 1 171 } 172 return -1 173} 174// the idx-th pipe field of the line starting at ls, copied nul-terminated into out; returns its length or -1 175func ca_field(buf: *u8, n: i64, ls: i64, idx: i64, out: *u8, cap: i64) -> i64 { 176 var le: i64 = ls 177 while le < n { if buf[le] == (CA_CH_NL as u8) { break } le = le + 1 } 178 var f: i64 = 0 179 var s: i64 = ls 180 var i: i64 = ls 181 while i <= le { 182 var end: i64 = 0 183 if i == le { end = 1 } else { if buf[i] == (CA_CH_PIPE as u8) { end = 1 } } 184 if end == 1 { 185 if f == idx { return ca_copy(out, cap, buf + s, i - s) } 186 f = f + 1 187 s = i + 1 188 } 189 i = i + 1 190 } 191 return -1 192} 193func ca_read_source(path: *u8, out_n: *i64, scratch: *u8) -> *u8 { 194 if ca_exists(path) == 1 { return sys_read_file(path, out_n) } 195 ca_cat(scratch, CA_PATH_W, CA_BR_PFX, path) 196 if ca_exists(scratch) == 1 { return sys_read_file(scratch, out_n) } 197 out_n[0] = -1 198 return 0 as *u8 199} 200// does [p, p+span) of an svg start tag carry aria-hidden before its closing bracket? 1 icon, 0 chart 201func ca_tag_is_icon(buf: *u8, n: i64, p: i64) -> i64 { 202 var e: i64 = p 203 var lim: i64 = p + CA_TAG_SPAN 204 if lim > n { lim = n } 205 while e < lim { if buf[e] == (CA_CH_GT as u8) { break } e = e + 1 } 206 if ca_find(buf + p, e - p, "aria-hidden" as *u8) >= 0 { return 1 } 207 return 0 208} 209// the verdict recorded for a function name in the emitter table, or CA_NONE 210func ca_verdict_of(em_names: *u8, em_verd: *i64, em_n: i64, name: *u8) -> i64 { 211 var i: i64 = 0 212 while i < em_n { if ca_eq(em_names + i * CA_NAME_W, name) == 1 { return em_verd[i] } i = i + 1 } 213 return CA_NONE 214} 215func ca_verdict_word(v: i64) -> *u8 { 216 if v == CA_ADOPTER { return "ADOPTER" as *u8 } 217 if v == CA_HAND { return "HAND" as *u8 } 218 if v == CA_HAND_COLOUR { return "HAND-COLOUR" as *u8 } 219 return "NONE" as *u8 220} 221 222// THE CENSUS. Reads the conf, walks every source row, classifies every function that writes an svg start tag, applies 223// the allow rows, fills st[] and the emitter table. Returns 0, or -1 when the conf itself cannot be read. 224func cl_adoption_gate(conf: *u8, st: *i64, em_names: *u8, em_verd: *i64) -> i64 { 225 var s: i64 = 0 226 while s < CA_S_W { st[s] = 0; s = s + 1 } 227 let cn_p: *i64 = sys_mmap(CA_I64) as *i64 228 let cbuf: *u8 = sys_read_file(conf, cn_p) 229 let cn: i64 = cn_p[0] 230 if cn <= 0 { return -1 } 231 // ruler verbs, each stored with its call paren so "cl_scale" cannot match "cl_scale_dom" by prefix alone 232 let rv: *u8 = sys_mmap(CA_MAX_ROWS * CA_NAME_W) 233 let fld: *u8 = sys_mmap(CA_REASON_W) 234 var nr: i64 = 0 235 var ls: i64 = ca_row(cbuf, cn, "ruler" as *u8, nr) 236 while ls >= 0 { 237 if nr < CA_MAX_ROWS { 238 ca_field(cbuf, cn, ls, 1, fld, CA_NAME_W) 239 let vl: i64 = ca_copy(rv + nr * CA_NAME_W, CA_NAME_W - 1, fld, ca_len(fld)) 240 let cell: *u8 = rv + nr * CA_NAME_W 241 cell[vl] = CA_CH_LPAREN as u8 242 cell[vl + 1] = 0 as u8 243 nr = nr + 1 244 } 245 ls = ca_row(cbuf, cn, "ruler" as *u8, nr) 246 } 247 st[CA_S_RULERS] = nr 248 // allow rows: path, function, reason, used flag 249 let ap: *u8 = sys_mmap(CA_MAX_ROWS * CA_PATH_W) 250 let af: *u8 = sys_mmap(CA_MAX_ROWS * CA_NAME_W) 251 let ar: *u8 = sys_mmap(CA_MAX_ROWS * CA_REASON_W) 252 let au: *i64 = sys_mmap(CA_MAX_ROWS * CA_I64) as *i64 253 var na: i64 = 0 254 ls = ca_row(cbuf, cn, "allow" as *u8, na) 255 while ls >= 0 { 256 if na < CA_MAX_ROWS { 257 ca_field(cbuf, cn, ls, 1, ap + na * CA_PATH_W, CA_PATH_W) 258 ca_field(cbuf, cn, ls, 2, af + na * CA_NAME_W, CA_NAME_W) 259 ca_field(cbuf, cn, ls, 3, ar + na * CA_REASON_W, CA_REASON_W) 260 au[na] = 0 261 na = na + 1 262 } 263 ls = ca_row(cbuf, cn, "allow" as *u8, na) 264 } 265 // per-file function table 266 let fn_name: *u8 = sys_mmap(CA_MAX_FUNCS * CA_NAME_W) 267 let fn_s: *i64 = sys_mmap(CA_MAX_FUNCS * CA_I64) as *i64 268 let fn_e: *i64 = sys_mmap(CA_MAX_FUNCS * CA_I64) as *i64 269 let path: *u8 = sys_mmap(CA_PATH_W) 270 let scratch: *u8 = sys_mmap(CA_PATH_W) 271 let sn_p: *i64 = sys_mmap(CA_I64) as *i64 272 var em_n: i64 = 0 273 var si: i64 = 0 274 ls = ca_row(cbuf, cn, "source" as *u8, si) 275 while ls >= 0 { 276 ca_field(cbuf, cn, ls, 1, path, CA_PATH_W) 277 st[CA_S_SOURCES] = st[CA_S_SOURCES] + 1 278 let raw: *u8 = ca_read_source(path, sn_p, scratch) 279 let sn: i64 = sn_p[0] 280 if sn < 0 { 281 st[CA_S_UNREADABLE] = st[CA_S_UNREADABLE] + 1 282 ca_put(1, "SOURCE-UNREADABLE path=" as *u8); ca_put(1, path); ca_put(1, " (a census over an absent file is a green by absence -- fix the row or the tree)\n" as *u8) 283 } else { 284 let dbuf: *u8 = sys_mmap(sn + 1) 285 let dn: i64 = ca_strip(raw, sn, dbuf) 286 // function table: a line starting with "func " opens a function that runs to the next such line 287 var nf: i64 = 0 288 var i: i64 = 0 289 while i < dn { 290 var le: i64 = i 291 while le < dn { if dbuf[le] == (CA_CH_NL as u8) { break } le = le + 1 } 292 if ca_starts(dbuf + i, le - i, "func " as *u8) == 1 { 293 if nf < CA_MAX_FUNCS { 294 var ne: i64 = i + 5 295 while ne < le { if dbuf[ne] == (CA_CH_LPAREN as u8) { break } ne = ne + 1 } 296 ca_copy(fn_name + nf * CA_NAME_W, CA_NAME_W, dbuf + i + 5, ne - (i + 5)) 297 fn_s[nf] = i 298 if nf > 0 { fn_e[nf - 1] = i } 299 nf = nf + 1 300 } 301 } 302 i = le + 1 303 } 304 if nf > 0 { fn_e[nf - 1] = dn } 305 st[CA_S_FUNCS] = st[CA_S_FUNCS] + nf 306 // classify every function that writes an svg start tag 307 var f: i64 = 0 308 while f < nf { 309 let body: *u8 = dbuf + fn_s[f] 310 let bl: i64 = fn_e[f] - fn_s[f] 311 var charts: i64 = 0 312 var icons: i64 = 0 313 var p: i64 = ca_find(body, bl, "<svg" as *u8) 314 var off: i64 = 0 315 while p >= 0 { 316 if ca_tag_is_icon(body, bl, off + p) == 1 { icons = icons + 1 } else { charts = charts + 1 } 317 off = off + p + 4 318 p = ca_find(body + off, bl - off, "<svg" as *u8) 319 } 320 let name: *u8 = fn_name + f * CA_NAME_W 321 if charts == 0 { 322 if icons > 0 { 323 st[CA_S_ICONS] = st[CA_S_ICONS] + 1 324 ca_put(1, "ICON file=" as *u8); ca_put(1, path); ca_put(1, " func=" as *u8); ca_put(1, name); ca_put(1, " (aria-hidden: decorative, not judged)\n" as *u8) 325 } 326 } else { 327 st[CA_S_EMITTERS] = st[CA_S_EMITTERS] + 1 328 // the prefix family: every function in this file whose name starts with the emitter's prefix 329 var pl: i64 = 0 330 while name[pl] != (0 as u8) { if name[pl] == (CA_CH_USCORE as u8) { pl = pl + 1; break } pl = pl + 1 } 331 var fam: i64 = 0 332 var ruler_hits: i64 = 0 333 var colour_hits: i64 = 0 334 var g: i64 = 0 335 while g < nf { 336 let gname: *u8 = fn_name + g * CA_NAME_W 337 var same: i64 = 1 338 var q: i64 = 0 339 while q < pl { if gname[q] != name[q] { same = 0; break } q = q + 1 } 340 if same == 1 { 341 fam = fam + 1 342 let gb: *u8 = dbuf + fn_s[g] 343 let gl: i64 = fn_e[g] - fn_s[g] 344 var r: i64 = 0 345 while r < nr { if ca_find(gb, gl, rv + r * CA_NAME_W) >= 0 { ruler_hits = ruler_hits + 1 } r = r + 1 } 346 if ca_find(gb, gl, "fill='rgb(" as *u8) >= 0 { colour_hits = colour_hits + 1 } 347 if ca_find(gb, gl, "stroke='rgb(" as *u8) >= 0 { colour_hits = colour_hits + 1 } 348 } 349 g = g + 1 350 } 351 var v: i64 = CA_HAND 352 if ruler_hits > 0 { v = CA_ADOPTER; if colour_hits > 0 { v = CA_HAND_COLOUR } } 353 if v == CA_ADOPTER { st[CA_S_ADOPTERS] = st[CA_S_ADOPTERS] + 1 } 354 if v == CA_HAND { st[CA_S_HAND] = st[CA_S_HAND] + 1 } 355 if v == CA_HAND_COLOUR { st[CA_S_HAND_COLOUR] = st[CA_S_HAND_COLOUR] + 1 } 356 var allowed: i64 = -1 357 if v != CA_ADOPTER { 358 var a: i64 = 0 359 while a < na { 360 if ca_eq(ap + a * CA_PATH_W, path) == 1 { if ca_eq(af + a * CA_NAME_W, name) == 1 { allowed = a; au[a] = 1; break } } 361 a = a + 1 362 } 363 if allowed >= 0 { st[CA_S_ALLOWED] = st[CA_S_ALLOWED] + 1 } else { st[CA_S_OFFENDERS] = st[CA_S_OFFENDERS] + 1 } 364 } 365 ca_put(1, "EMITTER file=" as *u8); ca_put(1, path); ca_put(1, " func=" as *u8); ca_put(1, name) 366 ca_put(1, " verdict=" as *u8); ca_put(1, ca_verdict_word(v)) 367 ca_put(1, " family=" as *u8); ca_putn(1, fam); ca_put(1, " ruler_calls=" as *u8); ca_putn(1, ruler_hits); ca_put(1, " colour_literals=" as *u8); ca_putn(1, colour_hits) 368 if allowed >= 0 { ca_put(1, " ALLOWED: " as *u8); ca_put(1, ar + allowed * CA_REASON_W) } 369 if v != CA_ADOPTER { if allowed < 0 { ca_put(1, " OFFENDER: compose the ruler from the emitter or its prefix family, or add an allow row with the reason" as *u8) } } 370 ca_put(1, "\n" as *u8) 371 if em_n < CA_MAX_EMIT { ca_copy(em_names + em_n * CA_NAME_W, CA_NAME_W, name, ca_len(name)); em_verd[em_n] = v; em_n = em_n + 1 } 372 else { st[CA_S_EMIT_TRUNC] = st[CA_S_EMIT_TRUNC] + 1 } 373 } 374 f = f + 1 375 } 376 } 377 si = si + 1 378 ls = ca_row(cbuf, cn, "source" as *u8, si) 379 } 380 // stale allow rows: an exemption nobody needs any more is a ratchet that stopped tightening 381 var a2: i64 = 0 382 while a2 < na { 383 if au[a2] == 0 { 384 st[CA_S_STALE_ALLOW] = st[CA_S_STALE_ALLOW] + 1 385 ca_put(1, "STALE-ALLOW file=" as *u8); ca_put(1, ap + a2 * CA_PATH_W); ca_put(1, " func=" as *u8); ca_put(1, af + a2 * CA_NAME_W); ca_put(1, " -- no longer an offender (or no longer exists): delete the row so the ratchet tightens\n" as *u8) 386 } 387 a2 = a2 + 1 388 } 389 em_verd[CA_MAX_EMIT] = em_n 390 return 0 391} 392 393// ---- fixtures, assembled at runtime so no source scanner can mistake them for the fleet ---- 394func ca_fx_adopter() -> i64 { 395 sys_unlinkat(CA_FX_A) 396 let fd: i64 = sys_openat_wr(CA_FX_A, CA_FILE_MODE) 397 if fd < 0 { return -1 } 398 ca_put(fd, "// adopter fixture: the chart family calls the ruler\nfunc ok_x(v: i64) -> i64 { return cl_scale(v, 0, 100, 10, 700) }\nfunc ok_chart(fd: i64) -> i64 {\n w(fd, " as *u8); ca_q(fd) 399 ca_put(fd, "<p>see https://example.org/</p><svg viewBox='0 0 800 500' width='800' height='500'>" as *u8); ca_q(fd) 400 ca_put(fd, " as *u8)\n w(fd, " as *u8); ca_q(fd); ca_put(fd, "<circle cx='" as *u8); ca_q(fd); ca_put(fd, " as *u8); wn(fd, ok_x(42)); w(fd, " as *u8); ca_q(fd) 401 ca_put(fd, "' fill='var(--nx-color-accent)'/></svg>" as *u8); ca_q(fd); ca_put(fd, " as *u8)\n return 0\n}\n" as *u8) 402 sys_close(fd) 403 return 0 404} 405func ca_fx_hand() -> i64 { 406 sys_unlinkat(CA_FX_H) 407 let fd: i64 = sys_openat_wr(CA_FX_H, CA_FILE_MODE) 408 if fd < 0 { return -1 } 409 ca_put(fd, "func note_only(fd: i64) -> i64 {\n // a legacy <svg chart named in a comment only\n return 0\n}\nfunc hand_chart(fd: i64) -> i64 {\n w(fd, " as *u8); ca_q(fd) 410 ca_put(fd, "<svg viewBox='0 0 800 500'>" as *u8); ca_q(fd); ca_put(fd, " as *u8)\n let x: i64 = 70 + (42 * 660) / 100\n w(fd, " as *u8); ca_q(fd) 411 ca_put(fd, "<circle cx='" as *u8); ca_q(fd); ca_put(fd, " as *u8); wn(fd, x); w(fd, " as *u8); ca_q(fd) 412 ca_put(fd, "' fill='var(--nx-color-accent)'/></svg>" as *u8); ca_q(fd); ca_put(fd, " as *u8)\n return 0\n}\n" as *u8) 413 sys_close(fd) 414 return 0 415} 416func ca_fx_colour() -> i64 { 417 sys_unlinkat(CA_FX_C) 418 let fd: i64 = sys_openat_wr(CA_FX_C, CA_FILE_MODE) 419 if fd < 0 { return -1 } 420 ca_put(fd, "func col_x(v: i64) -> i64 { return cl_scale(v, 0, 100, 10, 700) }\nfunc col_chart(fd: i64) -> i64 {\n w(fd, " as *u8); ca_q(fd) 421 ca_put(fd, "<svg viewBox='0 0 800 500'><rect x='10' y='10' width='40' height='40' fill='rgb(88,64,180)'/></svg>" as *u8); ca_q(fd) 422 ca_put(fd, " as *u8)\n return 0\n}\n" as *u8) 423 sys_close(fd) 424 return 0 425} 426func ca_fx_icon() -> i64 { 427 sys_unlinkat(CA_FX_I) 428 let fd: i64 = sys_openat_wr(CA_FX_I, CA_FILE_MODE) 429 if fd < 0 { return -1 } 430 ca_put(fd, "func icon_nav(fd: i64) -> i64 {\n w(fd, " as *u8); ca_q(fd) 431 ca_put(fd, "<nav><svg viewBox='0 0 24 24' width='22' height='22' aria-hidden='true'><path d='M3 3h18'/></svg></nav>" as *u8); ca_q(fd) 432 ca_put(fd, " as *u8)\n return 0\n}\n" as *u8) 433 sys_close(fd) 434 return 0 435} 436func ca_fx_conf(path: *u8, allow_row: *u8) -> i64 { 437 sys_unlinkat(path) 438 let fd: i64 = sys_openat_wr(path, CA_FILE_MODE) 439 if fd < 0 { return -1 } 440 ca_put(fd, "source|/tmp/nx_chartlay_adoption_gate/adopter.nx\nsource|/tmp/nx_chartlay_adoption_gate/hand.nx\nsource|/tmp/nx_chartlay_adoption_gate/colour.nx\nsource|/tmp/nx_chartlay_adoption_gate/icon.nx\nruler|cl_scale\nruler|cl_place\n" as *u8) 441 ca_put(fd, allow_row) 442 sys_close(fd) 443 return 0 444} 445 446func main() -> i64 { 447 let ctr: *i64 = gv_ctr() 448 sys_mkdir(CA_DIR, CA_DIR_MODE) 449 let st: *i64 = sys_mmap(CA_S_W * CA_I64) as *i64 450 let em_names: *u8 = sys_mmap(CA_MAX_EMIT * CA_NAME_W) 451 let em_verd: *i64 = sys_mmap((CA_MAX_EMIT + 1) * CA_I64) as *i64 452 // ---- planted fixtures: every class both ways ---- 453 gv_check_eq("F0-fixtures-written" as *u8, ca_fx_adopter() + ca_fx_hand() + ca_fx_colour() + ca_fx_icon() + ca_fx_conf(CA_FX_CONF, "" as *u8), 0, ctr) 454 gv_check_eq("F1-fixture-census-runs" as *u8, cl_adoption_gate(CA_FX_CONF, st, em_names, em_verd), 0, ctr) 455 gv_check_eq("F2-four-sources-all-readable" as *u8, st[CA_S_SOURCES] * 10 + st[CA_S_UNREADABLE], 40, ctr) 456 gv_check_eq("F3-three-chart-emitters" as *u8, st[CA_S_EMITTERS], 3, ctr) 457 gv_check_eq("F3a-partition-emitters-equal-adopters-plus-hand-plus-hand-colour" as *u8, st[CA_S_ADOPTERS] + st[CA_S_HAND] + st[CA_S_HAND_COLOUR], st[CA_S_EMITTERS], ctr) 458 gv_check_eq("F4-the-icon-is-counted-not-judged" as *u8, st[CA_S_ICONS], 1, ctr) 459 gv_check_eq("F4a-the-icon-function-is-not-an-emitter" as *u8, ca_verdict_of(em_names, em_verd, em_verd[CA_MAX_EMIT], "icon_nav" as *u8), CA_NONE, ctr) 460 gv_check_eq("F5-the-ruler-caller-is-an-adopter" as *u8, ca_verdict_of(em_names, em_verd, em_verd[CA_MAX_EMIT], "ok_chart" as *u8), CA_ADOPTER, ctr) 461 gv_check_eq("F5a-a-url-before-the-tag-does-not-hide-it" as *u8, st[CA_S_ADOPTERS], 1, ctr) 462 gv_check_eq("F6-inline-arithmetic-is-hand" as *u8, ca_verdict_of(em_names, em_verd, em_verd[CA_MAX_EMIT], "hand_chart" as *u8), CA_HAND, ctr) 463 gv_check_eq("F6a-a-comment-only-mention-is-nothing" as *u8, ca_verdict_of(em_names, em_verd, em_verd[CA_MAX_EMIT], "note_only" as *u8), CA_NONE, ctr) 464 gv_check_eq("F7-a-literal-fill-beside-the-ruler-is-hand-colour" as *u8, ca_verdict_of(em_names, em_verd, em_verd[CA_MAX_EMIT], "col_chart" as *u8), CA_HAND_COLOUR, ctr) 465 gv_check_eq("F8-two-offenders-with-no-allow-rows" as *u8, st[CA_S_OFFENDERS], 2, ctr) 466 gv_check_eq("F8a-nothing-allowed-nothing-stale" as *u8, st[CA_S_ALLOWED] + st[CA_S_STALE_ALLOW], 0, ctr) 467 // an allow row with a reason exempts exactly that function 468 gv_check_eq("F9-allow-conf-written" as *u8, ca_fx_conf(CA_FX_CONF_ALLOW, "allow|/tmp/nx_chartlay_adoption_gate/hand.nx|hand_chart|a planted exemption with its reason\n" as *u8), 0, ctr) 469 gv_check_eq("F9a-allow-census-runs" as *u8, cl_adoption_gate(CA_FX_CONF_ALLOW, st, em_names, em_verd), 0, ctr) 470 gv_check_eq("F9b-the-allowed-function-is-exempt" as *u8, st[CA_S_ALLOWED], 1, ctr) 471 gv_check_eq("F9c-one-offender-remains" as *u8, st[CA_S_OFFENDERS], 1, ctr) 472 gv_check_eq("F9d-a-used-allow-row-is-not-stale" as *u8, st[CA_S_STALE_ALLOW], 0, ctr) 473 // NEG: an allow row for a function that is not an offender is stale and must read RED 474 gv_check_eq("F10-stale-conf-written" as *u8, ca_fx_conf(CA_FX_CONF_STALE, "allow|/tmp/nx_chartlay_adoption_gate/adopter.nx|ok_chart|an exemption nobody needs\n" as *u8), 0, ctr) 475 gv_check_eq("F10a-stale-census-runs" as *u8, cl_adoption_gate(CA_FX_CONF_STALE, st, em_names, em_verd), 0, ctr) 476 gv_check_eq("neg-control-F10b-an-allow-row-for-an-adopter-is-stale" as *u8, st[CA_S_STALE_ALLOW], 1, ctr) 477 gv_check_eq("neg-control-F10c-and-it-exempts-nothing" as *u8, st[CA_S_ALLOWED] * 10 + st[CA_S_OFFENDERS], 2, ctr) 478 gv_check_eq("neg-control-F10d-an-unreadable-conf-is-refused" as *u8, cl_adoption_gate("/tmp/nx_chartlay_adoption_gate/absent.conf" as *u8, st, em_names, em_verd), -1, ctr) 479 // ---- the live fleet: the compare generators named in cl_adoption.conf ---- 480 var conf: *u8 = CA_CONF 481 if ca_exists(conf) == 0 { conf = CA_CONF_BR } 482 gv_need("L0-cl_adoption.conf-present" as *u8, ca_exists(conf), ctr) 483 ca_put(1, "LIVE conf=" as *u8); ca_put(1, conf); ca_put(1, "\n" as *u8) 484 gv_check_eq("L1-live-census-runs" as *u8, cl_adoption_gate(conf, st, em_names, em_verd), 0, ctr) 485 gv_check("L2-at-least-one-source-row" as *u8, (st[CA_S_SOURCES] > 0) as i64, ctr) 486 gv_check_eq("L3-every-source-readable" as *u8, st[CA_S_UNREADABLE], 0, ctr) 487 gv_check("L4-ruler-verbs-declared" as *u8, (st[CA_S_RULERS] > 0) as i64, ctr) 488 gv_check("L5-the-fleet-emits-at-least-one-chart" as *u8, (st[CA_S_EMITTERS] > 0) as i64, ctr) 489 gv_check_eq("L5a-partition-sums" as *u8, st[CA_S_ADOPTERS] + st[CA_S_HAND] + st[CA_S_HAND_COLOUR], st[CA_S_EMITTERS], ctr) 490 gv_check_eq("L6-the-position-map-figure-is-an-adopter" as *u8, ca_verdict_of(em_names, em_verd, em_verd[CA_MAX_EMIT], "cdp_fig_open" as *u8), CA_ADOPTER, ctr) 491 gv_check_eq("L7-no-unallowed-hand-chart-in-the-fleet" as *u8, st[CA_S_OFFENDERS], 0, ctr) 492 gv_check_eq("L8-no-stale-allow-row" as *u8, st[CA_S_STALE_ALLOW], 0, ctr) 493 gv_check_eq("L9-emitter-table-not-truncated" as *u8, st[CA_S_EMIT_TRUNC], 0, ctr) 494 gv_kv("live_sources" as *u8, st[CA_S_SOURCES]) 495 gv_kv("live_functions" as *u8, st[CA_S_FUNCS]) 496 gv_kv("live_emitters" as *u8, st[CA_S_EMITTERS]) 497 gv_kv("live_adopters" as *u8, st[CA_S_ADOPTERS]) 498 gv_kv("live_hand" as *u8, st[CA_S_HAND]) 499 gv_kv("live_hand_colour" as *u8, st[CA_S_HAND_COLOUR]) 500 gv_kv("live_icons" as *u8, st[CA_S_ICONS]) 501 gv_kv("live_allowed" as *u8, st[CA_S_ALLOWED]) 502 gv_kv("live_stale_allow" as *u8, st[CA_S_STALE_ALLOW]) 503 gv_kv("live_offenders" as *u8, st[CA_S_OFFENDERS]) 504 return gv_verdict("nx_chartlay_adoption_gate" as *u8, ctr, "the fleet ratchet for the one chart-layout ruler: every svg-emitting function in the compare generators named and judged (adopter, hand, hand-colour; icons counted, comments nothing), allow rows exempt with a reason and go stale when unneeded, planted fixtures prove every class both ways, and the live fleet carries no unallowed hand chart" as *u8) 505}