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

179 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
```hako
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 経由**: `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」で動作することを確認
```hako
// 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`
```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**: ループ後の `p``str` が正しく伝播
### 検証項目
1. ✅ P5 パイプライン通過JoinIR → MIR
2. ✅ 2-carrier の正しい伝播(`p``str`
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」の動作確認を優先する。