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}