code wiki / _hdl_build / nx_eco_graph_mermaid.nx
nx_eco_graph_mermaid.nx source
↩ module page · 88 lines · 4284 B
1// nx_eco_graph_mermaid.nx -- emit a MERMAID diagram of any organ's lineage (roots to god) from the sovereign
2// eco-graph store. Mermaid `graph TD` text renders natively in mermaid-aware viewers (GitHub, docs, Claude
3// artifacts, the nishi diagram tools) with ZERO JS from us -- so it's a real, portable, renderable diagram,
4// not a hand-rolled SVG that may not render. Composes nx_eco_graph (eg_load/eg_find/eg_ancestors + CSR).
5// This is the MCP-exposable "diagram" capability: `nx_eco_graph_mermaid <store> <seed>` -> mermaid text.
6// license_tier: ORIGINAL expect_exit:0
7import "nx_syscalls.nx"
8import "nx_eco_graph.nx"
9
10const MM_MAXS: i64 = 400
11
12func mw(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 }
13func mn(fd: i64, v: i64) -> i64 { let b:*u8=sys_mmap(24); var m:i64=v; if m<0{sys_write(fd,"-" as *u8,1);m=0-m} let t:*u8=sys_mmap(24); var k:i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var j:i64=0; while j<k{b[j]=t[k-1-j];j=j+1} sys_write(fd,b,k); return 0 }
14func mslen(s: *u8) -> i64 { var n:i64=0; while s[n]!=(0 as u8){n=n+1} return n }
15// write node name without ".nx" (mermaid label)
16func mwshort(fd: i64, g: *EcoGraph, idx: i64) -> i64 {
17 let off: i64 = g.node_off[idx]
18 var n: i64 = 0
19 while g.arena[off+n] != (0 as u8) { n = n + 1 }
20 var e: i64 = n
21 if n > 3 { if g.arena[off+n-3]==(46 as u8) { if g.arena[off+n-2]==(110 as u8) { if g.arena[off+n-1]==(120 as u8) { e = n - 3 } } } }
22 sys_write(fd, ((g.arena as i64)+off) as *u8, e)
23 return 0
24}
25
26func main(argc: i64, argv: *i64) -> i64 {
27 if argc < 2 { mw(1, "usage: nx_eco_graph_mermaid <store-prefix> <seed-basename> | nx_eco_graph_mermaid <seed> (MCP mode: pinned store)\n" as *u8); return 2 }
28 var storep: *u8 = "knowledge/store/ecograph_full\x00" as *u8 // MCP/API mode default (CWD=nishihost)
29 var seedname: *u8 = argv[1] as *u8
30 if argc >= 3 { storep = argv[1] as *u8; seedname = argv[2] as *u8 }
31 let g: *EcoGraph = eg_load(storep)
32 if (g as i64) == 0 { mw(1, "ERROR store not found\n" as *u8); return 3 }
33 let seed: i64 = eg_find(g, seedname, mslen(seedname))
34 if seed < 0 { mw(1, "ERROR seed not in graph\n" as *u8); return 4 }
35
36 // lineage subgraph S = seed + transitive imports
37 let visited: *u8 = sys_mmap(g.node_count + 2)
38 var vi: i64 = 0
39 while vi < g.node_count { visited[vi] = 0 as u8; vi = vi + 1 }
40 visited[seed] = 1 as u8
41 let anc: *i64 = sys_mmap((MM_MAXS+2)*8) as *i64
42 let ancn: *i64 = sys_mmap(16) as *i64
43 ancn[0] = 0
44 eg_ancestors(g, seed, visited, anc, ancn, MM_MAXS - 1)
45 let sidx: *i64 = sys_mmap((MM_MAXS+2)*8) as *i64
46 sidx[0] = seed
47 var sn: i64 = 1
48 var ai: i64 = 0
49 while ai < ancn[0] { sidx[sn] = anc[ai]; sn = sn + 1; ai = ai + 1 }
50 let g2l: *i64 = sys_mmap((g.node_count+2)*8) as *i64
51 vi = 0
52 while vi < g.node_count { g2l[vi] = 0 - 1; vi = vi + 1 }
53 var li: i64 = 0
54 while li < sn { g2l[sidx[li]] = li; li = li + 1 }
55
56 // emit mermaid graph TD (each node `nID["name"]`, each import edge `nA --> nB`)
57 mw(1, "graph TD\n" as *u8)
58 // node declarations
59 li = 0
60 while li < sn {
61 mw(1, " n" as *u8); mn(1, li); mw(1, "[\"" as *u8); mwshort(1, g, sidx[li]); mw(1, "\"]\n" as *u8)
62 li = li + 1
63 }
64 // edges: v --> its imports (toward god)
65 li = 0
66 while li < sn {
67 let v: i64 = sidx[li]
68 var p: i64 = g.out_head[v]; let e: i64 = g.out_head[v+1]
69 while p < e {
70 let dl: i64 = g2l[g.out_list[p]]
71 if dl >= 0 { mw(1, " n" as *u8); mn(1, li); mw(1, " --> n" as *u8); mn(1, dl); mw(1, "\n" as *u8) }
72 p = p + 1
73 }
74 li = li + 1
75 }
76 // style the seed + the roots (gen-0 leaves have no in-S imports)
77 mw(1, " style n0 fill:#c9a0ff,stroke:#333,color:#111\n" as *u8)
78 li = 0
79 while li < sn {
80 let v: i64 = sidx[li]
81 var p: i64 = g.out_head[v]; let e: i64 = g.out_head[v+1]
82 var hasimp: i64 = 0
83 while p < e { if g2l[g.out_list[p]] >= 0 { hasimp = 1 } p = p + 1 }
84 if hasimp == 0 { if li != 0 { mw(1, " style n" as *u8); mn(1, li); mw(1, " fill:#ffd479,stroke:#333,color:#111\n" as *u8) } }
85 li = li + 1
86 }
87 return 0
88}