code wiki / _hdl_build / nx_doctor_fix.nx

nx_doctor_fix.nx source

↩ module page · 118 lines · 4977 B

1// nx_doctor_fix.nx -- the DOCTOR organ (the FIXER role; runtime/source hygiene). Its 2// ONE responsibility: produce a candidate fix. A reserved-keyword-as-identifier 3// failure (e.g. `var match`) is mechanically fixable -- rename the offending word to a 4// non-reserved one. doc_heal reads a failing source, renames reserved words used as 5// identifiers (word-boundary aware, so `matched`/`index` are untouched), and writes a 6// healed candidate. RACI: the Doctor is RESPONSIBLE for the fix ONLY -- it does NOT 7// verify and does NOT admit its own work. The ENGINEER re-verifies (separate organ) 8// and the WARDEN/COUNCIL govern admission. The fixer never blesses its own fix. 9// Depends only on syscalls -- decoupled from the Engineer, so the roles stay clean. 10 11import "nx_syscalls.nx" 12 13func doc_is_ic(c: i64) -> i64 { 14 if c >= 48 { if c <= 57 { return 1 } } // 0-9 15 if c >= 65 { if c <= 90 { return 1 } } // A-Z 16 if c >= 97 { if c <= 122 { return 1 } } // a-z 17 if c == 95 { return 1 } // _ 18 return 0 19} 20func doc_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 21 22// word-boundary replace kw -> kw + "_" in src[0..slen) -> out. returns out length. 23func doc_rename(src: *u8, slen: i64, kw: *u8, out: *u8) -> i64 { 24 let klen: i64 = doc_slen(kw) 25 var i: i64 = 0 26 var o: i64 = 0 27 while i < slen { 28 var hit: i64 = 0 29 if i + klen <= slen { 30 var m: i64 = 1 31 var j: i64 = 0 32 while j < klen { if src[i + j] != kw[j] { m = 0 } j = j + 1 } 33 if m == 1 { 34 var lb: i64 = 1 35 if i > 0 { if doc_is_ic(src[i - 1] as i64) == 1 { lb = 0 } } 36 var rb: i64 = 1 37 if i + klen < slen { if doc_is_ic(src[i + klen] as i64) == 1 { rb = 0 } } 38 if lb == 1 { if rb == 1 { hit = 1 } } 39 } 40 } 41 if hit == 1 { 42 var j: i64 = 0 43 while j < klen { out[o] = kw[j]; o = o + 1; j = j + 1 } 44 out[o] = 95; o = o + 1 // '_' 45 i = i + klen 46 } else { 47 out[o] = src[i]; o = o + 1; i = i + 1 48 } 49 } 50 return o 51} 52 53func doc_write_file(path: *u8, buf: *u8, len: i64) -> i64 { 54 let fd: i64 = sys_openat_wr(path, 0x1a4) 55 if fd < 0 { return 0 - 1 } 56 sys_write(fd, buf, len) 57 sys_close(fd) 58 return 0 59} 60 61// find needle in hay[0..hlen). returns the start index, or -1 if absent. 62func doc_find(hay: *u8, hlen: i64, needle: *u8) -> i64 { 63 let nlen: i64 = doc_slen(needle) 64 var i: i64 = 0 65 while i + nlen <= hlen { 66 var m: i64 = 1 67 var j: i64 = 0 68 while j < nlen { if hay[i + j] != needle[j] { m = 0 } j = j + 1 } 69 if m == 1 { return i } 70 i = i + 1 71 } 72 return 0 - 1 73} 74 75// DIAGNOSE-the-fix: read the Engineer's captured diagnostic and extract the offending 76// identifier the compiler named -- the token inside `reserved keyword '...'`. Copies it 77// (NUL-terminated) into out and returns its length, or 0 if the diagnostic is not this 78// class. This is what makes the Doctor data-driven: it heals whatever keyword the 79// compiler flags, not a hardcoded set. The Doctor reads the artifact; it does not run 80// the compiler (that is the Engineer's detection). 81func doc_offending_kw(diag_path: *u8, out: *u8) -> i64 { 82 let lenp: *i64 = sys_mmap(8) as *i64 83 let buf: *u8 = sys_read_file(diag_path, lenp) 84 let blen: i64 = lenp[0] 85 let at: i64 = doc_find(buf, blen, "reserved keyword '" as *u8) 86 if at < 0 { out[0] = 0 as u8; return 0 } 87 var i: i64 = at + 18 // past "reserved keyword '" 88 var o: i64 = 0 89 while i < blen { if buf[i] == (39 as u8) { i = blen } else { out[o] = buf[i]; o = o + 1; i = i + 1 } } 90 out[o] = 0 as u8 91 return o 92} 93 94// HEAL one NAMED reserved keyword (the token the diagnostic gave us): rename every 95// word-boundary occurrence of kw -> kw_ . Returns chars added (= number of renames). 96func doc_heal_token(src_path: *u8, out_path: *u8, kw: *u8) -> i64 { 97 let lenp: *i64 = sys_mmap(8) as *i64 98 let src: *u8 = sys_read_file(src_path, lenp) 99 let slen: i64 = lenp[0] 100 let buf: *u8 = sys_mmap(slen * 2 + 256) 101 let olen: i64 = doc_rename(src, slen, kw, buf) 102 doc_write_file(out_path, buf, olen) 103 return olen - slen 104} 105 106// HEAL src_path -> out_path: rename reserved 'match' and 'in' used as identifiers. 107// Returns the number of characters added (1 per rename), 0 if nothing to fix. 108func doc_heal(src_path: *u8, out_path: *u8) -> i64 { 109 let lenp: *i64 = sys_mmap(8) as *i64 110 let src: *u8 = sys_read_file(src_path, lenp) 111 let slen: i64 = lenp[0] 112 let bufA: *u8 = sys_mmap(slen * 2 + 256) 113 let bufB: *u8 = sys_mmap(slen * 2 + 256) 114 let lenA: i64 = doc_rename(src, slen, "match" as *u8, bufA) 115 let lenB: i64 = doc_rename(bufA, lenA, "in" as *u8, bufB) 116 doc_write_file(out_path, bufB, lenB) 117 return lenB - slen 118}