code wiki / _hdl_build / nx_depcycle.nx
nx_depcycle.nx source
↩ module page · 298 lines · 11883 B
1// nx_depcycle.nx -- ACYCLIC DEPENDENCIES PRINCIPLE checker (industry SOTA, grounded in nishi research:
2// corpus confirms Coupling / Cohesion / Dependency-inversion / Clean-architecture as the canon). Reads
3// the ecosystem IMPORT GRAPH (the atlas edges) and finds circular dependencies: A imports B imports ...
4// imports A. Cycles are the top structural smell -- organs in a cycle can't be built / understood / tested
5// / reused independently, blocking modular OO design (Robert Martin, "Clean Architecture", ADP). Uses
6// KAHN topological reduction (iterative, no recursion): repeatedly remove zero-in-degree nodes; whatever
7// remains with in-degree>0 is trapped in a cycle = the ADP-violation set. Deterministic.
8// argv: <dir> [dir2...]
9// Report: CYCLENODE <basename> (each node still trapped after reduction), then
10// NX-DEPCYCLE nodes=N edges=E cyclic=C verdict=ACYCLIC|CYCLIC-C
11// exit 0 = ran. license_tier: ORIGINAL No hw writes (Rule 26).
12import "nx_seat_drive_lib.nx"
13import "nx_deploy_lib.nx"
14import "nx_syscalls.nx"
15const DC_MAGIC_262144: i64 = 262144
16const DC_MAGIC_1024: i64 = 1024
17
18const DC_MAXN: i64 = 30000
19const DC_MAXE: i64 = 200000
20const DC_NB: i64 = 65536
21const DC_NAMEBUF: i64 = 3145728
22const DC_FILECAP: i64 = 2097152
23const DC_FNV_OFF: i64 = 1469598103934665603
24const DC_FNV_PRIME: i64 = 1099511628211
25
26func dc_isid(c: i64) -> i64 {
27 if c >= 97 { if c <= 122 { return 1 } }
28 if c >= 65 { if c <= 90 { return 1 } }
29 if c >= 48 { if c <= 57 { return 1 } }
30 if c == 95 { return 1 }
31 if c == 46 { return 1 }
32 return 0
33}
34func dc_ends_nx(nm: *u8) -> i64 {
35 var n: i64 = 0
36 while nm[n] != (0 as u8) { n = n + 1 }
37 if n < 3 { return 0 }
38 if nm[n-3] == (46 as u8) { if nm[n-2] == (110 as u8) { if nm[n-1] == (120 as u8) { return 1 } } }
39 return 0
40}
41func dc_fnv(buf: *u8, s: i64, e: i64) -> i64 {
42 var h: i64 = DC_FNV_OFF
43 var i: i64 = s
44 while i < e { h = h ^ (buf[i] as i64); h = h * DC_FNV_PRIME; i = i + 1 }
45 return h
46}
47
48// globals for the node map (module statics defined ABOVE use per fwd-const law)
49static g_nbuf: *u8
50static g_noff: *i64
51static g_nlen: *i64
52static g_bhead: *i64
53static g_bnext: *i64
54static g_nb: *i64
55static g_nn: *i64
56
57func dc_streq(buf: *u8, s: i64, l: i64, name: *u8, no: i64, nl: i64) -> i64 {
58 if l != nl { return 0 }
59 var i: i64 = 0
60 while i < l { if buf[s+i] != name[no+i] { return 0 } i = i + 1 }
61 return 1
62}
63// find-or-insert basename[s..e) from src into the node map; returns node index
64func dc_intern(src: *u8, s: i64, e: i64) -> i64 {
65 let l: i64 = e - s
66 let h: i64 = dc_fnv(src, s, e)
67 let b: i64 = h & (DC_NB - 1)
68 var p: i64 = g_bhead[b]
69 while p >= 0 {
70 if dc_streq(g_nbuf, g_noff[p], g_nlen[p], src, s, l) == 1 { return p }
71 p = g_bnext[p]
72 }
73 // insert
74 let idx: i64 = g_nn[0]
75 if idx >= DC_MAXN { return 0 - 1 }
76 let no: i64 = g_nb[0]
77 if no + l + 1 >= DC_NAMEBUF { return 0 - 1 }
78 g_noff[idx] = no
79 g_nlen[idx] = l
80 var w: i64 = 0
81 while w < l { g_nbuf[no + w] = src[s + w]; w = w + 1 }
82 g_nb[0] = no + l
83 g_bnext[idx] = g_bhead[b]
84 g_bhead[b] = idx
85 g_nn[0] = idx + 1
86 return idx
87}
88// find only (no insert); -1 if absent
89func dc_find(src: *u8, s: i64, e: i64) -> i64 {
90 let l: i64 = e - s
91 let h: i64 = dc_fnv(src, s, e)
92 let b: i64 = h & (DC_NB - 1)
93 var p: i64 = g_bhead[b]
94 while p >= 0 {
95 if dc_streq(g_nbuf, g_noff[p], g_nlen[p], src, s, l) == 1 { return p }
96 p = g_bnext[p]
97 }
98 return 0 - 1
99}
100
101func main(argc: i64, argv: *i64) -> i64 {
102 if argc < 2 { sd_w("usage: nx_depcycle <dir> [dir2...]\n" as *u8); sys_exit(2); return 2 }
103 g_nbuf = sys_mmap(DC_NAMEBUF)
104 g_noff = sys_mmap(8 * DC_MAXN) as *i64
105 g_nlen = sys_mmap(8 * DC_MAXN) as *i64
106 g_bhead = sys_mmap(8 * DC_NB) as *i64
107 g_bnext = sys_mmap(8 * DC_MAXN) as *i64
108 g_nb = sys_mmap(16) as *i64
109 g_nn = sys_mmap(16) as *i64
110 g_nb[0] = 0
111 g_nn[0] = 0
112 var bi: i64 = 0
113 while bi < DC_NB { g_bhead[bi] = 0 - 1; bi = bi + 1 }
114
115 // edges: src->dst arrays + per-node out-adjacency linked list + in-degree
116 let esrc: *i64 = sys_mmap(8 * DC_MAXE) as *i64
117 let edst: *i64 = sys_mmap(8 * DC_MAXE) as *i64
118 var ne: i64 = 0
119 let src: *u8 = sys_mmap(DC_FILECAP)
120 let dbuf: *u8 = sys_mmap(DC_MAGIC_262144)
121
122 // PASS 1: register all .nx basenames as nodes
123 var d: i64 = 1
124 while d < argc {
125 let dir: *u8 = argv[d] as *u8
126 let dfd: i64 = sys_openat_rd(dir)
127 if dfd >= 0 {
128 var go: i64 = 1
129 while go == 1 {
130 let dn: i64 = sys_getdents64(dfd, dbuf, DC_MAGIC_262144)
131 if dn <= 0 { go = 0 } else {
132 var off: i64 = 0
133 while off < dn {
134 let rec: *u8 = ((dbuf as i64) + off) as *u8
135 let ty: i64 = dirent_type(rec)
136 let nm: *u8 = dirent_name(rec)
137 if ty != 4 { if dc_ends_nx(nm) == 1 {
138 var l: i64 = 0
139 while nm[l] != (0 as u8) { l = l + 1 }
140 dc_intern(nm, 0, l)
141 } }
142 off = off + dirent_reclen(rec)
143 }
144 }
145 }
146 sys_close(dfd)
147 }
148 d = d + 1
149 }
150
151 // PASS 2: for each file, parse import "Y.nx" -> edge (thisnode -> Ynode)
152 d = 1
153 while d < argc {
154 let dir: *u8 = argv[d] as *u8
155 let dfd: i64 = sys_openat_rd(dir)
156 if dfd >= 0 {
157 var go: i64 = 1
158 while go == 1 {
159 let dn: i64 = sys_getdents64(dfd, dbuf, DC_MAGIC_262144)
160 if dn <= 0 { go = 0 } else {
161 var off: i64 = 0
162 while off < dn {
163 let rec: *u8 = ((dbuf as i64) + off) as *u8
164 let ty: i64 = dirent_type(rec)
165 let nm: *u8 = dirent_name(rec)
166 if ty != 4 { if dc_ends_nx(nm) == 1 {
167 var nl: i64 = 0
168 while nm[nl] != (0 as u8) { nl = nl + 1 }
169 let selfnode: i64 = dc_find(nm, 0, nl)
170 // read file, scan for import "..."
171 let p: *u8 = sys_mmap(DC_MAGIC_1024)
172 var po: i64 = 0
173 var dj: i64 = 0
174 while dir[dj] != (0 as u8) { p[po] = dir[dj]; po = po + 1; dj = dj + 1 }
175 p[po] = 47 as u8; po = po + 1
176 var mj: i64 = 0
177 while nm[mj] != (0 as u8) { p[po] = nm[mj]; po = po + 1; mj = mj + 1 }
178 p[po] = 0 as u8
179 let fd: i64 = sys_openat_rd(p)
180 if fd >= 0 {
181 var slen: i64 = 0
182 var r: i64 = sys_read(fd, src, DC_FILECAP - 1)
183 while r > 0 { slen = slen + r; if slen >= DC_FILECAP - 1 { r = 0 } else { r = sys_read(fd, src + slen, DC_FILECAP - 1 - slen) } }
184 sys_close(fd)
185 // scan for import "NAME" at line starts -> edge selfnode -> Ynode
186 var k: i64 = 0
187 while k + 8 < slen {
188 var atbol: i64 = 0
189 if k == 0 { atbol = 1 }
190 if k > 0 { if src[k-1] == (10 as u8) { atbol = 1 } }
191 if atbol == 1 {
192 var mm: i64 = 1
193 if src[k] != (105 as u8) { mm = 0 }
194 if mm == 1 { if src[k+1] != (109 as u8) { mm = 0 } }
195 if mm == 1 { if src[k+2] != (112 as u8) { mm = 0 } }
196 if mm == 1 { if src[k+3] != (111 as u8) { mm = 0 } }
197 if mm == 1 { if src[k+4] != (114 as u8) { mm = 0 } }
198 if mm == 1 { if src[k+5] != (116 as u8) { mm = 0 } }
199 if mm == 1 { if src[k+6] != (32 as u8) { mm = 0 } }
200 if mm == 1 { if src[k+7] != (34 as u8) { mm = 0 } }
201 if mm == 1 {
202 let ys: i64 = k + 8
203 var ye: i64 = ys
204 var s3: i64 = 1
205 while s3 == 1 { if ye >= slen { s3 = 0 } else { if src[ye] == (34 as u8) { s3 = 0 } else { ye = ye + 1 } } }
206 if ye > ys {
207 let dstnode: i64 = dc_find(src, ys, ye)
208 if dstnode >= 0 { if selfnode >= 0 { if selfnode != dstnode { if ne < DC_MAXE {
209 esrc[ne] = selfnode
210 edst[ne] = dstnode
211 ne = ne + 1
212 } } } }
213 }
214 }
215 }
216 k = k + 1
217 }
218 }
219 } }
220 off = off + dirent_reclen(rec)
221 }
222 }
223 }
224 sys_close(dfd)
225 }
226 d = d + 1
227 }
228
229 let nn: i64 = g_nn[0]
230 // in-degree
231 let indeg: *i64 = sys_mmap(8 * DC_MAXN) as *i64
232 var z: i64 = 0
233 while z < nn { indeg[z] = 0; z = z + 1 }
234 var ei: i64 = 0
235 while ei < ne { indeg[edst[ei]] = indeg[edst[ei]] + 1; ei = ei + 1 }
236 // out-adjacency linked list
237 let ohead: *i64 = sys_mmap(8 * DC_MAXN) as *i64
238 let onext: *i64 = sys_mmap(8 * DC_MAXE) as *i64
239 z = 0
240 while z < nn { ohead[z] = 0 - 1; z = z + 1 }
241 ei = 0
242 while ei < ne { onext[ei] = ohead[esrc[ei]]; ohead[esrc[ei]] = ei; ei = ei + 1 }
243 // KAHN: queue zero-indeg, process, decrement dst indeg
244 let queue: *i64 = sys_mmap(8 * DC_MAXN) as *i64
245 var qh: i64 = 0
246 var qt: i64 = 0
247 z = 0
248 while z < nn { if indeg[z] == 0 { queue[qt] = z; qt = qt + 1 } z = z + 1 }
249 var processed: i64 = 0
250 while qh < qt {
251 let u: i64 = queue[qh]
252 qh = qh + 1
253 processed = processed + 1
254 var e2: i64 = ohead[u]
255 while e2 >= 0 {
256 let v: i64 = edst[e2]
257 indeg[v] = indeg[v] - 1
258 if indeg[v] == 0 { queue[qt] = v; qt = qt + 1 }
259 e2 = onext[e2]
260 }
261 }
262 let cyclic: i64 = nn - processed
263
264 // report cyclic nodes (indeg>0 still)
265 z = 0
266 var shown: i64 = 0
267 while z < nn {
268 if indeg[z] > 0 {
269 if shown < 200 {
270 sd_w("CYCLENODE " as *u8)
271 sys_write(1, ((g_nbuf as i64) + g_noff[z]) as *u8, g_nlen[z])
272 sd_w("\n" as *u8)
273 }
274 shown = shown + 1
275 }
276 z = z + 1
277 }
278
279 sd_w("NX-DEPCYCLE nodes=" as *u8)
280 let ob: *u8 = sys_mmap(64)
281 sd_num(ob, 0, nn)
282 sd_w(ob)
283 sd_w(" edges=" as *u8)
284 sd_num(ob, 0, ne)
285 sd_w(ob)
286 sd_w(" cyclic=" as *u8)
287 sd_num(ob, 0, cyclic)
288 sd_w(ob)
289 sd_w(" verdict=" as *u8)
290 if cyclic == 0 { sd_w("ACYCLIC\n" as *u8) } else {
291 sd_w("CYCLIC-" as *u8)
292 sd_num(ob, 0, cyclic)
293 sd_w(ob)
294 sd_w("\n" as *u8)
295 }
296 sys_exit(0)
297 return 0
298}