feat(hako_check): Phase 171-2 JsonParserBox integration (37.6% code reduction)

🎉 手書き JSON パーサ大幅削減成功!

🔧 実装内容:
- analysis_consumer.hako: 手書き JSON パーサ削除(266行、37.6%削減)
  - 708行 → 442行
  - 9つの解析メソッド削除
- JsonParserBox 統合(15行実装、95%削減達成)
- using 文追加: tools.hako_shared.json_parser

⚠️ 発見された重大な問題:
- using statement が静的 Box のメソッドを正しく解決できない
- 症状: Unknown method '_skip_whitespace' on InstanceBox
- 根本原因: 静的 Box の using サポートが Phase 15.5+ で必要

📊 現在の状態:
-  コード統合完了(37.6%削減)
-  using 文追加完了
-  コンパイル成功
- ⚠️ 実行時エラー(using 制限のため)

🎯 次のステップ:
- Phase 173 で using system 修正
- 静的 Box のメソッド解決を修正
- JsonParserBox 統合を完全動作させる

📋 ドキュメント:
- 詳細な実装結果を phase171-2_hako_check_integration.md に記録
- using 制限の発見と対応方針を明記
- Option A (推奨): Phase 173 で using system 修正を待つ
- Option B (代替): 一時的にインライン化

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-04 17:03:14 +09:00
parent b8118f36ec
commit 140a3d5715
2 changed files with 459 additions and 276 deletions

View File

