nx_normdiff.nx
buildroot/runtime/nx_normdiff.nx
about
nx_normdiff.nx -- IS THIS DIFFERENCE MADE OF CODE, OR OF COSMETICS? The escalation ruler.
WHY IT EXISTS. nx_srcdiff answers "is a direction merge safe?" on the RAW line multiset, and that
strictness is correct as a GUARD -- it is what caught the escaped nx_media_extract mutant that a
symbol-set ruler waved through. But as a CENSUS it overstates the fork: it reports BIDIRECTIONAL
for two files that differ only because a NAS cron lifted `4096` into `FOO_MAGIC_4096`, or because
one side carries a trailing `// why` comment the other lacks. Measured on the standing residual:
the raw ruler could decide 3.6% of 3,073 forked files; normalising first decides 8.6%. The other
91% is REAL CODE -- surplus shapes censused as control-flow 1408, bindings 1042, func-decls 278 --
which is the finding that matters most: NO FURTHER NORMALISER WILL CRACK IT, so stop building them.
=> ★A RULER TUNED TO BE A SAFE GUARD IS THE WRONG RULER FOR A CENSUS. Report BOTH verdicts and
let the caller pick: the guard decides whether to ACT, the census decides what to LOOK AT.
WHAT IT NORMALISES, and nothing else:
1. a trailing `//` comment -- ONLY when outside a string literal (backslash-escape aware, so
`"http://x"` and `"a\"b // c"` are not mistaken for comments)
2. runs of whitespace -> one space; leading/trailing stripped
3. a const identifier -> ITS DECLARED VALUE TEXT, read out of the file that declares it.
!!THE OBVIOUS SHORTCUT IS UNSOUND, measured: reading the digits out of `FOO_MAGIC_4096` breaks
on nx_lz4_header's `const LZ4_MAGIC_0: i64 = 0x04` and nx_nxtask_store's `NXT_MAGIC_0 = 0x4E`.
There the trailing digit is a FIELD INDEX ("magic byte 0"), and the value is an unrelated hex
byte. A name-digit un-lift equates 0 with 4 and calls two different files identical.
⇒ ★A NAMING CONVENTION IS A COMMENT: IT IS EVIDENCE ABOUT INTENT, NEVER ABOUT VALUE.
The value is substituted as its DECLARED TEXT, verbatim and unparsed. That is deliberate: a
parser that "helpfully" normalised 0x04 to 4 would need to be right about every literal form
in the language, and being wrong about one is how the line above got mis-measured TWICE (a
decimal regex silently matched the `0` out of `0x04` and reported the const as declared zero).
⇒ ★DON'T PARSE WHAT YOU ONLY NEED TO COMPARE.
WHERE THE TABLE COMES FROM, and why there is no tree walk here: NishiLang requires consts above
use, and the NAS magicsweep lifts a literal into a const IN THE SAME FILE. Measured across both
trees: 3,728 of 3,730 lifted-const uses (99.946%) are declared in the file that uses them -- the
only two exceptions tree-wide are nx_archive_server's K_MAGIC_8080 and nx_web_crawl_step_gate's
WC_MAGIC_2048. So each side's table is built FROM THAT SIDE'S OWN SOURCE, which is also the
semantically correct scope: a const means what ITS file declares it to mean. Two files that
declare the same name differently therefore normalise DIFFERENTLY and stay visible as surplus,
which a single shared table would have silently equated.
⇒ ★A SYMBOL'S MEANING IS SCOPED; A LOOKUP TABLE THAT FORGETS THE SCOPE INVENTS AGREEMENT.
[consttab] remains as an optional FALLBACK for the cross-file residual; file-local always wins.
dependencies 1 imports · 0 importers
imports: nx_syscalls.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 58 | const ND_MAGIC_8192: i64 = 8192 |
| 60 | const ND_SLOTS: i64 = 131072 // power of two; a 1000-line file loads at <1% -- headroom is cheap |
| 61 | const ND_MASK: i64 = 131071 |
| 62 | const ND_OUTBUF: i64 = 4194304 |
| 63 | const ND_NUMBUF: i64 = 64 |
| 64 | const ND_TOK: i64 = 512 // longest identifier we will try to resolve |
| 65 | const ND_ARENA: i64 = 8388608 // fixed per-side arena for census mode; a source over this is REFUSED, never truncated |
| 66 | const ND_SURP_CAP: i64 = 16384 // surplus lines tracked per side for the supersede match |
| 67 | const ND_MAXIMP: i64 = 128 // direct imports followed; a .nx file today declares <20 |
| 68 | const ND_BCL_CAP: i64 = 65536 // B comment-sites tracked; a 1000-line file uses <2% |
| 69 | const ND_SLACK: i64 = 65536 // normalise can only shrink a line, but never trust that alone |
| 70 | const ND_FNV_OFF: i64 = 1469598103934665603 |
| 71 | const ND_FNV_PRIME: i64 = 1099511628211 |
| 72 | const ND_SAMPLE_CAP: i64 = 25 // stdout sample; the FULL surplus list always goes to [outfile] |
| 73 | const ND_LF: i64 = 10 |
| 74 | const ND_CR: i64 = 13 |
| 75 | const ND_TAB: i64 = 9 |
| 76 | const ND_SP: i64 = 32 |
| 77 | const ND_SLASH: i64 = 47 |
| 78 | const ND_QUOTE: i64 = 34 |
| 79 | const ND_BSLASH: i64 = 92 |
| 80 | const ND_USCORE: i64 = 95 |
functions
| 149 | func nd_puts(s: *u8) -> i64 |
| 155 | func nd_putn(v: i64) -> i64 |
| 166 | func nd_streq(a: *u8, b: *u8) -> i64 |
| 177 | func nd_hash(s: *u8) -> i64 |
| 188 | func nd_add(keys: *i64, counts: *i64, gen: *i64, line: *u8) -> i64 |
| 203 | func nd_get(keys: *i64, counts: *i64, gen: *i64, line: *u8) -> i64 |
| 216 | func nd_put_kv(keys: *i64, vals: *i64, gen: *i64, k: *u8, v: *u8) -> i64 |
| 227 | func nd_get_kv(keys: *i64, vals: *i64, gen: *i64, k: *u8) -> i64 |
| 238 | func nd_is_id_char(c: i64) -> i64 |
| 245 | func nd_is_id_start(c: i64) -> i64 called by 1: nd_norm |
| 254 | func nd_comment_at(src: *u8) -> i64 |
| 281 | func nd_len(s: *u8) -> i64 |
| 289 | func nd_norm(src: *u8, dst: *u8, ctk: *i64, ctv: *i64, ctg: *i64) -> i64 |
| 348 | func nd_scan_consts(buf: *u8, n: i64, ctk: *i64, ctv: *i64, ctg: *i64) -> i64 |
| 453 | func nd_scan_imports(buf: *u8, n: i64) -> i64 called by 1: nd_run_pair |
| 519 | func nd_is_dash(s: *u8) -> i64 called by 1: nd_run_pair |
| 525 | func nd_join_root(root: *u8, name: *u8, out: *u8) -> i64 |
| 536 | func nd_join_dir(path: *u8, name: *u8, out: *u8) -> i64 called by 1: nd_run_pair |
| 554 | func nd_fold_module(path: *u8) -> i64 |
| 630 | func nd_is_import(l: *u8) -> i64 called by 1: nd_supersedes |
| 641 | func nd_supersedes(a: *u8, b: *u8) -> i64 |
| 686 | func nd_selftest() -> i64 |
| 708 | func nd_emit(tag: *u8, n: i64, line: *u8) -> i64 |
| 731 | func nd_sample(tag: *u8, n: i64, line: *u8) -> i64 |
| 741 | func nd_load(buf: *u8, n: i64, rk: *i64, rc: *i64, rg: *i64, side: i64) -> i64 |
| 821 | func nd_lost_comments() -> i64 |
| 838 | func nd_load_consttab(path: *u8) -> i64 |
| 882 | func nd_run_pair(apath: *u8, bpath: *u8, iroot: *u8, quiet: i64, argc: i64, argv: *i64) -> i64 called by 3: nd_adoptnd_censusmain calls 22: sys_read_filend_putssys_exitnd_scan_constsnd_scan_importsnd_is_dash+16 |
| 1218 | func nd_copy_atomic(src: *u8, dst: *u8) -> i64 |
| 1250 | func nd_is_adopt(s: *u8) -> i64 called by 1: main |
| 1259 | func nd_is_apply(s: *u8) -> i64 called by 1: main |
| 1266 | func nd_is_census(s: *u8) -> i64 called by 1: main |
| 1288 | func nd_adopt(listp: *u8, aroot: *u8, broot: *u8, iroot: *u8, apply: i64) -> i64 |
| 1375 | func nd_census(listp: *u8, aroot: *u8, broot: *u8, iroot: *u8) -> i64 |
| 1427 | func main(argc: i64, argv: *i64) -> i64 |