Files
hakorune/docs/development/current/main/phase173-jsonparser-loop-recheck.md
nyash-codex 290e97c54c feat(joinir): Phase 173 - JsonParser P5 expansion with _skip_whitespace
- Task 173-1: JsonParser loop inventory recheck
  - Observed 6 loops: _trim (2 loops, already working), _skip_whitespace,
    _parse_string, _parse_array, _unescape_string
  - Created comprehensive observation report with Trim similarity ratings
  - Discovered that JsonParser._trim already uses P5 pipeline successfully

- Task 173-2: Selected _skip_whitespace as Trim-equivalent pattern
  - Perfect structural match with Trim (100% identical)
  - Independent helper method, easy to test
  - Frequently used in JsonParser (7 call sites)

- Task 173-3: Design doc for P5 pipeline extension to JsonParser
  - Confirmed existing TrimLoopHelper works without modification
  - No charAt() support needed (_skip_whitespace uses substring())
  - Documented data flow and risk analysis

- Task 173-4: Successfully converted _skip_whitespace to JoinIR
  - Created test case: local_tests/test_jsonparser_skip_whitespace.hako
  - Added routing whitelist: JsonParserTest._skip_whitespace/3
  - Pattern detection SUCCESS:
    * LoopBodyLocal 'ch' detected
    * Carrier promotion to 'is_ch_match'
    * Trim pattern recognized
    * JoinIR generation successful
  - Verified P5 pipeline works for both static box methods and helper methods

- Task 173-5: Documentation updates
  - phase173-jsonparser-loop-recheck.md: Loop observation report
  - phase173-jsonparser-p5-design.md: P5 extension design
  - phase173-jsonparser-p5-impl.md: Implementation results
  - CURRENT_TASK.md: Phase 173 completion record

Key achievement: Proven that Trim P5 pipeline is fully generic -
works for both TrimTest (static box method) and JsonParser (helper method).
LoopBodyLocal carrier promotion is production-ready for Trim-like patterns.

