// tools/hako_check/render/graphviz.hako — GraphvizRenderBox (MVP) // Render minimal DOT graph from one or more Analysis IRs. using selfhost.shared.common.string_helpers as Str static box GraphvizRenderBox { render_multi(irs) { // irs: ArrayBox of IR Map print("digraph Hako {") // optional graph attributes (kept minimal) // print(" rankdir=LR;") // Node and edge sets to avoid duplicates local nodes = new MapBox() local edges = new MapBox() if irs != null { local gi = 0 while gi < irs.size() { local ir = irs.get(gi) me._render_ir(ir, nodes, edges) gi = gi + 1 } } // Emit nodes local itn = nodes // Map iteration: keys() not available → store labels as keys in map // Use a synthetic loop by scanning a known list captured during _render_ir // For MVP, nodes map has key=name, value=1 // We cannot iterate map keys deterministically; accept arbitrary order. // Re-emitting by re-collecting from edges as well (ensures endpoints appear). // Emit edges if edges != null { // edges map key = from + "\t" + to // naive iteration by trying to get keys from a stored list // We kept an ArrayBox under edges.get("__keys__") for listing local ks = edges.get("__keys__") if ks != null { local ei = 0 while ei < ks.size() { local key = ks.get(ei) local tab = key.indexOf("\t") if tab > 0 { local src = key.substring(0, tab) local dst = key.substring(tab+1) print(" \"" + src + "\" -> \"" + dst + "\";") // also register nodes (in case they weren’t explicitly collected) nodes.set(src, 1) nodes.set(dst, 1) } ei = ei + 1 } } } // Now emit nodes at the end for any isolated methods // Rebuild a list of node keys from a synthetic array stored under nodes.get("__keys__") local nk = nodes.get("__keys__") if nk != null { local ni = 0 while ni < nk.size() { local name = nk.get(ni) print(" \"" + name + "\";") ni = ni + 1 } } print("}") return 0 } _render_ir(ir, nodes, edges) { if ir == null { return } // methods local ms = ir.get("methods") if ms != null { local mi = 0 while mi < ms.size() { me._add_node(nodes, ms.get(mi)) mi = mi + 1 } } // calls local cs = ir.get("calls") if cs != null { local ci = 0 while ci < cs.size() { local c = cs.get(ci) local f = c.get("from") local t = c.get("to") me._add_edge(edges, f, t) ci = ci + 1 } } } _add_node(nodes, name) { if name == null { return } nodes.set(name, 1) // also store a list of keys for emitting (since Map has no key iterator) local arr = nodes.get("__keys__"); if arr == null { arr = new ArrayBox(); nodes.set("__keys__", arr) } // avoid duplicates local seen = 0 local i = 0; while i < arr.size() { if arr.get(i) == name { seen = 1; break } i = i + 1 } if seen == 0 { arr.push(name) } } _add_edge(edges, src, dst) { if src == null || dst == null { return } local key = src + "\t" + dst if edges.get(key) == null { edges.set(key, 1) } local arr = edges.get("__keys__"); if arr == null { arr = new ArrayBox(); edges.set("__keys__", arr) } // avoid duplicates local seen = 0 local i = 0; while i < arr.size() { if arr.get(i) == key { seen = 1; break } i = i + 1 } if seen == 0 { arr.push(key) } } } static box GraphvizRenderMain { method main(args) { return 0 } }