Files
hakorune/tools/hako_check/render/graphviz.hako
nyash-codex fa3091061d trace: add execution route visibility + debug passthrough; phase2170 canaries; docs
- Add HAKO_TRACE_EXECUTION to trace executor route
  - Rust hv1_inline: stderr [trace] executor: hv1_inline (rust)
  - Hakovm dispatcher: stdout [trace] executor: hakovm (hako)
  - test_runner: trace lines for hv1_inline/core/hakovm routes
- Add HAKO_VERIFY_SHOW_LOGS and HAKO_DEBUG=1 (enables both)
  - verify_v1_inline_file() log passthrough with numeric rc extraction
  - test_runner exports via HAKO_DEBUG
- Canary expansion under phase2170 (state spec)
  - Array: push×5/10 → size, len/length alias, per‑recv/global, flow across blocks
  - Map: set dup-key non-increment, value_state get/has
  - run_all.sh: unify, remove SKIPs; all PASS
- Docs
  - ENV_VARS.md: add Debug/Tracing toggles and examples
  - PLAN.md/CURRENT_TASK.md: mark 21.7 green, add Quickstart lines

All changes gated by env vars; default behavior unchanged.
2025-11-08 23:45:29 +09:00

146 lines
4.7 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 {")
// Internal key names for node/edge key lists (avoid collisions with user names)
local NK_KEY = "__graphviz_nodes__"
local EK_KEY = "__graphviz_edges__"
// 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
}
}
// Build clusters by box: group nodes whose name looks like Box.method/arity
local groups = new MapBox()
local group_keys = new ArrayBox()
local nk = nodes.get(NK_KEY)
if nk == null { nk = new ArrayBox() }
if nk != null {
local i = 0
while i < nk.size() {
local name = nk.get(i)
local dot = name.indexOf(".")
if dot > 0 {
local box_name = name.substring(0, dot)
local gkey = "cluster_" + box_name
local arr = groups.get(gkey)
if arr == null { arr = new ArrayBox(); groups.set(gkey, arr); group_keys.push(gkey) }
// dedup in group
local seen = 0; local j=0; while j < arr.size() { if arr.get(j) == name { seen = 1; break } j = j + 1 }
if seen == 0 { arr.push(name) }
}
i = i + 1
}
}
// Emit clusters
local gi = 0
while gi < group_keys.size() {
local gk = group_keys.get(gi)
print(" subgraph \"" + gk + "\" {")
// label = box name (strip "cluster_")
local label = gk.substring("cluster_".length())
print(" label=\"" + label + "\";")
local arr = groups.get(gk)
local j = 0; while j < arr.size() { print(" \"" + arr.get(j) + "\";"); j = j + 1 }
print(" }")
gi = gi + 1
}
// Emit edges
// 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(EK_KEY)
if ks == null { ks = new ArrayBox() }
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
}
}
}
// Emit standalone nodes not covered by clusters
if nk != null {
local ni = 0
while ni < nk.size() {
local name = nk.get(ni)
local dot = name.indexOf(".")
if dot < 0 { 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("__graphviz_nodes__"); if arr == null { arr = new ArrayBox(); nodes.set("__graphviz_nodes__", 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("__graphviz_edges__"); if arr == null { arr = new ArrayBox(); edges.set("__graphviz_edges__", 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 } }