Root cause: JoinIR ValueId collision between function parameters and condition bindings - Same ValueId used for both `result_init` (carrier param) and `limit` (condition var) - Phase 33-21 was overwriting condition bindings when remapping carrier PHIs Fix implemented (Option B - immediate protection): 1. Phase 177-3: Protect condition-only variables from Phase 33-21 override - Collect condition_bindings that are NOT carriers (by checking exit_bindings) - Skip remapping for these protected ValueIds 2. Phase 177-3-B: Handle body-only carriers explicitly - Carriers that appear in condition_bindings (added by Phase 176-5) - Map them to correct PHI dsts by name lookup Investigation tools added: - [DEBUG-177] trace logs for remapper state tracking - Phase 177-3 protection logging - BoundaryInjector PHI collision detection Test results: - ✅ Integer multi-carrier test: Output 3 (expected) - ⚠️ String test: RC=0 but empty output (separate issue - string concat emit) Design docs created: - phase177-parse-string-design.md: _parse_string loop analysis - phase177-carrier-evolution.md: Carrier progression Phase 174-179 Next: Investigate string concatenation emit for full _parse_string support
6.1 KiB
6.1 KiB
Phase 177: _parse_string 本体 JoinIR 適用設計
目的
Production JsonParserBox._parse_string メソッドに JoinIR P5 パイプラインを適用する。
Phase 174/175/176 で確立した「1-carrier → 2-carrier」検証の成果を、実際の JSON パーサーに展開する。
本番ループ構造(lines 144-181)
前提条件
- 開始位置
posは"を指している(line 145 でチェック) - ループ開始時
p = pos + 1(引用符の次の文字から開始)
ループ本体(lines 150-178)
loop(p < s.length()) {
local ch = s.substring(p, p+1)
if ch == '"' {
// End of string (lines 153-160)
local result = new MapBox()
result.set("value", me._unescape_string(str))
result.set("pos", p + 1)
result.set("type", "string")
return result // ← Early return (通常の exit)
}
if ch == "\\" {
// Escape sequence (lines 162-174)
local has_next = 0
if p + 1 < s.length() { has_next = 1 }
if has_next == 0 { return null } // ← Early return (エラー)
str = str + ch
p = p + 1
str = str + s.substring(p, p+1)
p = p + 1
continue // ← ループ継続
}
str = str + ch // 通常文字の追加
p = p + 1
}
return null // ← ループ終端に到達(エラー)
制御フロー分岐
- 終端クォート検出 (
ch == '"'): 成功 return - エスケープ検出 (
ch == "\\") + continue: 2文字消費してループ継続 - 通常文字: バッファ蓄積 + 位置進行
- ループ終端: 引用符が閉じていない(エラー)
Carriers 分析
| 変数 | 初期値 | 役割 | 更新式 | P5 昇格対象? | 備考 |
|---|---|---|---|---|---|
p |
pos + 1 |
カウンタ(位置) | p = p + 1 |
N(ループ変数) | 条件式に使用 |
str |
"" |
バッファ(蓄積) | str = str + ch |
N(通常キャリア) | エスケープ時2回更新 |
has_next |
- | 一時変数(フラグ) | - | - | ループ内のみ有効 |
is_escape |
- | (潜在的フラグ) | - | - | 明示的変数なし |
重要な発見
- エスケープ処理は continue 経由:
pとstrを 2 回更新してから continue - Early return が 2 箇所: 成功 return (line 159) とエラー return (line 167)
- 通常キャリアのみ: P5 昇格対象(
is_ch_match相当)は不要
min ケースとの差分
共通点
| 項目 | min (Phase 174) | min2 (Phase 175) | 本番 (Phase 177) |
|---|---|---|---|
| ループ変数 | pos |
pos |
p |
| バッファ | なし | result |
str |
| 終端条件 | ch == '"' → break |
ch == '"' → break |
ch == '"' → return |
| 通常処理 | pos++ |
result += ch; pos++ |
str += ch; p++ |
差分
| 項目 | min/min2 | 本番 |
|---|---|---|
| 終端処理 | break のみ |
Early return (MapBox 返却) |
| エスケープ処理 | なし | continue を使った複雑な分岐 |
| エラー処理 | なし | ループ内・外に return null |
方針決定: 段階的アプローチ
Phase 177-A: Simple Case(今回)
対象: エスケープなし・終端クォート検出のみ
- ループ構造: min2 と完全同型(
p+strの 2-carrier) - 終端処理:
break→ 直後に MapBox 構築(return 代替) - 目的: P5 パイプラインが「2-carrier + break」で動作することを確認
// Simplified for Phase 177-A
loop(p < s.length()) {
local ch = s.substring(p, p+1)
if ch == '"' {
break // P5: Pattern4 対応(Loop+If PHI merge)
} else {
str = str + ch
p = p + 1
}
}
// break 後: MapBox 構築
Phase 178: Escape Handling(次回)
追加要素:
continue分岐(エスケープ処理)- Early return(エラーハンドリング)
- P5 昇格キャリア候補の検討(
is_escapeフラグ?)
Phase 179: Full Production(最終)
統合:
_unescape_string()呼び出し- 完全なエラーハンドリング
- 本番同等の MapBox 返却
Phase 177-A 実装計画
Test Case: test_jsonparser_parse_string_simple.hako
// Phase 177-A: Production-like simple case
static box JsonParserStringTest3 {
parse_string_simple() {
local s = "hello world\""
local p = 0 // pos + 1 相当(簡略化のため 0 から開始)
local str = ""
local len = s.length()
// 2-carrier loop (min2 と同型)
loop(p < len) {
local ch = s.substring(p, p+1)
if ch == "\"" {
break
} else {
str = str + ch
p = p + 1
}
}
// Post-loop: 結果出力(MapBox 構築の代替)
print("Parsed string: ")
print(str)
print(", final pos: ")
print(p)
}
main() {
me.parse_string_simple()
return "OK"
}
}
期待される MIR 構造
- Pattern4 検出: Loop + If PHI merge(Phase 170 実装済み)
- 2 carriers:
p(ループ変数) +str(バッファ) - Exit PHI: ループ後の
pとstrが正しく伝播
検証項目
- ✅ P5 パイプライン通過(JoinIR → MIR)
- ✅ 2-carrier の正しい伝播(
pとstr) - ✅
break後の変数値が正しく使用可能
成功基準
test_jsonparser_parse_string_simple.hakoが実行成功- MIR ダンプで Pattern4 検出確認
- 出力:
Parsed string: hello world, final pos: 11
次のステップ(Phase 178)
continue分岐の追加(エスケープ処理)- P5 昇格キャリア候補の検討(必要性を再評価)
- Early return 対応(JoinIR での処理検討)
まとめ
Phase 177-A では、min2 と同型の Simple Case を Production 環境に適用する。 エスケープ処理は Phase 178 以降に回し、まず「2-carrier + break」の動作確認を優先する。