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}