311 lines
9.6 KiB
Markdown
311 lines
9.6 KiB
Markdown
|
|
# Phase 173-3: JsonParser P5 Design
|
|||
|
|
|
|||
|
|
**Date**: 2025-12-08
|
|||
|
|
**Purpose**: Trim P5 パイプラインを JsonParser に展開する設計
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 基本方針
|
|||
|
|
|
|||
|
|
> **「Trim パターンをそのまま再利用」** - JsonParser の `_skip_whitespace` は Trim と完全同型なので、既存の TrimPatternInfo / TrimLoopHelper をそのまま使用できる。
|
|||
|
|
|
|||
|
|
### 重要な発見
|
|||
|
|
|
|||
|
|
Phase 173-1 の観測で判明した事実:
|
|||
|
|
|
|||
|
|
✅ **JsonParser の `_trim` メソッドは既に P5 対応済み!**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
[pattern2/trim] Safe Trim pattern detected, implementing lowering
|
|||
|
|
[pattern2/trim] Carrier: 'is_ch_match', original var: 'ch', whitespace chars: ["\r", "\n", "\t", " "]
|
|||
|
|
[joinir/pattern2] Generated JoinIR for Loop with Break Pattern (Phase 170-B)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
これは、Phase 171 で実装した P5 パイプラインが、以下の範囲で既に機能していることを意味する:
|
|||
|
|
|
|||
|
|
- ✅ `LoopConditionScopeBox::analyze()` - LoopBodyLocal 検出
|
|||
|
|
- ✅ `LoopBodyCarrierPromoter::try_promote()` - Carrier 昇格
|
|||
|
|
- ✅ `TrimLoopHelper::is_safe_trim()` - Trim パターン検証
|
|||
|
|
- ✅ Pattern2 lowerer - Trim 特例経路で JoinIR 生成
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 必要な対応
|
|||
|
|
|
|||
|
|
### 1. 命名の汎用化(将来対応、今回は不要)
|
|||
|
|
|
|||
|
|
現在の `TrimLoopHelper` は「Trim」という名前だが、実際は「LoopBodyLocal 変数を bool carrier に昇格する」汎用的なパターン。将来的には以下の名前変更を検討:
|
|||
|
|
|
|||
|
|
- `TrimPatternInfo` → `CharComparisonPatternInfo`
|
|||
|
|
- `TrimLoopHelper` → `CharComparisonLoopHelper`
|
|||
|
|
|
|||
|
|
**ただし Phase 173 では名前変更しない**(挙動不変を優先)。
|
|||
|
|
|
|||
|
|
**理由**:
|
|||
|
|
1. **動作実績**: Phase 171 で Trim として実装し、既に JsonParser._trim で動作確認済み
|
|||
|
|
2. **リスク回避**: 名前変更は破壊的変更のリスクあり
|
|||
|
|
3. **段階的実装**: まず JsonParser で動作確認してから、汎用化を検討
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2. JsonParser 用のテストケース追加
|
|||
|
|
|
|||
|
|
**目的**: `_skip_whitespace` が P5 パイプラインで動作することを確認
|
|||
|
|
|
|||
|
|
**ファイル**: `local_tests/test_jsonparser_skip_whitespace.hako`(NEW)
|
|||
|
|
|
|||
|
|
**内容**:
|
|||
|
|
```hako
|
|||
|
|
static box JsonParserTest {
|
|||
|
|
s: StringBox
|
|||
|
|
pos: IntegerBox
|
|||
|
|
len: IntegerBox
|
|||
|
|
|
|||
|
|
skip_whitespace(input_str, start_pos) {
|
|||
|
|
me.s = input_str
|
|||
|
|
me.pos = start_pos
|
|||
|
|
me.len = me.s.length()
|
|||
|
|
|
|||
|
|
local p = me.pos
|
|||
|
|
loop(p < me.len) {
|
|||
|
|
local ch = me.s.substring(p, p+1)
|
|||
|
|
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
|
|||
|
|
p = p + 1
|
|||
|
|
} else {
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return p
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
main() {
|
|||
|
|
print("=== JsonParser skip_whitespace Test ===")
|
|||
|
|
|
|||
|
|
local result = me.skip_whitespace(" \t\n hello", 0)
|
|||
|
|
print("Skipped to position: ")
|
|||
|
|
print(result)
|
|||
|
|
|
|||
|
|
if result == 5 {
|
|||
|
|
print("PASS: Correctly skipped 5 whitespace characters")
|
|||
|
|
return 0
|
|||
|
|
} else {
|
|||
|
|
print("FAIL: Expected position 5, got ")
|
|||
|
|
print(result)
|
|||
|
|
return 1
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**期待される動作**:
|
|||
|
|
1. `loop(p < me.len)` - Pattern2 として検出
|
|||
|
|
2. `local ch = me.s.substring(p, p+1)` - LoopBodyLocal として検出
|
|||
|
|
3. `if ch == " " || ...` - Trim パターンとして認識
|
|||
|
|
4. Carrier `is_ch_match` に昇格
|
|||
|
|
5. JoinIR 生成成功
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 3. Pattern2 での Trim 特例判定拡張(必要なら)
|
|||
|
|
|
|||
|
|
現在の Pattern2 は「Trim」という名前で判定しているが、実際は構造判定なので、JsonParser の `_skip_whitespace` も同じ経路を通るはず。
|
|||
|
|
|
|||
|
|
**確認事項**:
|
|||
|
|
- ✅ `TrimLoopHelper::is_safe_trim()` が JsonParser ループでも true になるか?
|
|||
|
|
- ✅ `substring()` メソッドの検出が機能するか?
|
|||
|
|
- ⚠️ `charAt()` メソッドの検出が必要か?(`_skip_whitespace` は `substring()` を使用)
|
|||
|
|
|
|||
|
|
**結論**: Phase 173-4 で実行時に確認
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## データフロー(JsonParser 版)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
JsonParser._skip_whitespace (AST)
|
|||
|
|
↓
|
|||
|
|
LoopConditionScopeBox::analyze()
|
|||
|
|
↓ has_loop_body_local() == true (ch が LoopBodyLocal)
|
|||
|
|
LoopBodyCarrierPromoter::try_promote()
|
|||
|
|
↓ Promoted { trim_info }
|
|||
|
|
TrimPatternInfo::to_carrier_info()
|
|||
|
|
↓
|
|||
|
|
CarrierInfo::merge_from()
|
|||
|
|
↓
|
|||
|
|
TrimLoopHelper::is_safe_trim() → true
|
|||
|
|
↓
|
|||
|
|
Pattern2 lowerer (Trim 特例経路)
|
|||
|
|
↓
|
|||
|
|
JoinIR 生成(bool carrier: is_ch_match)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Trim との違い**: なし(完全同型)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 実装方針
|
|||
|
|
|
|||
|
|
### Phase 173-4 で確認すべきこと
|
|||
|
|
|
|||
|
|
1. **LoopBodyLocal 検出**: JsonParser の `_skip_whitespace` が `LoopBodyCarrierPromoter` で検出されるか?
|
|||
|
|
- 期待: `local ch = s.substring(p, p+1)` が LoopBodyLocal として認識される
|
|||
|
|
|
|||
|
|
2. **Trim パターン認識**: `TrimLoopHelper::is_safe_trim()` が true になるか?
|
|||
|
|
- 期待: 空白文字判定パターンが検出される
|
|||
|
|
|
|||
|
|
3. **Trim 特例経路**: Pattern2 の Trim 特例経路を通るか?
|
|||
|
|
- 期待: `[pattern2/trim] Safe Trim pattern detected` が出力される
|
|||
|
|
|
|||
|
|
4. **JoinIR 生成**: JoinIR → MIR lowering が成功するか?
|
|||
|
|
- 期待: `[joinir/pattern2] Generated JoinIR for Loop with Break Pattern` が出力される
|
|||
|
|
|
|||
|
|
5. **実行成功**: 生成された MIR が正しく実行されるか?
|
|||
|
|
- 期待: `PASS: Correctly skipped 5 whitespace characters` が出力される
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 追加実装が必要な場合
|
|||
|
|
|
|||
|
|
**可能性のある拡張**:
|
|||
|
|
|
|||
|
|
1. **charAt() メソッドの検出**
|
|||
|
|
- 現在: `substring()` のみ検出
|
|||
|
|
- 拡張: `charAt()` も検出(JsonParser の一部のコードで使用)
|
|||
|
|
- 実装箇所: `src/mir/loop_pattern_detection/loop_body_carrier_promoter.rs`
|
|||
|
|
- 方法: `is_substring_method_call()` を `is_char_extraction_method()` に拡張
|
|||
|
|
|
|||
|
|
2. **メソッド名の汎用化**
|
|||
|
|
- 現在: "substring" ハードコード
|
|||
|
|
- 拡張: "charAt", "substring" の両方に対応
|
|||
|
|
- リスク: 低(既存動作を壊さない追加)
|
|||
|
|
|
|||
|
|
**Phase 173-4 での判断基準**:
|
|||
|
|
- ✅ `_skip_whitespace` が `substring()` のみ使用 → 追加実装不要
|
|||
|
|
- ⚠️ 他の JsonParser ループが `charAt()` 使用 → Phase 174+ で対応
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 構造比較
|
|||
|
|
|
|||
|
|
### Trim (test_trim_main_pattern.hako)
|
|||
|
|
|
|||
|
|
```hako
|
|||
|
|
loop(start < end) {
|
|||
|
|
local ch = s.substring(start, start+1)
|
|||
|
|
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
|
|||
|
|
start = start + 1
|
|||
|
|
} else {
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### JsonParser._skip_whitespace
|
|||
|
|
|
|||
|
|
```hako
|
|||
|
|
loop(p < s.length()) {
|
|||
|
|
local ch = s.substring(p, p+1)
|
|||
|
|
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
|
|||
|
|
p = p + 1
|
|||
|
|
} else {
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 構造的一致点
|
|||
|
|
|
|||
|
|
| 要素 | Trim | JsonParser | 一致 |
|
|||
|
|
|------|------|------------|------|
|
|||
|
|
| ループ条件 | `start < end` | `p < s.length()` | ✅ (比較演算) |
|
|||
|
|
| LoopBodyLocal | `local ch = s.substring(...)` | `local ch = s.substring(...)` | ✅ (完全一致) |
|
|||
|
|
| 空白判定 | `ch == " " \|\| ch == "\t" \|\| ...` | `ch == " " \|\| ch == "\t" \|\| ...` | ✅ (完全一致) |
|
|||
|
|
| 進行処理 | `start = start + 1` | `p = p + 1` | ✅ (加算代入) |
|
|||
|
|
| 終了処理 | `break` | `break` | ✅ (完全一致) |
|
|||
|
|
|
|||
|
|
**結論**: 100% 構造的に一致 → 既存の P5 パイプラインで完全対応可能
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## リスク分析
|
|||
|
|
|
|||
|
|
### 低リスク要因
|
|||
|
|
|
|||
|
|
1. **既存実装の活用**:
|
|||
|
|
- `TrimLoopHelper` は既に JsonParser._trim で動作確認済み
|
|||
|
|
- `LoopBodyCarrierPromoter` は既に Trim で動作確認済み
|
|||
|
|
- Pattern2 Trim 特例経路は既に実装済み
|
|||
|
|
|
|||
|
|
2. **構造的一致**:
|
|||
|
|
- `_skip_whitespace` と Trim は完全同型
|
|||
|
|
- 新しいパターン認識ロジック不要
|
|||
|
|
|
|||
|
|
3. **独立性**:
|
|||
|
|
- `_skip_whitespace` は独立した helper method
|
|||
|
|
- 他のコードへの影響なし
|
|||
|
|
|
|||
|
|
### 潜在的リスク
|
|||
|
|
|
|||
|
|
1. **MethodCall 検出の差異**:
|
|||
|
|
- Trim: static box method 内のループ
|
|||
|
|
- JsonParser: helper method 内のループ
|
|||
|
|
- 影響: 低(AST レベルでは同じ構造)
|
|||
|
|
|
|||
|
|
2. **変数スコープの差異**:
|
|||
|
|
- Trim: `start`, `end` が method local
|
|||
|
|
- JsonParser: `p` が method local、`s` が parameter
|
|||
|
|
- 影響: 低(LoopConditionScopeBox は parameter も OuterLocal として扱う)
|
|||
|
|
|
|||
|
|
**結論**: 既存実装で対応可能、追加実装不要の見込み
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 次のステップ (Phase 173-4)
|
|||
|
|
|
|||
|
|
### 実装タスク
|
|||
|
|
|
|||
|
|
1. **テストケース作成**:
|
|||
|
|
- `local_tests/test_jsonparser_skip_whitespace.hako` 作成
|
|||
|
|
- Trim と同じ構造、JsonParser の文脈でテスト
|
|||
|
|
|
|||
|
|
2. **JoinIR モード実行**:
|
|||
|
|
```bash
|
|||
|
|
NYASH_JOINIR_CORE=1 NYASH_LEGACY_LOOPBUILDER=0 \
|
|||
|
|
./target/release/hakorune local_tests/test_jsonparser_skip_whitespace.hako
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
3. **期待される出力確認**:
|
|||
|
|
```
|
|||
|
|
[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
|
|||
|
|
PASS: Correctly skipped 5 whitespace characters
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
4. **charAt() 対応確認**:
|
|||
|
|
- 必要なら `LoopBodyCarrierPromoter` を拡張
|
|||
|
|
- Phase 173-4 の実行結果で判断
|
|||
|
|
|
|||
|
|
5. **テスト実行**:
|
|||
|
|
```bash
|
|||
|
|
cargo test --release --lib loop_body_carrier_promoter
|
|||
|
|
cargo test --release --lib pattern2_with_break
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 成功基準
|
|||
|
|
|
|||
|
|
- ✅ `test_jsonparser_skip_whitespace.hako` が JoinIR で成功
|
|||
|
|
- ✅ `[pattern2/trim] Safe Trim pattern detected` 出力
|
|||
|
|
- ✅ `PASS: Correctly skipped 5 whitespace characters` 出力
|
|||
|
|
- ✅ 既存の Trim テストが引き続き PASS
|
|||
|
|
- ✅ ユニットテスト全て PASS
|
|||
|
|
|
|||
|
|
**ドキュメント成果物**:
|
|||
|
|
- `phase173-jsonparser-p5-impl.md` - 実装結果レポート
|
|||
|
|
- `CURRENT_TASK.md` - Phase 173 成果記録
|