// flow_debugger.hako — Mini‑VM JSON v0 デバッグ用の軽量箱 // 責務: // - JSON v0 の関数/ブロック/命令を静的に走査し、 // - ブロックID集合の抽出 // - branch/jump の then/else/target が妥当なIDか検証 // - op シーケンスの要約出力(最初の N 件) // 非責務: // - 実行・評価(それは MirVmMin に委譲) using selfhost.shared.common.string_helpers as StringHelpers static box FlowDebugBox { // ユーティリティ — 文字列検索 _index_of_from(hay, needle, pos) { if pos < 0 { pos = 0 } local n = hay.length() if pos > n { return -1 } local i = pos local m = needle.length() if m <= 0 { return pos } local limit = n - m loop(i <= limit) { if hay.substring(i, i+m) == needle { return i } i = i + 1 } return -1 } _read_digits(text, pos) { return StringHelpers.read_digits(text, pos) } _int_to_str(n) { return StringHelpers.int_to_str(n) } // ブロックID集合を抽出 collect_block_ids(mjson) { local ids = new ArrayBox() local pos = 0 loop(true) { local p = me._index_of_from(mjson, "\"id\":", pos) if p < 0 { break } local d = me._read_digits(mjson, p + 5) if d != "" { ids.push(d) } pos = p + 5 } return ids } // op シーケンスを先頭から limit 件だけ抽出 collect_ops(mjson, limit) { if limit == null { limit = 50 } local ops = new ArrayBox() local pos = 0 loop(ops.length() < limit) { local p = me._index_of_from(mjson, "\"op\":\"", pos) if p < 0 { break } local q = me._index_of_from(mjson, "\"", p + 6) if q < 0 { break } local op = mjson.substring(p + 6, q) ops.push(op) pos = q + 1 } return ops } // branch/jump の then/else/target を抽出し、集合 membership を検査 validate_cf_targets(mjson) { local ids = me.collect_block_ids(mjson) // Set 風マップ化 local idset = {} local i = 0 loop(i < ids.length()) { idset.set(ids.get(i), 1) i = i + 1 } local errs = new ArrayBox() local pos = 0 loop(true) { local p = me._index_of_from(mjson, "\"op\":\"branch\"", pos) if p < 0 { break } // then local pt = me._index_of_from(mjson, "\"then\":", p) local pe = me._index_of_from(mjson, "\"else\":", p) if pt >= 0 { local t = me._read_digits(mjson, pt + 7) if t != "" && idset.get(t) == null { errs.push("branch.then invalid:" + t) } } if pe >= 0 { local e = me._read_digits(mjson, pe + 7) if e != "" && idset.get(e) == null { errs.push("branch.else invalid:" + e) } } pos = p + 14 } // jump pos = 0 loop(true) { local p = me._index_of_from(mjson, "\"op\":\"jump\"", pos) if p < 0 { break } local pt = me._index_of_from(mjson, "\"target\":", p) if pt >= 0 { local t = me._read_digits(mjson, pt + 9) if t != "" && idset.get(t) == null { errs.push("jump.target invalid:" + t) } } pos = p + 12 } // レポート if errs.length() == 0 { print("{\"kind\":\"flow_debug\",\"ok\":true,\"blocks\":" + (""+ids.length()) + "}") } else { local k = 0 loop(k < errs.length()) { print("{\"kind\":\"flow_debug\",\"ok\":false,\"msg\":\"" + errs.get(k) + "\"}") k = k + 1 } } return errs.length() } // 要約: 先頭の op を列挙 summarize_ops(mjson, limit) { local ops = me.collect_ops(mjson, limit) local i = 0 loop(i < ops.length()) { print("{\"kind\":\"flow_ops\",\"op\":\"" + ops.get(i) + "\"}") i = i + 1 } return ops.length() } main(args) { return 0 } }