code wiki / (root) / nx_func_extract.nx

nx_func_extract.nx source

↩ module page · 331 lines · 12899 B

1// nx_extract.nx -- function-body extraction + normalization + hashing. 2// 3// Substrate-native primitive for semantic-equivalence verification. 4// Foundation for the dedup-then-consolidate workflow: extract a 5// named function from a file, normalize away comments and whitespace, 6// compute a deterministic hash, compare across files. 7// 8// Capabilities provided: 9// - nx_extract_func_body : locate `func NAME(...)` and return its 10// body byte range [body_start, body_end) 11// - nx_extract_normalize : copy a buffer slice with line comments 12// removed and runs of whitespace collapsed 13// to single spaces; strings preserved verbatim 14// - nx_extract_hash : FNV-1a 64-bit hash of a byte range 15// - nx_extract_func_hash : convenience -- extract + normalize + hash 16// 17// All pure-NishiLang. No reliance on external grep/awk/sed. This is 18// the substrate's answer to "diff two functions for equivalence" 19// without leaving NishiLang. 20// 21// genealogy_id: fowler_noll_vo_1991_fnv1a + baker_1995_clone_detection 22// lineage_id: syntax_directed_function_extraction + content_hash 23// axioms: NX_AX_REL_REFLEXIVITY (hash(a) = hash(a)) 24// + NX_AX_REL_SYMMETRY (hash(a)=hash(b) <-> hash(b)=hash(a)) 25// + NX_AX_REL_TRANSITIVITY (a~b, b~c -> a~c) 26// together: hash-equality is an equivalence relation; 27// collisions are theoretically possible (Pigeonhole) but 28// FNV-1a 64-bit collision probability is negligible at 29// our function-population scale (thousands, not billions). 30 31// nx_safety_envelope: 32// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 33// sil_target: SIL1 34// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 35// verdict: NOT_YET_EVALUATED 36 37import "syscalls.nx" 38import "runtime.nx" 39import "nx_axioms.nx" 40import "nx_lex.nx" 41 42const NX_EXTRACT_FNV64_OFFSET: i64 = 0xcbf29ce484222325 43const NX_EXTRACT_FNV64_PRIME: i64 = 1099511628211 // 0x100000001b3 44 45// ===== character class helpers ========================================= 46 47// is_ws canonical in nx_lex.nx. 48 49func nx_extract_is_alnum_us(c: i64) -> i64 { 50 if c >= 48 { if c <= 57 { return 1 } } // 0-9 51 if c >= 65 { if c <= 90 { return 1 } } // A-Z 52 if c >= 97 { if c <= 122 { return 1 } } // a-z 53 if c == 95 { return 1 } // _ 54 return 0 55} 56 57// ===== function body extraction ======================================== 58// 59// Walks the buffer looking for `func NAME(`. When found, scans 60// forward for the opening `{`, then tracks brace depth (ignoring 61// braces inside string literals) until depth returns to 0. 62// 63// Writes the body byte range (start at the `{`, end just past matching `}`) 64// to out_start / out_end. Returns 0 on success, -1 if not found. 65 66func nx_extract_func_body(buf: *u8, len: i64, fname: *u8, 67 out_start: *i64, out_end: *i64) -> i64 { 68 let fname_len: i64 = strlen(fname) 69 if fname_len <= 0 { return -1 } 70 71 var i: i64 = 0 72 var found: i64 = 0 73 while found == 0 { 74 if i >= len { return -1 } 75 76 // Match "func " at line start. 77 var at_line_start: i64 = 0 78 if i == 0 { at_line_start = 1 } 79 if at_line_start == 0 { 80 if buf[i - 1] == 10 { at_line_start = 1 } 81 } 82 83 var is_match: i64 = 0 84 if at_line_start == 1 { 85 if i + 5 <= len { 86 if buf[i] == 102 { // 'f' 87 if buf[i + 1] == 117 { // 'u' 88 if buf[i + 2] == 110 { // 'n' 89 if buf[i + 3] == 99 { // 'c' 90 let sep: i64 = buf[i + 4] 91 if sep == 32 { is_match = 1 } 92 if sep == 9 { is_match = 1 } 93 } 94 } 95 } 96 } 97 } 98 } 99 100 if is_match == 1 { 101 // Skip whitespace after "func". 102 var p: i64 = i + 5 103 var done_ws: i64 = 0 104 while done_ws == 0 { 105 if p >= len { done_ws = 1 } 106 if done_ws == 0 { 107 if buf[p] == 32 { p = p + 1 } 108 if buf[p] != 32 { 109 if buf[p] == 9 { p = p + 1 } 110 if buf[p] != 9 { done_ws = 1 } 111 } 112 } 113 } 114 // Compare with fname (must be followed by '('). 115 if p + fname_len + 1 <= len { 116 if strneq(((buf as i64) + p) as *u8, fname, fname_len) == 1 { 117 if buf[p + fname_len] == 40 { // '(' 118 // Found. Now find next '{'. 119 var q: i64 = p + fname_len 120 var found_lbrace: i64 = 0 121 while found_lbrace == 0 { 122 if q >= len { return -1 } 123 if buf[q] == 123 { // '{' 124 found_lbrace = 1 125 } 126 if found_lbrace == 0 { q = q + 1 } 127 } 128 // q points at '{'. Walk to matching '}'. 129 let start: i64 = q 130 var depth: i64 = 1 131 q = q + 1 132 while depth > 0 { 133 if q >= len { return -1 } 134 let c: i64 = buf[q] 135 var handled: i64 = 0 136 137 // string literal 138 if c == 34 { // '"' 139 q = q + 1 140 var done_str: i64 = 0 141 while done_str == 0 { 142 if q >= len { return -1 } 143 let sc: i64 = buf[q] 144 if sc == 92 { // '\' 145 q = q + 2 146 } 147 if sc != 92 { 148 if sc == 34 { // closing " 149 done_str = 1 150 } 151 q = q + 1 152 } 153 } 154 handled = 1 155 } 156 157 // line comment "//" 158 if handled == 0 { 159 if c == 47 { 160 if q + 1 < len { 161 if buf[q + 1] == 47 { 162 q = q + 2 163 var done_cmt: i64 = 0 164 while done_cmt == 0 { 165 if q >= len { done_cmt = 1 } 166 if done_cmt == 0 { 167 if buf[q] == 10 { done_cmt = 1 } 168 q = q + 1 169 } 170 } 171 handled = 1 172 } 173 } 174 } 175 } 176 177 // open brace 178 if handled == 0 { 179 if c == 123 { 180 depth = depth + 1 181 q = q + 1 182 handled = 1 183 } 184 } 185 186 // close brace 187 if handled == 0 { 188 if c == 125 { 189 depth = depth - 1 190 q = q + 1 191 handled = 1 192 } 193 } 194 195 // default: advance one byte 196 if handled == 0 { q = q + 1 } 197 } 198 out_start[0] = start 199 out_end[0] = q // just past matching '}' 200 return 0 201 } 202 } 203 } 204 } 205 i = i + 1 206 } 207 return -1 208} 209 210// ===== normalization ======================================================= 211// 212// Copy buf[start..end) into out, removing: 213// - line comments (// ... \n) 214// - runs of whitespace collapsed to single space (ASCII 32) 215// Strings preserved byte-for-byte. 216// Leading/trailing whitespace dropped. 217// Returns output length. 218 219func nx_extract_normalize(buf: *u8, start: i64, end: i64, out: *u8) -> i64 { 220 var out_pos: i64 = 0 221 var last_was_ws: i64 = 1 // start "before" content 222 var i: i64 = start 223 while i < end { 224 let c: i64 = buf[i] 225 226 // line comment 227 var consumed: i64 = 0 228 if c == 47 { // '/' 229 if i + 1 < end { 230 if buf[i + 1] == 47 { 231 // skip to newline 232 i = i + 2 233 var done_cmt: i64 = 0 234 while done_cmt == 0 { 235 if i >= end { done_cmt = 1 } 236 if done_cmt == 0 { 237 if buf[i] == 10 { done_cmt = 1 } 238 i = i + 1 239 } 240 } 241 consumed = 1 242 } 243 } 244 } 245 246 if consumed == 0 { 247 // string literal: copy verbatim including quotes 248 if c == 34 { 249 out[out_pos] = c 250 out_pos = out_pos + 1 251 last_was_ws = 0 252 i = i + 1 253 var done_str: i64 = 0 254 while done_str == 0 { 255 if i >= end { done_str = 1 } 256 if done_str == 0 { 257 let sc: i64 = buf[i] 258 out[out_pos] = sc 259 out_pos = out_pos + 1 260 if sc == 92 { // '\' 261 i = i + 1 262 if i < end { 263 out[out_pos] = buf[i] 264 out_pos = out_pos + 1 265 } 266 i = i + 1 267 } 268 if sc != 92 { 269 if sc == 34 { done_str = 1 } 270 i = i + 1 271 } 272 } 273 } 274 consumed = 1 275 } 276 } 277 278 if consumed == 0 { 279 if nx_lex_is_ws(c) == 1 { 280 if last_was_ws == 0 { 281 out[out_pos] = 32 // single space 282 out_pos = out_pos + 1 283 last_was_ws = 1 284 } 285 i = i + 1 286 } 287 if nx_lex_is_ws(c) == 0 { 288 out[out_pos] = c 289 out_pos = out_pos + 1 290 last_was_ws = 0 291 i = i + 1 292 } 293 } 294 } 295 // Drop trailing space if any. 296 if out_pos > 0 { 297 if out[out_pos - 1] == 32 { out_pos = out_pos - 1 } 298 } 299 out[out_pos] = 0 300 return out_pos 301} 302 303// ===== FNV-1a 64-bit hash ============================================== 304// 305// h = FNV_OFFSET 306// for each byte b: h = (h XOR b) * FNV_PRIME 307// In i64: NishiLang's multiplication wraps to 2^63 -- equivalent mod 2^64 308// for our purposes since collisions are content-dependent, not modulus- 309// dependent. 310 311func nx_extract_hash(buf: *u8, len: i64) -> i64 { 312 var h: i64 = NX_EXTRACT_FNV64_OFFSET 313 var i: i64 = 0 314 while i < len { 315 h = h ^ buf[i] 316 h = h * NX_EXTRACT_FNV64_PRIME 317 i = i + 1 318 } 319 return h 320} 321 322// ===== convenience ===================================================== 323 324func nx_extract_func_hash(buf: *u8, len: i64, fname: *u8, 325 norm_buf: *u8) -> i64 { 326 let s: *i64 = (sys_mmap(8)) as *i64 327 let e: *i64 = (sys_mmap(8)) as *i64 328 if nx_extract_func_body(buf, len, fname, s, e) != 0 { return 0 } 329 let nlen: i64 = nx_extract_normalize(buf, s[0], e[0], norm_buf) 330 return nx_extract_hash(norm_buf, nlen) 331}