## Overview Analyzed 34 loops across selfhost codebase to identify JoinIR coverage gaps. Current readiness: 47% (16/30 loops). Next frontier: Pattern P5b (Escape Handling). ## Current Status - Phase 91 planning document: Complete - Loop inventory across 6 key files - Priority ranking: P5b (escape) > P5 (guard) > P6 (nested) - Effort estimates and ROI analysis - Pattern P5b Design: Complete - Problem statement (variable-step carriers) - Pattern definition with Skeleton layout - Recognition algorithm (8-step detection) - Capability taxonomy (P5b-specific guards) - Lowering strategy (Phase 92 preview) - Test fixture: Created - Minimal escape sequence parser - JSON string with backslash escape - Loop Canonicalizer extended - Capability table updated with P5b entries - Fail-Fast criteria documented - Implementation checklist added ## Key Findings ### Loop Readiness Matrix | Category | Count | JoinIR Status | |----------|-------|--------------| | Pattern 1 (simple bounded) | 16 | ✅ Ready | | Pattern 2 (with break) | 1 | ⚠️ Partial | | **Pattern P5b (escape seq)** | ~3 | ❌ NEW | | Pattern P5 (guard-bounded) | ~2 | ❌ Deferred | | Pattern P6 (nested loops) | ~8 | ❌ Deferred | ### Top Candidates 1. **P5b**: json_loader.hako:30 (8 lines, high reuse) - Effort: 2-3 days (recognition) - Impact: Unlocks all escape parsers 2. **P5**: mini_vm_core.hako:541 (204 lines, monolithic) - Effort: 1-2 weeks - Impact: Major JSON optimization 3. **P6**: seam_inspector.hako:76 (7+ nesting) - Effort: 2-3 weeks - Impact: Demonstrates nested composition ## Phase 91 Strategy **Recognition-only phase** (no lowering in P1): - Step 1: Design & planning ✅ - Step 2: Canonicalizer implementation (detect_escape_pattern) - Step 3: Unit tests + parity verification - Step 4: Lowering deferred to Phase 92 ## Files Added - docs/development/current/main/phases/phase-91/README.md - Full analysis & planning - docs/development/current/main/design/pattern-p5b-escape-design.md - Technical design - tools/selfhost/test_pattern5b_escape_minimal.hako - Test fixture ## Files Modified - docs/development/current/main/design/loop-canonicalizer.md - Capability table extended with P5b entries - Pattern P5b full section added - Implementation checklist updated ## Acceptance Criteria (Phase 91 Step 1) - ✅ Loop inventory complete (34 loops across 6 files) - ✅ Pattern P5b design document ready - ✅ Test fixture created - ✅ Capability taxonomy extended - ⏳ Implementation deferred (Step 2+) ## References - JoinIR Architecture: joinir-architecture-overview.md - Phase 91 Plan: phases/phase-91/README.md - P5b Design: design/pattern-p5b-escape-design.md Next: Implement detect_escape_pattern() recognition in Phase 91 Step 2 🤖 Generated with Claude Code Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
544 lines
21 KiB
Markdown
544 lines
21 KiB
Markdown
# Loop Canonicalizer(設計 SSOT)
|
||
|
||
Status: Phase 141 完了(ドキュメント充実)
|
||
Scope: ループ形の組み合わせ爆発を抑えるための "前処理" の設計(fixture/shape guard/fail-fast と整合)
|
||
Related:
|
||
- SSOT (契約/不変条件): `docs/development/current/main/joinir-architecture-overview.md`
|
||
- SSOT (地図/入口): `docs/development/current/main/design/joinir-design-map.md`
|
||
- SSOT (パターン空間): `docs/development/current/main/loop_pattern_space.md`
|
||
|
||
## アーキテクチャ図
|
||
|
||
### データフロー
|
||
|
||
```mermaid
|
||
graph LR
|
||
A[AST Loop] --> B[Canonicalizer]
|
||
B --> C{LoopSkeleton}
|
||
B --> D{RoutingDecision}
|
||
C --> E[Capability Guard]
|
||
D --> E
|
||
E --> F{Pass?}
|
||
F -->|Yes| G[Pattern Router]
|
||
F -->|No| H[Fail-Fast Error]
|
||
G --> I[JoinIR Lowerer]
|
||
```
|
||
|
||
### モジュール構成
|
||
|
||
```
|
||
loop_canonicalizer/
|
||
├── skeleton_types.rs [Data Structures]
|
||
│ ├── LoopSkeleton
|
||
│ ├── SkeletonStep
|
||
│ ├── UpdateKind
|
||
│ └── CarrierSlot
|
||
├── capability_guard.rs [Validation]
|
||
│ ├── RoutingDecision
|
||
│ ├── CapabilityTag (enum)
|
||
│ └── Decision constructors
|
||
├── pattern_recognizer.rs [Pattern Detection]
|
||
│ └── detect patterns (ast_feature_extractor 呼び出し)
|
||
└── canonicalizer.rs [Main Logic]
|
||
└── canonicalize_loop_expr()
|
||
↓
|
||
LoopSkeleton + RoutingDecision
|
||
↓
|
||
Pattern Router (patterns/router.rs)
|
||
↓
|
||
Pattern Lowerer (pattern1-5_*.rs)
|
||
```
|
||
|
||
### 処理フロー(Phase 140 完了後)
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant AST as AST Loop
|
||
participant Canon as Canonicalizer
|
||
participant Guard as Capability Guard
|
||
participant Router as Pattern Router
|
||
participant Lower as JoinIR Lowerer
|
||
|
||
AST->>Canon: canonicalize_loop_expr()
|
||
Canon->>Canon: extract pattern (ast_feature_extractor)
|
||
Canon->>Canon: build LoopSkeleton
|
||
Canon->>Guard: validate capabilities
|
||
alt All capabilities satisfied
|
||
Guard->>Router: RoutingDecision(Pattern2)
|
||
Router->>Lower: lower to JoinIR
|
||
Lower-->>AST: MIR blocks
|
||
else Missing capabilities
|
||
Guard-->>AST: Fail-Fast error
|
||
end
|
||
```
|
||
|
||
## 目的
|
||
|
||
- 実アプリ由来のループ形を、fixture + shape guard + Fail-Fast の段階投入で飲み込む方針を維持したまま、
|
||
“パターン数を増やさない” 形でスケールさせる。
|
||
- ループ lowerer が「検出 + 正規化 + merge 契約」を同時に背負って肥大化するのを防ぎ、責務を前処理に寄せる。
|
||
|
||
## 推奨配置(結論)
|
||
|
||
**おすすめ**: `AST → LoopSkeleton → JoinIR(Structured)` の前処理として Canonicalizer を置く。
|
||
|
||
- 「組み合わせ爆発」が Pattern 検出/shape guard の手前で起きるため、Normalized 変換だけでは吸収しきれない。
|
||
- LoopSkeleton を SSOT にすると、lowerer は “骨格を吐く” だけの薄い箱になり、Fail-Fast の理由が明確になる。
|
||
|
||
代替案(参考):
|
||
- `Structured JoinIR → Normalized JoinIR` を実質 Canonicalizer とみなす(既存延長)。
|
||
ただし「検出/整理の肥大」は Structured 生成側に残りやすい。
|
||
|
||
## LoopSkeleton(SSOT になる出力)
|
||
|
||
Canonicalizer の出力は “ループの骨格” に限る。例示フィールド:
|
||
|
||
- `steps: Vec<Step>`
|
||
- `HeaderCond`(あれば)
|
||
- `BodyInit`(body-local 初期化を分離するなら)
|
||
- `BreakCheck` / `ContinueCheck`(あれば)
|
||
- `Updates`(carrier 更新規則)
|
||
- `Tail`(継続呼び出し/次ステップ)
|
||
- `carriers: Vec<CarrierSlot>`(loop var を含む。役割/更新規則/境界通過の契約)
|
||
- `exits: ExitContract`(break/continue/return の有無と payload)
|
||
- `captured: Vec<CapturedSlot>`(外側変数の取り込み)
|
||
- `derived: Vec<DerivedSlot>`(digit_pos 等の派生値)
|
||
|
||
## Capability Guard(shape guard の上位化)
|
||
|
||
Skeleton を生成できても、lower/merge 契約が成立するとは限らない。
|
||
そこで `SkeletonGuard` を “Capability の集合” として設計する。
|
||
|
||
例:
|
||
- `RequiresConstStepIncrement`(i=i+const のみ)
|
||
- `BreakOnlyOnce` / `ContinueOnlyInTail`
|
||
- `NoSideEffectInHeader`(header に副作用がない)
|
||
- `ExitBindingsComplete`(境界へ渡す値が過不足ない)
|
||
|
||
未達の場合は Fail-Fast(理由を `RoutingDecision` に載せる)。
|
||
|
||
## RoutingDecision(理由の SSOT)
|
||
|
||
Canonicalizer は "できない理由" を機械的に返す。
|
||
|
||
- `RoutingDecision { chosen, missing_caps, notes, error_tags }`
|
||
- `missing_caps` は定型の語彙で出す(ログ/デバッグ/統計で集約可能にする)
|
||
|
||
## Capability Tags 対応表
|
||
|
||
### 各 Tag の詳細
|
||
|
||
| Tag | 必要な条件 | 対応Pattern | 検出方法 |
|
||
|-----|----------|------------|---------|
|
||
| `ConstStep` | キャリア更新が定数ステップ(`i = i + const`) | P1, P2, P3 | UpdateKind 分析(ConstStep variant) |
|
||
| `SingleBreak` | break 文が単一箇所のみ | P2 | AST 走査でカウント(`count_break_checks() == 1`) |
|
||
| `SingleContinue` | continue 文が単一箇所のみ | P1, P3 | AST 走査でカウント(`count_continue_checks() == 1`) |
|
||
| `PureHeader` | ループ条件に副作用がない | P1, P2, P3, P4 | 副作用解析(将来実装) |
|
||
| `OuterLocalCond` | 条件変数が外側スコープで定義済み | P3 | Scope 分析(BindingContext 参照) |
|
||
| `ExitBindings` | 境界へ渡す値が過不足ない | P2, P3 | Carrier 収支計算(ExitContract 分析) |
|
||
| `CarrierPromotion` | LoopBodyLocal を昇格可能 | P3, P4 | Binding 解析(promoted carriers 検出) |
|
||
| `BreakValueType` | break 値の型が一貫 | P2 | 型推論(TypeContext 参照) |
|
||
|
||
### 各 Pattern の必須 Capability(Phase 140 時点)
|
||
|
||
#### Pattern1 (Minimal)
|
||
- ✅ `ConstStep` - 定数ステップ増分
|
||
- ✅ `PureHeader` - 副作用なし条件
|
||
- ✅ `SingleContinue` - continue 単一箇所
|
||
|
||
#### Pattern2 (Break)
|
||
- ✅ `ConstStep` - 定数ステップ増分
|
||
- ✅ `PureHeader` - 副作用なし条件
|
||
- ✅ `SingleBreak` - break 単一箇所
|
||
- ✅ `ExitBindings` - 出口値の完全性
|
||
|
||
**例**: `skip_whitespace` は `has_break=true` なので Pattern2 へルーティング(Phase 137-5)
|
||
|
||
#### Pattern3 (IfPhi)
|
||
- ✅ `ConstStep` - 定数ステップ増分
|
||
- ✅ `PureHeader` - 副作用なし条件
|
||
- ✅ `OuterLocalCond` - 外側スコープ条件変数
|
||
- ✅ `CarrierPromotion` - LoopBodyLocal 昇格
|
||
|
||
**例**: Trim パターン(Phase 133)
|
||
|
||
#### Pattern4 (Composite)
|
||
- ✅ `PureHeader` - 副作用なし条件
|
||
- ✅ `CarrierPromotion` - LoopBodyLocal 昇格
|
||
- ✅ `ExitBindings` - 出口値の完全性
|
||
|
||
#### Pattern5 (Future)
|
||
- 🚧 TBD - 将来定義
|
||
|
||
### Capability 追加時のチェックリスト
|
||
|
||
新しい Capability を追加する際は以下を確認:
|
||
|
||
1. **enum 定義**: `capability_guard.rs` の `CapabilityTag` に variant 追加
|
||
2. **文字列変換**: `to_tag()` メソッドに対応を追加(`CAP_MISSING_*` 形式)
|
||
3. **説明文**: `description()` メソッドに説明を追加
|
||
4. **検出ロジック**: `canonicalizer.rs` に検出ロジックを実装
|
||
5. **対応表更新**: このドキュメントの対応表を更新
|
||
6. **Pattern 更新**: 必要に応じて各 Pattern の必須 Capability リストを更新
|
||
7. **テスト追加**: 新 Capability の検出テストを追加
|
||
|
||
## Corpus / Signature(拡張のための仕組み)
|
||
|
||
将来の規模増加に備え、ループ形の差分検知を Skeleton ベースで行えるようにする。
|
||
|
||
- `LoopSkeletonSignature = hash(steps + exit_contract + carrier_roles + required_caps)`
|
||
- 既知集合との差分が出たら “fixture 化候補” として扱う(設計上の導線)。
|
||
|
||
## 実装の境界(非目標)
|
||
|
||
- 新しい言語仕様/ルール実装はしない(既存の意味論を保つ)。
|
||
- 非 JoinIR への退避(prohibited fallback)は導入しない。
|
||
- 既定挙動は変えない(必要なら dev-only で段階投入する)。
|
||
|
||
## LoopSkeleton の最小フィールド(SSOT 境界)
|
||
|
||
Canonicalizer の出力は以下のフィールドに限定する(これ以上細かくしない):
|
||
|
||
```rust
|
||
/// ループの骨格(Canonicalizer の唯一の出力)
|
||
pub struct LoopSkeleton {
|
||
/// ステップの列(HeaderCond, BodyInit, BreakCheck, Updates, Tail)
|
||
pub steps: Vec<SkeletonStep>,
|
||
|
||
/// キャリア(ループ変数・更新規則・境界通過の契約)
|
||
pub carriers: Vec<CarrierSlot>,
|
||
|
||
/// 出口契約(break/continue/return の有無と payload)
|
||
pub exits: ExitContract,
|
||
|
||
/// 外部キャプチャ(省略可: 外側変数の取り込み)
|
||
pub captured: Option<Vec<CapturedSlot>>,
|
||
}
|
||
|
||
/// ステップの種類(最小限)
|
||
pub enum SkeletonStep {
|
||
/// ループ継続条件(loop(cond) の cond)
|
||
HeaderCond { expr: AstExpr },
|
||
|
||
/// 早期終了チェック(if cond { break })
|
||
BreakCheck { cond: AstExpr, has_value: bool },
|
||
|
||
/// スキップチェック(if cond { continue })
|
||
ContinueCheck { cond: AstExpr },
|
||
|
||
/// キャリア更新(i = i + 1 など)
|
||
Update { carrier_name: String, update_kind: UpdateKind },
|
||
|
||
/// 本体(その他の命令)
|
||
Body { stmts: Vec<AstStmt> },
|
||
}
|
||
|
||
/// キャリアの更新種別
|
||
pub enum UpdateKind {
|
||
/// 定数ステップ(i = i + const)
|
||
ConstStep { delta: i64 },
|
||
|
||
/// 条件付き更新(if cond { x = a } else { x = b })
|
||
Conditional { then_value: AstExpr, else_value: AstExpr },
|
||
|
||
/// 任意更新(上記以外)
|
||
Arbitrary,
|
||
}
|
||
|
||
/// 出口契約
|
||
pub struct ExitContract {
|
||
pub has_break: bool,
|
||
pub has_continue: bool,
|
||
pub has_return: bool,
|
||
pub break_has_value: bool,
|
||
}
|
||
|
||
/// キャリアスロット
|
||
pub struct CarrierSlot {
|
||
pub name: String,
|
||
pub role: CarrierRole,
|
||
pub update_kind: UpdateKind,
|
||
}
|
||
|
||
/// キャリアの役割
|
||
pub enum CarrierRole {
|
||
/// ループカウンタ(i < n の i)
|
||
Counter,
|
||
/// アキュムレータ(sum += x の sum)
|
||
Accumulator,
|
||
/// 条件変数(while(is_valid) の is_valid)
|
||
ConditionVar,
|
||
/// 派生値(digit_pos 等)
|
||
Derived,
|
||
}
|
||
```
|
||
|
||
### SSOT 境界の原則
|
||
|
||
- **入力**: AST(LoopExpr)
|
||
- **出力**: LoopSkeleton のみ(JoinIR は生成しない)
|
||
- **禁止**: Skeleton に JoinIR 固有の情報を含めない(BlockId, ValueId 等)
|
||
|
||
---
|
||
|
||
## 実装の入口(現状)
|
||
|
||
実装(Phase 1–2)はここ:
|
||
- `src/mir/loop_canonicalizer/mod.rs`
|
||
|
||
注意:
|
||
- Phase 2 で `canonicalize_loop_expr(...) -> Result<(LoopSkeleton, RoutingDecision), String>` を導入し、JoinIR ループ入口で dev-only 観測できるようにした(既定挙動は不変)。
|
||
- 観測ポイント(JoinIR ループ入口): `src/mir/builder/control_flow/joinir/routing.rs`(`joinir_dev_enabled()` 配下)
|
||
- Phase 3 で `skip_whitespace` の最小形を `Pattern3IfPhi` として安定に識別できるようにした(dev-only 観測)。
|
||
|
||
## Capability の語彙(Fail-Fast reason タグ)
|
||
|
||
Skeleton を生成できても lower/merge できるとは限らない。以下の Capability で判定する:
|
||
|
||
| Capability | 説明 | 未達時の理由タグ | Pattern対応 |
|
||
|--------------------------|------------------------------------------|-------------------------------------|------------|
|
||
| `ConstStepIncrement` | キャリア更新が定数ステップ(i=i+const) | `CAP_MISSING_CONST_STEP` | P1-P5 |
|
||
| `SingleBreakPoint` | break が単一箇所のみ | `CAP_MISSING_SINGLE_BREAK` | P1-P5 |
|
||
| `SingleContinuePoint` | continue が単一箇所のみ | `CAP_MISSING_SINGLE_CONTINUE` | P4 |
|
||
| `NoSideEffectInHeader` | ループ条件に副作用がない | `CAP_MISSING_PURE_HEADER` | P1-P5 |
|
||
| `OuterLocalCondition` | 条件変数が外側スコープで定義済み | `CAP_MISSING_OUTER_LOCAL_COND` | P1-P5 |
|
||
| `ExitBindingsComplete` | 境界へ渡す値が過不足ない | `CAP_MISSING_EXIT_BINDINGS` | P1-P5 |
|
||
| `CarrierPromotion` | LoopBodyLocal を昇格可能 | `CAP_MISSING_CARRIER_PROMOTION` | P2-P3 |
|
||
| `BreakValueConsistent` | break 値の型が一貫 | `CAP_MISSING_BREAK_VALUE_TYPE` | P2-P5 |
|
||
| `EscapeSequencePattern` | エスケープシーケンス対応(P5b専用) | `CAP_MISSING_ESCAPE_PATTERN` | **P5b** |
|
||
|
||
**新規 P5b 関連 Capability**:
|
||
|
||
| Capability | 説明 | 必須条件 |
|
||
|--------------------------|------------------------------------------|---------------------------------------|
|
||
| `ConstEscapeDelta` | escape_delta が定数 | `if ch == "\\" { i = i + const }` |
|
||
| `ConstNormalDelta` | normal_delta が定数 | `i = i + const` (after escape block) |
|
||
| `SingleEscapeCheck` | escape check が単一箇所のみ | 複数の escape 処理がない |
|
||
| `ClearBoundaryCondition` | 文字列終端検出が明確 | `if ch == boundary { break }` |
|
||
|
||
### 語彙の安定性
|
||
|
||
- reason タグは `CAP_MISSING_*` プレフィックスで統一
|
||
- 新規追加時は `loop-canonicalizer.md` に先に追記してからコード実装
|
||
- ログ / 統計 / error_tags で集約可能
|
||
|
||
---
|
||
|
||
## RoutingDecision の出力先
|
||
|
||
Canonicalizer の判定結果は `RoutingDecision` に集約し、以下に流す:
|
||
|
||
```rust
|
||
pub struct RoutingDecision {
|
||
/// 選択された Pattern(None = Fail-Fast)
|
||
/// Phase 137-5: ExitContract に基づく最終 lowerer 選択を反映
|
||
pub chosen: Option<LoopPatternKind>,
|
||
|
||
/// 不足している Capability のリスト
|
||
pub missing_caps: Vec<&'static str>,
|
||
|
||
/// 選択理由(デバッグ用)
|
||
pub notes: Vec<String>,
|
||
|
||
/// error_tags への追記(contract_checks 用)
|
||
pub error_tags: Vec<String>,
|
||
}
|
||
```
|
||
|
||
### Phase 137-5: Decision Policy SSOT
|
||
|
||
**原則**: `RoutingDecision.chosen` は「lowerer 選択の最終結果」を返す(構造クラス名ではなく)。
|
||
|
||
- **ExitContract が優先**: `has_break=true` なら `Pattern2Break`、`has_continue=true` なら `Pattern4Continue`
|
||
- **構造的特徴は notes へ**: 「if-else 構造がある」等の情報は `notes` フィールドに記録
|
||
- **一致保証**: Router と Canonicalizer の pattern 選択が一致することを parity check で検証
|
||
|
||
**例**: `skip_whitespace` パターン
|
||
- 構造: if-else 形式(Pattern3IfPhi に似ている)
|
||
- ExitContract: `has_break=true`
|
||
- **chosen**: `Pattern2Break`(ExitContract が決定)
|
||
- **notes**: "if-else structure with break in else branch"(構造特徴を記録)
|
||
|
||
### 出力先マッピング
|
||
|
||
| 出力先 | 条件 | 用途 |
|
||
|----------------------------|-------------------------------|-----------------------------------|
|
||
| `error_tags` | `chosen.is_none()` | Fail-Fast のエラーメッセージ |
|
||
| `contract_checks` | debug build + 契約違反時 | Phase 135 P1 の verifier に統合 |
|
||
| JoinIR dev/debug | `joinir_dev_enabled()==true` | 開発時のルーティング追跡 |
|
||
| 統計 JSON | 将来拡張 | Corpus 分析(Skeleton Signature) |
|
||
|
||
### error_tags との統合
|
||
|
||
- `RoutingDecision` の Fail-Fast 文言は `src/mir/join_ir/lowering/error_tags.rs` の語彙に寄せる
|
||
- 既存のエラータグ(例: `error_tags::freeze(...)`)を使用し、文字列直書きを増やさない
|
||
|
||
---
|
||
|
||
## 対象ループ 1: skip_whitespace(受け入れ基準)
|
||
|
||
### 対象ファイル
|
||
|
||
`tools/selfhost/test_pattern3_skip_whitespace.hako`
|
||
|
||
```hako
|
||
loop(p < len) {
|
||
local ch = s.substring(p, p + 1)
|
||
local is_ws = ... // whitespace 判定
|
||
if is_ws == 1 {
|
||
p = p + 1
|
||
} else {
|
||
break
|
||
}
|
||
}
|
||
```
|
||
|
||
### Skeleton 差分
|
||
|
||
| フィールド | 値 |
|
||
|-------------------|-------------------------------------------|
|
||
| `steps[0]` | `HeaderCond { expr: p < len }` |
|
||
| `steps[1]` | `Body { ... }` (ch, is_ws 計算) |
|
||
| `steps[2]` | `BreakCheck { cond: is_ws == 0 }` |
|
||
| `steps[3]` | `Update { carrier: "p", ConstStep(1) }` |
|
||
| `carriers[0]` | `{ name: "p", role: Counter, ConstStep }` |
|
||
| `exits` | `{ has_break: true, break_has_value: false }` |
|
||
|
||
### 必要 Capability
|
||
|
||
- ✅ `ConstStepIncrement` (p = p + 1)
|
||
- ✅ `SingleBreakPoint` (else { break } のみ)
|
||
- ✅ `OuterLocalCondition` (p, len は外側定義)
|
||
- ✅ `ExitBindingsComplete` (p を境界に渡す)
|
||
|
||
### 受け入れ基準
|
||
|
||
1. `LoopCanonicalizer::canonicalize(ast)` が上記 Skeleton を返す
|
||
2. `RoutingDecision.chosen == Some(Pattern3)`
|
||
3. `RoutingDecision.missing_caps == []`
|
||
4. 既存 smoke `phase135_trim_mir_verify.sh` が退行しない
|
||
|
||
---
|
||
|
||
## 対象ループ 2: Pattern P5b - Escape Sequence Handling(Phase 91 新規)
|
||
|
||
### 目的
|
||
|
||
エスケープシーケンス対応ループを JoinIR 対象に拡大する。JSON/CSV パーサーの文字列処理で共通パターン。
|
||
|
||
### 対象ファイル
|
||
|
||
`tools/selfhost/test_pattern5b_escape_minimal.hako`
|
||
|
||
```hako
|
||
loop(i < n) {
|
||
local ch = s.substring(i, i+1)
|
||
|
||
if ch == "\"" { break } // String boundary
|
||
|
||
if ch == "\\" {
|
||
i = i + 1 // Skip escape character (conditional +2 total)
|
||
ch = s.substring(i, i+1) // Read escaped character
|
||
}
|
||
|
||
out = out + ch // Process character
|
||
i = i + 1 // Standard increment
|
||
}
|
||
```
|
||
|
||
### Pattern P5b の特徴
|
||
|
||
| 特性 | 説明 |
|
||
|-----|------|
|
||
| **Header** | `loop(i < n)` - Bounded loop on string length |
|
||
| **Escape Check** | `if ch == escape_char { i = i + escape_delta }` |
|
||
| **Normal Increment** | `i = i + 1` (always +1) |
|
||
| **Accumulator** | `out = out + char` - String append pattern |
|
||
| **Boundary** | `if ch == boundary { break }` - String terminator |
|
||
| **Carriers** | Position (`i`), Accumulator (`out`) |
|
||
| **Deltas** | normal_delta=1, escape_delta=2 (or variable) |
|
||
|
||
### 必要 Capability (P5b 拡張)
|
||
|
||
- ✅ `ConstStepIncrement` (normal: i = i + 1)
|
||
- ✅ `SingleBreakPoint` (boundary check only)
|
||
- ✅ `NoSideEffectInHeader` (i < n は pure)
|
||
- ✅ `OuterLocalCondition` (i, n は外側定義)
|
||
- ✅ **`ConstEscapeDelta`** (escape: i = i + 2, etc.) - **P5b 専用**
|
||
- ✅ **`SingleEscapeCheck`** (one escape pattern only) - **P5b 専用**
|
||
- ✅ **`ClearBoundaryCondition`** (explicit boundary detection) - **P5b 専用**
|
||
|
||
### Fail-Fast 基準 (P5b 非対応のケース)
|
||
|
||
以下のいずれかに該当する場合、Fail-Fast:
|
||
|
||
1. **複数エスケープチェック**: `if ch == "\\" ... if ch2 == "'" ...`
|
||
- 理由: `CAP_MISSING_SINGLE_ESCAPE_CHECK`
|
||
|
||
2. **可変ステップ**: `i = i + var` (定数でない)
|
||
- 理由: `CAP_MISSING_CONST_ESCAPE_DELTA`
|
||
|
||
3. **無条件に近いループ**: `loop(true)` without clear boundary
|
||
- 理由: `CAP_MISSING_CLEAR_BOUNDARY_CONDITION`
|
||
|
||
4. **複数 break 点**: String boundary + escape processing で exit
|
||
- 理由: `CAP_MISSING_SINGLE_BREAK`
|
||
|
||
### 認識アルゴリズム (高レベル)
|
||
|
||
```
|
||
1. Header carrier 抽出: loop(i < n) から i を取得
|
||
2. Escape check block 発見: if ch == "\" { ... }
|
||
3. Escape delta 抽出: i = i + const
|
||
4. Accumulator パターン発見: out = out + ch
|
||
5. Normal increment 抽出: i = i + 1 (escape if block 外)
|
||
6. Boundary check 発見: if ch == "\"" { break }
|
||
7. LoopSkeleton 構築
|
||
- carriers: [i (dual deltas), out (append)]
|
||
- exits: has_break=true
|
||
8. RoutingDecision: Pattern5bEscape
|
||
```
|
||
|
||
### 実装予定 (Phase 91)
|
||
|
||
**Step 1** (このドキュメント):
|
||
- [ ] Pattern P5b 設計書完成 ✅
|
||
- [ ] テストフィクスチャ作成 ✅
|
||
- [ ] Capability 定義追加 ✅
|
||
|
||
**Step 2** (Phase 91 本実装):
|
||
- [ ] `detect_escape_pattern()` in Canonicalizer
|
||
- [ ] Unit tests (P5b recognition)
|
||
- [ ] Parity verification (strict mode)
|
||
- [ ] Documentation update
|
||
|
||
**Step 3** (Phase 92 lowering):
|
||
- [ ] Pattern5bEscape lowerer 実装
|
||
- [ ] E2E test with escape fixture
|
||
- [ ] VM/LLVM parity verification
|
||
|
||
### 受け入れ基準 (Phase 91)
|
||
|
||
1. ✅ Canonicalizer が escape pattern を認識
|
||
2. ✅ RoutingDecision.chosen == Pattern5bEscape
|
||
3. ✅ missing_caps == [] (すべての capability 満たす)
|
||
4. ✅ Strict parity green (`HAKO_JOINIR_STRICT=1`)
|
||
5. ✅ 既存テスト退行なし
|
||
6. ❌ Lowering は Step 3 へ (Phase 91 では recognition のみ)
|
||
|
||
### References
|
||
|
||
- **P5b 詳細設計**: `docs/development/current/main/design/pattern-p5b-escape-design.md`
|
||
- **テストフィクスチャ**: `tools/selfhost/test_pattern5b_escape_minimal.hako`
|
||
- **Phase 91 計画**: `docs/development/current/main/phases/phase-91/README.md`
|
||
|
||
---
|
||
|
||
## 追加・変更チェックリスト
|
||
|
||
- [ ] 追加するループ形を最小 fixture に落とす(再現固定)
|
||
- [ ] LoopSkeleton の差分(steps/exits/carriers)を明示する
|
||
- [ ] 必要 Capability を列挙し、未達は Fail-Fast(理由が出る)
|
||
- [ ] 既存 smoke/verify が退行しない(quick は重くしない)
|
||
- [ ] 新規 Capability は先に `loop-canonicalizer.md` に追記してから実装
|