Files
hakorune/docs/development/current/main/phase177-parse-string-design.md
nyash-codex 7a01ffe522 fix(joinir): Phase 177-3 ValueId collision fix for multi-carrier loops
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
2025-12-08 16:34:04 +09:00

6.1 KiB
Raw Blame History

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  // ← ループ終端に到達(エラー)

制御フロー分岐

  1. 終端クォート検出 (ch == '"'): 成功 return
  2. エスケープ検出 (ch == "\\") + continue: 2文字消費してループ継続
  3. 通常文字: バッファ蓄積 + 位置進行
  4. ループ終端: 引用符が閉じていない(エラー)

Carriers 分析

変数 初期値 役割 更新式 P5 昇格対象? 備考
p pos + 1 カウンタ(位置) p = p + 1 Nループ変数 条件式に使用
str "" バッファ(蓄積) str = str + ch N通常キャリア エスケープ時2回更新
has_next - 一時変数(フラグ) - - ループ内のみ有効
is_escape - (潜在的フラグ) - - 明示的変数なし

重要な発見

  • エスケープ処理は continue 経由: pstr を 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 mergePhase 170 実装済み)
  • 2 carriers: p (ループ変数) + str (バッファ)
  • Exit PHI: ループ後の pstr が正しく伝播

検証項目

  1. P5 パイプライン通過JoinIR → MIR
  2. 2-carrier の正しい伝播(pstr
  3. 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」の動作確認を優先する。