114 lines
3.6 KiB
Plaintext
114 lines
3.6 KiB
Plaintext
|
|
// 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 } }
|