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
This commit is contained in:
165
docs/development/current/main/phase177-carrier-evolution.md
Normal file
165
docs/development/current/main/phase177-carrier-evolution.md
Normal file
@ -0,0 +1,165 @@
|
||||
# Phase 177: Carrier Evolution - min から Production へ
|
||||
|
||||
## 視覚的比較: ループ構造の進化
|
||||
|
||||
### Phase 174: min(1-carrier)
|
||||
```
|
||||
Input: "hello world""
|
||||
^start ^target
|
||||
|
||||
loop(pos < len) {
|
||||
ch = s[pos]
|
||||
if ch == '"' → break ✓
|
||||
else → pos++
|
||||
}
|
||||
|
||||
Carriers: 1
|
||||
- pos (ループ変数)
|
||||
|
||||
Pattern: 4 (Loop + If PHI)
|
||||
Exit: pos = 11
|
||||
```
|
||||
|
||||
### Phase 175/176: min2(2-carrier)
|
||||
```
|
||||
Input: "hello world""
|
||||
^start ^target
|
||||
|
||||
loop(pos < len) {
|
||||
ch = s[pos]
|
||||
if ch == '"' → break ✓
|
||||
else → result += ch
|
||||
pos++
|
||||
}
|
||||
|
||||
Carriers: 2
|
||||
- pos (ループ変数)
|
||||
- result (バッファ)
|
||||
|
||||
Pattern: 4 (Loop + If PHI)
|
||||
Exit: pos = 11, result = "hello world"
|
||||
```
|
||||
|
||||
### Phase 177-A: Simple Case(2-carrier, Production-like)
|
||||
```
|
||||
Input: "hello world""
|
||||
^start ^target
|
||||
|
||||
loop(p < len) {
|
||||
ch = s[p]
|
||||
if ch == '"' → break ✓
|
||||
else → str += ch
|
||||
p++
|
||||
}
|
||||
|
||||
Carriers: 2
|
||||
- p (ループ変数)
|
||||
- str (バッファ)
|
||||
|
||||
Pattern: 4 (Loop + If PHI)
|
||||
Exit: p = 11, str = "hello world"
|
||||
```
|
||||
|
||||
### Phase 178: Escape Handling(2-carrier + continue)
|
||||
```
|
||||
Input: "hello \"world\"""
|
||||
^start ^escape ^target
|
||||
|
||||
loop(p < len) {
|
||||
ch = s[p]
|
||||
if ch == '"' → break ✓
|
||||
if ch == '\\' → str += ch
|
||||
p++
|
||||
str += s[p]
|
||||
p++
|
||||
continue ←← 新要素
|
||||
else → str += ch
|
||||
p++
|
||||
}
|
||||
|
||||
Carriers: 2 (変わらず)
|
||||
- p (ループ変数)
|
||||
- str (バッファ)
|
||||
|
||||
Pattern: 4? (continue 対応は未検証)
|
||||
Exit: p = 15, str = "hello \"world\""
|
||||
```
|
||||
|
||||
### Phase 179: Full Production(2-carrier + early return)
|
||||
```
|
||||
Input: "hello \"world\"""
|
||||
^start ^escape ^target
|
||||
|
||||
loop(p < len) {
|
||||
ch = s[p]
|
||||
if ch == '"' → return MapBox {...} ✓ ←← early return
|
||||
if ch == '\\' → if p+1 >= len → return null ←← error
|
||||
str += ch
|
||||
p++
|
||||
str += s[p]
|
||||
p++
|
||||
continue
|
||||
else → str += ch
|
||||
p++
|
||||
}
|
||||
return null ←← ループ終端エラー
|
||||
|
||||
Carriers: 2 (変わらず)
|
||||
- p (ループ変数)
|
||||
- str (バッファ)
|
||||
|
||||
Pattern: 4? (early return 対応は未検証)
|
||||
Exit: 正常: MapBox, 異常: null
|
||||
```
|
||||
|
||||
## Carrier 安定性分析
|
||||
|
||||
### 重要な発見
|
||||
**Phase 174 → 179 を通じて Carrier 数は安定(1 → 2)**
|
||||
|
||||
| Phase | Carriers | 新要素 | P5昇格候補 |
|
||||
|-------|----------|--------|-----------|
|
||||
| 174 | 1 (pos) | - | なし |
|
||||
| 175/176 | 2 (pos + result) | バッファ追加 | なし |
|
||||
| 177-A | 2 (p + str) | - | なし |
|
||||
| 178 | 2 (p + str) | continue | なし(確認中) |
|
||||
| 179 | 2 (p + str) | early return | なし(確認中) |
|
||||
|
||||
### P5昇格候補の不在
|
||||
**Trim と異なり、`is_ch_match` 相当は不要**
|
||||
|
||||
理由:
|
||||
- Trim: `is_ch_match` が「次も空白か?」を決定(**次の判断に影響**)
|
||||
- _parse_string: `ch == '"'` は「今終了か?」のみ(**次に影響しない**)
|
||||
|
||||
```
|
||||
Trim の制御フロー:
|
||||
is_ch_match = (ch == ' ')
|
||||
if is_ch_match → pos++ → 次も is_ch_match を評価 ←← 連鎖
|
||||
|
||||
_parse_string の制御フロー:
|
||||
if ch == '"' → break → 終了
|
||||
else → str += ch, p++ → 次は独立判断 ←← 連鎖なし
|
||||
```
|
||||
|
||||
## JoinIR Pattern 対応予測
|
||||
|
||||
| Phase | Pattern 候補 | 理由 |
|
||||
|-------|-------------|------|
|
||||
| 177-A | Pattern4 | Loop + If PHI + break(実装済み) |
|
||||
| 178 | Pattern4? | continue は Pattern4-with-continue(要実装確認) |
|
||||
| 179 | Pattern5? | early return は新パターン候補(要設計) |
|
||||
|
||||
## まとめ
|
||||
|
||||
### 段階的検証戦略
|
||||
1. **Phase 177-A**: min2 と同型 → P5 安定性確認
|
||||
2. **Phase 178**: continue 追加 → JoinIR 拡張必要性評価
|
||||
3. **Phase 179**: early return 追加 → Pattern5 設計判断
|
||||
|
||||
### Carrier 設計の教訓
|
||||
- **最小構成で開始**: 1-carrier (Phase 174)
|
||||
- **段階的拡張**: 2-carrier (Phase 175/176)
|
||||
- **Production 適用**: 構造は変えず、制御フローのみ追加(Phase 177+)
|
||||
|
||||
→ **「Carrier 数を固定して制御フローを段階的に複雑化」が正解**
|
||||
178
docs/development/current/main/phase177-parse-string-design.md
Normal file
178
docs/development/current/main/phase177-parse-string-design.md
Normal file
@ -0,0 +1,178 @@
|
||||
# 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 merge(Phase 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」の動作確認を優先する。
|
||||
Reference in New Issue
Block a user