Changes:
- src/mir/builder/control_flow/joinir/routing.rs: Add JsonParserTest whitelist
- docs/development/current/main/*173*.md: 3 new documentation files
- CURRENT_TASK.md: Phase 173 completion entry
2025-12-08 10:13:34 +09:00

384 lines
11 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 173-1: JsonParser ループ再観測
**Date**: 2025-12-08
**Purpose**: Trim P5 パイプラインを JsonParser に展開するための予備調査
---
## 観測対象ループ
### 1. _trim - Leading Whitespace
**File**: `tools/hako_shared/json_parser.hako`
**Lines**: 330-337
```hako
loop(start < end) {
local ch = s.substring(start, start+1)
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
start = start + 1
} else {
break
}
}
```
**Pattern 分類**: Pattern2 (break付き)
**LoopBodyLocal**: `ch` (substring定義、break条件で使用)
**Trim 類似度**: ★★★★★ (完全同型 - 既に実装済み)
**JoinIR 実行結果**: ✅ SUCCESS
```
[pattern2/check] Analyzing condition scope: 3 variables
[pattern2/check] 'ch': LoopBodyLocal
[pattern2/promoter] LoopBodyLocal 'ch' promoted to carrier 'is_ch_match'
[pattern2/trim] Safe Trim pattern detected, implementing lowering
[joinir/pattern2] Generated JoinIR for Loop with Break Pattern (Phase 170-B)
```
**状態**: ✅ Phase 171 で既に P5 パイプライン実装済み
---
### 2. _trim - Trailing Whitespace
**File**: `tools/hako_shared/json_parser.hako`
**Lines**: 340-347
```hako
loop(end > start) {
local ch = s.substring(end-1, end)
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
end = end - 1
} else {
break
}
}
```
**Pattern 分類**: Pattern2 (break付き)
**LoopBodyLocal**: `ch` (substring定義、break条件で使用)
**Trim 類似度**: ★★★★★ (完全同型 - 既に実装済み)
**JoinIR 実行結果**: ✅ SUCCESS
```
[pattern2/check] Analyzing condition scope: 3 variables
[pattern2/check] 'ch': LoopBodyLocal
[pattern2/promoter] LoopBodyLocal 'ch' promoted to carrier 'is_ch_match'
[pattern2/trim] Safe Trim pattern detected, implementing lowering
[joinir/pattern2] Generated JoinIR for Loop with Break Pattern (Phase 170-B)
```
**状態**: ✅ Phase 171 で既に P5 パイプライン実装済み
---
### 3. _skip_whitespace
**File**: `tools/hako_shared/json_parser.hako`
**Lines**: 310-321
```hako
_skip_whitespace(s, pos) {
local p = pos
loop(p < s.length()) {
local ch = s.substring(p, p+1)
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
p = p + 1
} else {
break
}
}
return p
}
```
**Pattern 分類**: Pattern2 (break付き)
**LoopBodyLocal**: `ch` (substring定義、break条件で使用)
**Trim 類似度**: ★★★★★ (Trim の leading whitespace と完全同型)
**構造的特徴**:
- `loop(p < s.length())` - 長さチェックTrim の `start < end` と同等)
- `local ch = s.substring(p, p+1)` - 1文字抽出完全一致
- `if ch == " " || ch == "\t" || ch == "\n" || ch == "\r"` - 空白判定(完全一致)
- `p = p + 1` vs `break` - 進行 vs 脱出(完全一致)
**JoinIR 実行可能性**: ✅ 既存の Trim パイプラインで即座に対応可能
- `TrimLoopHelper::is_safe_trim()` が true を返すはず
- `LoopBodyCarrierPromoter::try_promote()` で carrier 昇格成功するはず
**Phase 173-2 での選定**: ★★★★★ 第一候補Trim と完全同型)
---
### 4. _parse_string
**File**: `tools/hako_shared/json_parser.hako`
**Lines**: 150-178
```hako
loop(p < s.length()) {
local ch = s.substring(p, p+1)
if ch == '"' {
// End of string
local result = new MapBox()
result.set("value", me._unescape_string(str))
result.set("pos", p + 1)
result.set("type", "string")
return result
}
if ch == "\\" {
// Escape sequence (workaround: flatten to avoid MIR nested-if bug)
local has_next = 0
if p + 1 < s.length() { has_next = 1 }
if has_next == 0 { return null }
str = str + ch
p = p + 1
str = str + s.substring(p, p+1)
p = p + 1
continue
}
str = str + ch
p = p + 1
}
```
**Pattern 分類**: Pattern4 候補continue付き
**LoopBodyLocal**: `ch` (substring定義、複数条件で使用)
**Trim 類似度**: ★★☆☆☆ (構造が複雑)
**構造的差異**:
-`local ch = s.substring(p, p+1)` - Trim と同じ
- ❌ 複数の条件分岐(`ch == '"'`, `ch == "\\"`
- ❌ return 文での早期終了
- ❌ continue での反復継続
- ❌ 中間状態の蓄積(`str = str + ch`
**LoopBodyLocal 使用**: `ch` を複数箇所で使用
**Phase 173 での対応**: ⚠️ 除外複雑すぎる、Phase 174+ で検討)
---
### 5. _parse_array
**File**: `tools/hako_shared/json_parser.hako`
**Lines**: 189-230+
```hako
loop(p < s.length()) {
// Parse value
local val_result = me._parse_value(s, p)
if val_result == null { return null }
local val = val_result.get("value")
arr.push(val)
p = val_result.get("pos")
p = me._skip_whitespace(s, p)
if p < s.length() {
local next_ch = s.substring(p, p+1)
if next_ch == "," {
p = p + 1
p = me._skip_whitespace(s, p)
continue
}
if next_ch == "]" {
// End of array
p = p + 1
local result = new MapBox()
result.set("value", arr)
result.set("pos", p)
result.set("type", "array")
return result
}
}
return null
}
```
**Pattern 分類**: Pattern4 候補continue付き
**LoopBodyLocal**: `next_ch` (substring定義、条件で使用)
**Trim 類似度**: ★☆☆☆☆ (構造が大きく異なる)
**構造的差異**:
- ❌ MethodCall 多数(`me._parse_value`, `me._skip_whitespace`
- ❌ 複数の return 文(成功・失敗パス)
- ❌ 配列操作(`arr.push(val)`
- ❌ MapBox からの値取得
-`local next_ch = s.substring(p, p+1)` - Trim と同じパターン
-`if next_ch == ","` - 単純な等価比較
**LoopBodyLocal 使用**: `next_ch` のみ(限定的)
**Phase 173 での対応**: ⚠️ 除外構造が複雑、Phase 175+ で検討)
---
### 6. _unescape_string
**File**: `tools/hako_shared/json_parser.hako`
**Lines**: 373-410+
```hako
loop(i < s.length()) {
local ch = s.substring(i, i+1)
// Workaround: flatten to avoid MIR nested-if bug
local is_escape = 0
local has_next = 0
if ch == "\\" { is_escape = 1 }
if i + 1 < s.length() { has_next = 1 }
local process_escape = 0
if is_escape == 1 {
if has_next == 1 {
process_escape = 1
}
}
if process_escape == 1 {
// ... complex escape handling
}
result = result + ch
i = i + 1
}
```
**Pattern 分類**: Pattern1 候補break/continue なし)
**LoopBodyLocal**: `ch`, `is_escape`, `has_next`, `process_escape`
**Trim 類似度**: ☆☆☆☆☆ (全く異なる)
**構造的差異**:
- ❌ break/continue なし(自然終了のみ)
- ❌ 複数の LoopBodyLocal 変数
- ❌ ネストした条件分岐
- ❌ エスケープシーケンス処理
**Phase 173 での対応**: ❌ 除外Pattern1、LoopBodyLocal の問題なし)
**JoinIR 実行結果**: ❌ FAILED
```
[ERROR] ❌ MIR compilation error: [joinir/freeze] Loop lowering failed:
JoinIR does not support this pattern, and LoopBuilder has been removed.
```
**失敗理由**: Pattern1 でも JoinIR 未対応の構造ありPhase 173 対象外)
---
## 選定候補
### Phase 173-2 で選ぶべきループ: `_skip_whitespace`
**選定理由**:
1.**Trim と完全同型**: 既存の P5 パイプラインがそのまま使える
- `local ch = s.substring(p, p+1)` パターン
- `if ch == " " || ch == "\t" || ch == "\n" || ch == "\r"` 空白判定
- `break` での終了
2.**独立した関数**: 他の複雑なロジックに依存しない
3.**実用的**: JsonParser で頻繁に使われる7箇所で呼び出し
4.**テスト容易**: 単純な入出力でテスト可能
5.**既存実装の活用**:
- `TrimLoopHelper::is_safe_trim()` - そのまま使える
- `LoopBodyCarrierPromoter::try_promote()` - そのまま使える
- Pattern2 lowerer - Trim 特例経路がそのまま機能する
**次点候補**: なし他のループは複雑すぎるか、LoopBodyLocal 問題がない)
---
## 重要な発見
### ✅ JsonParser の _trim は既に P5 対応済み!
Phase 171 で実装した Trim パイプラインが、JsonParser の `_trim` メソッドで **既に完全動作** していることを確認:
```
[pattern2/trim] Safe Trim pattern detected, implementing lowering
[joinir/pattern2] Generated JoinIR for Loop with Break Pattern (Phase 170-B)
```
これは、Phase 173 の目的が達成済みであることを意味する。
### 🎯 Phase 173 の真の価値
**既存実装の検証**: Trim パイプラインが JsonParser でも機能することを実証
-`TrimLoopHelper` の汎用性確認
- ✅ Pattern2 Trim 特例の堅牢性確認
- ✅ LoopBodyLocal 昇格の実用性確認
**次のステップ**: `_skip_whitespace` で独立関数パターンの検証
- Trim は static box のメソッド内ループ
- _skip_whitespace は helper method 内ループ
- 両方で機能すれば、P5 パイプラインの汎用性が完全証明される
---
## 次のステップ (Phase 173-2)
### 選定ループ: `_skip_whitespace`
**選定基準確認**:
- ✅ LoopBodyLocal 変数を break 条件に使用
- ✅ substring でループ内定義
- ✅ OR chain での比較(空白文字判定)
- ✅ Pattern2 構造break 付き、continue なし)
**期待される動作**:
1. `LoopConditionScopeBox::analyze()` - `ch` を LoopBodyLocal として検出
2. `LoopBodyCarrierPromoter::try_promote()` - carrier `is_ch_match` に昇格
3. `TrimLoopHelper::is_safe_trim()` - true を返す
4. Pattern2 lowerer - Trim 特例経路で JoinIR 生成
**Phase 173-4 で実装**: ミニテストケースでの動作確認
---
## Phase 173-2 選定結果
**選定ループ**: `_skip_whitespace` (line 310-321)
**選定理由**:
1. **Trim と完全同型**: 構造が完全に一致
- `loop(p < s.length())` ← Trim の `loop(start < end)` と等価
- `local ch = s.substring(p, p+1)` ← 完全一致
- `if ch == " " || ch == "\t" || ch == "\n" || ch == "\r"` ← 完全一致
- `{ p = p + 1 } else { break }` ← Trim の `start = start + 1` と同パターン
2. **独立関数**: helper method として独立(テスト容易)
3. **実用性**: JsonParser で 7箇所で使用される頻出パターン
4. **既存実装で対応可能**:
- `TrimLoopHelper::is_safe_trim()` がそのまま使える
- `LoopBodyCarrierPromoter::try_promote()` で carrier 昇格可能
- Pattern2 Trim 特例経路で JoinIR 生成可能
5. **検証価値**:
-`_trim` メソッド内ループで既に成功static box method
- 🎯 `_skip_whitespace` で helper method パターンを検証
- → 両方成功すれば P5 パイプラインの汎用性が完全証明される
**次点候補**: なし
**理由**:
- `_parse_string` - 複雑すぎるreturn/continue 混在、Phase 174+
- `_parse_array` - MethodCall 多数、構造複雑Phase 175+
- `_unescape_string` - Pattern1、LoopBodyLocal 問題なし(対象外)
**Phase 173-3 での設計方針**: 既存の Trim パイプラインをそのまま活用