@ -11,6 +11,7 @@
using selfhost.shared.common.string_helpers as Str
using tools.hako_parser.parser_core as HakoParserCoreBox
using tools.hako_shared.json_parser as JsonParserBox
static box HakoAnalysisBuilderBox {
build_from_source(text, path) { return me.build_from_source_flags(text, path, 0) }
@ -202,290 +203,23 @@ static box HakoAnalysisBuilderBox {
return ir
}
// Phase 156: Extract CFG from MIR JSON text
// Phase 171-2: Extract CFG from MIR JSON text using JsonParserBox
_extract_cfg_from_mir_json(json_text) {
if json_text == null { return me._empty_cfg() }
// Simple JSON parsing for CFG structure
// Expected format: {"cfg": {"functions": [...]}}
local cfg_start = json_text.indexOf('"cfg"')
if cfg_start < 0 { return me._empty_cfg() }
// Use JsonParserBox to parse the MIR JSON
local root = JsonParserBox.parse(json_text)
if root == null { return me._empty_cfg() }
// Find the cfg object boundaries
local cfg_obj_start = json_text.indexOf('{', cfg_start)
if cfg_obj_start < 0 { return me._empty_cfg() }
// Extract functions array
local functions_key = json_text.indexOf('"functions"', cfg_obj_start)
if functions_key < 0 { return me._empty_cfg() }
local functions_arr_start = json_text.indexOf('[', functions_key)
if functions_arr_start < 0 { return me._empty_cfg() }
// Parse the functions array (simplified parsing)
local cfg = new MapBox()
local functions = me._parse_cfg_functions(json_text, functions_arr_start)
cfg.set("functions", functions)
// Extract cfg object: {"cfg": {"functions": [...]}}
local cfg = root.get("cfg")
if cfg == null { return me._empty_cfg() }
// Return the cfg object directly - it's already a MapBox with "functions" array
// The CFG structure is preserved from the MIR JSON
return cfg
}
_parse_cfg_functions(json_text, start_pos) {
// Parse the functions array from MIR CFG JSON
local functions = new ArrayBox()
// Find each function object in the array
local pos = start_pos + 1
local depth = 0
local in_func = 0
local func_start = -1
local i = pos
while i < json_text.length() {
local ch = json_text.substring(i, i+1)
if ch == '[' { i = i + 1; continue }
if ch == ']' { break }
if ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r' { i = i + 1; continue }
if ch == '{' {
if depth == 0 { func_start = i }
depth = depth + 1
i = i + 1
continue
}
if ch == '}' {
depth = depth - 1
if depth == 0 && func_start >= 0 {
// Extract this function object
local func_json = json_text.substring(func_start, i+1)
local func = me._parse_single_function(func_json)
if func != null { functions.push(func) }
func_start = -1
}
i = i + 1
continue
}
i = i + 1
}
return functions
}
_parse_single_function(func_json) {
// Parse a single function CFG object
local func = new MapBox()
// Extract name
local name = me._extract_json_string_value(func_json, "name")
if name == null { name = "unknown" }
func.set("name", name)
// Extract entry_block
local entry_block = me._extract_json_int_value(func_json, "entry_block")
func.set("entry_block", entry_block)
// Extract blocks array
local blocks_key = func_json.indexOf('"blocks"')
if blocks_key < 0 {
func.set("blocks", new ArrayBox())
return func
}
local blocks_arr_start = func_json.indexOf('[', blocks_key)
if blocks_arr_start < 0 {
func.set("blocks", new ArrayBox())
return func
}
local blocks = me._parse_blocks_array(func_json, blocks_arr_start)
func.set("blocks", blocks)
return func
}
_parse_blocks_array(json_text, start_pos) {
// Parse the blocks array
local blocks = new ArrayBox()
local pos = start_pos + 1
local depth = 0
local block_start = -1
local i = pos
while i < json_text.length() {
local ch = json_text.substring(i, i+1)
if ch == ']' && depth == 0 { break }
if ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r' || ch == ',' {
i = i + 1
continue
}
if ch == '{' {
if depth == 0 { block_start = i }
depth = depth + 1
i = i + 1
continue
}
if ch == '}' {
depth = depth - 1
if depth == 0 && block_start >= 0 {
local block_json = json_text.substring(block_start, i+1)
local block = me._parse_single_block(block_json)
if block != null { blocks.push(block) }
block_start = -1
}
i = i + 1
continue
}
i = i + 1
}
return blocks
}
_parse_single_block(block_json) {
// Parse a single block object
local block = new MapBox()
// Extract id
local id = me._extract_json_int_value(block_json, "id")
block.set("id", id)
// Extract reachable flag
local reachable = me._extract_json_bool_value(block_json, "reachable")
block.set("reachable", reachable)
// Extract terminator
local terminator = me._extract_json_string_value(block_json, "terminator")
if terminator == null { terminator = "Unknown" }
block.set("terminator", terminator)
// Extract successors array
local successors = me._extract_json_int_array(block_json, "successors")
block.set("successors", successors)
return block
}
_extract_json_string_value(json_text, key) {
// Extract a string value from JSON: "key": "value"
local key_pos = json_text.indexOf('"' + key + '"')
if key_pos < 0 { return null }
local colon_pos = json_text.indexOf(':', key_pos)
if colon_pos < 0 { return null }
local quote1 = json_text.indexOf('"', colon_pos + 1)
if quote1 < 0 { return null }
local quote2 = json_text.indexOf('"', quote1 + 1)
if quote2 < 0 { return null }
return json_text.substring(quote1 + 1, quote2)
}
_extract_json_int_value(json_text, key) {
// Extract an integer value from JSON: "key": 123
local key_pos = json_text.indexOf('"' + key + '"')
if key_pos < 0 { return 0 }
local colon_pos = json_text.indexOf(':', key_pos)
if colon_pos < 0 { return 0 }
// Skip whitespace after colon
local num_start = colon_pos + 1
while num_start < json_text.length() {
local ch = json_text.substring(num_start, num_start+1)
if ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r' { break }
num_start = num_start + 1
}
// Extract digits
local num_str = ""
local i = num_start
while i < json_text.length() {
local ch = json_text.substring(i, i+1)
if ch >= '0' && ch <= '9' {
num_str = num_str + ch
i = i + 1
continue
}
break
}
if num_str == "" { return 0 }
return me._atoi(num_str)
}
_extract_json_bool_value(json_text, key) {
// Extract a boolean value from JSON: "key": true/false
local key_pos = json_text.indexOf('"' + key + '"')
if key_pos < 0 { return 0 }
local colon_pos = json_text.indexOf(':', key_pos)
if colon_pos < 0 { return 0 }
// Check for "true" or "false" after colon
local true_pos = json_text.indexOf('true', colon_pos)
local false_pos = json_text.indexOf('false', colon_pos)
// Find which comes first (and is close enough to colon)
if true_pos >= 0 && (true_pos - colon_pos) < 10 { return 1 }
if false_pos >= 0 && (false_pos - colon_pos) < 10 { return 0 }
return 0
}
_extract_json_int_array(json_text, key) {
// Extract an integer array from JSON: "key": [1, 2, 3]
local arr = new ArrayBox()
local key_pos = json_text.indexOf('"' + key + '"')
if key_pos < 0 { return arr }
local bracket_pos = json_text.indexOf('[', key_pos)
if bracket_pos < 0 { return arr }
local end_bracket = json_text.indexOf(']', bracket_pos)
if end_bracket < 0 { return arr }
// Extract the array content
local array_content = json_text.substring(bracket_pos + 1, end_bracket)
// Parse comma-separated integers
local current_num = ""
local i = 0
while i < array_content.length() {
local ch = array_content.substring(i, i+1)
if ch >= '0' && ch <= '9' {
current_num = current_num + ch
} else {
if ch == ',' || ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' {
if current_num != "" {
arr.push(me._atoi(current_num))
current_num = ""
}
}
}
i = i + 1
}
// Add last number if any
if current_num != "" {
arr.push(me._atoi(current_num))
}
return arr
}
_empty_cfg() {
local cfg = new MapBox()
local functions = new ArrayBox()