624 lines
18 KiB
Markdown
624 lines
18 KiB
Markdown
|
|
# Phase 195: Pattern 3 拡張(if-in-loop + マルチキャリア)
|
|||
|
|
|
|||
|
|
**Status**: Design Phase
|
|||
|
|
**Date**: 2025-12-09
|
|||
|
|
**Prerequisite**: Phase 194 complete (JsonParser deployment & validation)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 目的
|
|||
|
|
|
|||
|
|
**Pattern 3(If-Else PHI)の対応範囲を拡張**し、複数キャリアの条件付き更新を JoinIR で扱えるようにする。
|
|||
|
|
|
|||
|
|
**スコープ**:
|
|||
|
|
- ✅ if 内で完結する複数キャリア更新(2-3個程度)
|
|||
|
|
- ❌ ConditionEnv 拡張は行わない(外部ローカル・digits 系は Phase 200+ 保留)
|
|||
|
|
- ❌ LoopBodyLocal + MethodCall 混在は Phase 195+ に延期
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Task 195-1: 対象ループの絞り込み(doc-only)
|
|||
|
|
|
|||
|
|
### 目標
|
|||
|
|
JsonParser / selfhost から「P3 で攻めたいループ」を **1-2 本だけ**選定し、AST 構造を詳細に分析する。
|
|||
|
|
|
|||
|
|
### 候補ループ
|
|||
|
|
|
|||
|
|
#### 候補 1: JsonParser `_parse_string` の簡易版(優先度: 高)
|
|||
|
|
|
|||
|
|
**ループ構造**:
|
|||
|
|
```nyash
|
|||
|
|
// _parse_string (escape 処理の簡略版)
|
|||
|
|
static box JsonParser {
|
|||
|
|
_parse_string(s, start) {
|
|||
|
|
local buffer = ""
|
|||
|
|
local escaped = false
|
|||
|
|
local i = start
|
|||
|
|
|
|||
|
|
loop(i < s.length() and s[i] != '"') {
|
|||
|
|
local ch = s[i]
|
|||
|
|
|
|||
|
|
if(ch == '\\') {
|
|||
|
|
escaped = true // ← Carrier 1 update
|
|||
|
|
} else {
|
|||
|
|
buffer = buffer + ch // ← Carrier 2 update
|
|||
|
|
escaped = false // ← Carrier 1 update
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
i = i + 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return buffer
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**AST 構造**:
|
|||
|
|
```
|
|||
|
|
Loop {
|
|||
|
|
condition: BinOp(i < len AND s[i] != '"')
|
|||
|
|
body: [
|
|||
|
|
LocalVar { name: "ch", init: ArrayAccess(s, i) }, // body-local
|
|||
|
|
If {
|
|||
|
|
condition: Compare(ch == '\\'),
|
|||
|
|
then: [
|
|||
|
|
Assign { lhs: "escaped", rhs: BoolLiteral(true) }
|
|||
|
|
],
|
|||
|
|
else: [
|
|||
|
|
Assign { lhs: "buffer", rhs: BinOp(buffer + ch) },
|
|||
|
|
Assign { lhs: "escaped", rhs: BoolLiteral(false) }
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
Assign { lhs: "i", rhs: BinOp(i + 1) }
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**キャリア分析**:
|
|||
|
|
| Carrier | Type | Update Pattern | Then Branch | Else Branch |
|
|||
|
|
|---------|------|----------------|-------------|-------------|
|
|||
|
|
| `escaped` | BoolBox | Conditional flag | `true` | `false` |
|
|||
|
|
| `buffer` | StringBox | StringAppend | (unchanged) | `buffer + ch` |
|
|||
|
|
| `i` | IntegerBox | CounterLike | `i + 1` | `i + 1` |
|
|||
|
|
|
|||
|
|
**P3 で扱う範囲**:
|
|||
|
|
- ✅ `escaped`: 条件フラグ(両分岐で定義)
|
|||
|
|
- ✅ `buffer`: StringAppend(else のみ更新、then は不変)
|
|||
|
|
- ✅ `i`: ループ外で統一更新(P3 の外で処理)
|
|||
|
|
|
|||
|
|
**制約**:
|
|||
|
|
- `ch` は body-local 変数(Phase 191 で対応済み)
|
|||
|
|
- `s[i]` の配列アクセスは ConditionEnv で解決(`s` は outer local → Phase 200+)
|
|||
|
|
- **この Phase では `ch = "x"` のような定数で代替してテスト**
|
|||
|
|
|
|||
|
|
#### 候補 2: selfhost の if-sum パターン(優先度: 中)
|
|||
|
|
|
|||
|
|
**ループ構造**:
|
|||
|
|
```nyash
|
|||
|
|
// selfhost: 条件付き集計
|
|||
|
|
static box Aggregator {
|
|||
|
|
sum_positive(array) {
|
|||
|
|
local sum = 0
|
|||
|
|
local count = 0
|
|||
|
|
local i = 0
|
|||
|
|
|
|||
|
|
loop(i < array.length()) {
|
|||
|
|
if(array[i] > 0) {
|
|||
|
|
sum = sum + array[i] // ← Carrier 1 update
|
|||
|
|
count = count + 1 // ← Carrier 2 update
|
|||
|
|
}
|
|||
|
|
i = i + 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return sum
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**AST 構造**:
|
|||
|
|
```
|
|||
|
|
Loop {
|
|||
|
|
condition: Compare(i < array.length())
|
|||
|
|
body: [
|
|||
|
|
If {
|
|||
|
|
condition: Compare(array[i] > 0),
|
|||
|
|
then: [
|
|||
|
|
Assign { lhs: "sum", rhs: BinOp(sum + array[i]) },
|
|||
|
|
Assign { lhs: "count", rhs: BinOp(count + 1) }
|
|||
|
|
],
|
|||
|
|
else: [] // 空(更新なし)
|
|||
|
|
},
|
|||
|
|
Assign { lhs: "i", rhs: BinOp(i + 1) }
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**キャリア分析**:
|
|||
|
|
| Carrier | Type | Update Pattern | Then Branch | Else Branch |
|
|||
|
|
|---------|------|----------------|-------------|-------------|
|
|||
|
|
| `sum` | IntegerBox | NumberAccumulation | `sum + array[i]` | (unchanged) |
|
|||
|
|
| `count` | IntegerBox | CounterLike | `count + 1` | (unchanged) |
|
|||
|
|
| `i` | IntegerBox | CounterLike | `i + 1` | `i + 1` |
|
|||
|
|
|
|||
|
|
**P3 で扱う範囲**:
|
|||
|
|
- ✅ `sum`: NumberAccumulation(then のみ更新)
|
|||
|
|
- ✅ `count`: CounterLike(then のみ更新)
|
|||
|
|
- ✅ `i`: ループ外で統一更新
|
|||
|
|
|
|||
|
|
**制約**:
|
|||
|
|
- `array[i]` の配列アクセスは ConditionEnv で解決(`array` は outer local → Phase 200+)
|
|||
|
|
- **この Phase では `i` のような既存パラメータで代替してテスト**
|
|||
|
|
|
|||
|
|
### Phase 195 での選定
|
|||
|
|
|
|||
|
|
**優先順位 1**: 候補 1(_parse_string 簡易版)
|
|||
|
|
- 理由: JsonParser の実戦コード、flag + buffer の2キャリア
|
|||
|
|
- 簡略化: `ch = "x"` 定数で配列アクセス回避
|
|||
|
|
|
|||
|
|
**優先順位 2**: 候補 2(if-sum)
|
|||
|
|
- 理由: selfhost の典型パターン、sum + count の2キャリア
|
|||
|
|
- 簡略化: `i` のみで配列アクセス回避
|
|||
|
|
|
|||
|
|
**成果物**:
|
|||
|
|
- 選定したループの詳細 AST 構造記録(本セクション)
|
|||
|
|
- キャリア分析表(UpdateKind 分類済み)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Task 195-2: LoopUpdateSummary / CarrierInfo の設計整理
|
|||
|
|
|
|||
|
|
### 現状の P3 サポート(Phase 170-189)
|
|||
|
|
|
|||
|
|
**既存の P3 は単一キャリアのみ対応**:
|
|||
|
|
```rust
|
|||
|
|
// 既存の P3 ケース
|
|||
|
|
if(cond) {
|
|||
|
|
sum = sum + i // ← 単一キャリア "sum"
|
|||
|
|
} else {
|
|||
|
|
sum = sum - i
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**LoopUpdateSummary / CarrierInfo の構造**:
|
|||
|
|
```rust
|
|||
|
|
pub struct CarrierInfo {
|
|||
|
|
pub carriers: Vec<String>, // キャリア名リスト
|
|||
|
|
pub updates: HashMap<String, ASTNode>, // name → update式
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
pub struct LoopUpdateSummary {
|
|||
|
|
pub kind: UpdateKind, // CounterLike | NumberAccumulation | ...
|
|||
|
|
// ...
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Phase 195 で扱う「複数キャリア + 条件付き更新」
|
|||
|
|
|
|||
|
|
**拡張要件**:
|
|||
|
|
```rust
|
|||
|
|
// Phase 195 の P3 ケース
|
|||
|
|
if(cond) {
|
|||
|
|
escaped = true // ← Carrier 1
|
|||
|
|
// buffer は更新なし(不変)
|
|||
|
|
} else {
|
|||
|
|
buffer = buffer + ch // ← Carrier 2
|
|||
|
|
escaped = false // ← Carrier 1
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**設計原則**:
|
|||
|
|
1. **両分岐で同じ Carrier が必ず定義**されること(PHI 生成の前提)
|
|||
|
|
- 片方の分岐で更新なし(不変)の場合、明示的に `carrier = carrier` を挿入
|
|||
|
|
- OR: PHI 生成時に「更新なし = 前の値を使う」として扱う
|
|||
|
|
|
|||
|
|
2. **各 Carrier の update 式は既存 UpdateKind 範囲内**:
|
|||
|
|
- CounterLike: `count + 1`, `count - 1`
|
|||
|
|
- NumberAccumulation: `sum + i`, `sum * base + addend`
|
|||
|
|
- StringAppend: `buffer + ch`
|
|||
|
|
- BoolFlag: `true`, `false`(新規 UpdateKind 候補)
|
|||
|
|
|
|||
|
|
3. **CarrierInfo は複数 Carrier を同時に保持**:
|
|||
|
|
```rust
|
|||
|
|
CarrierInfo {
|
|||
|
|
carriers: vec!["escaped", "buffer"],
|
|||
|
|
updates: {
|
|||
|
|
"escaped": ..., // then/else で異なる式
|
|||
|
|
"buffer": ...,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 制約の明確化
|
|||
|
|
|
|||
|
|
**P3 で扱う Carrier の制約** (Phase 195):
|
|||
|
|
- ✅ if-else の**両分岐で同じ Carrier が定義**される(明示的 or 不変)
|
|||
|
|
- ✅ 各 update 式は**既存 UpdateKind に対応**する
|
|||
|
|
- ❌ MethodCall を含む update は Phase 193 の制約に従う(ループパラメータのみ)
|
|||
|
|
- ❌ 外部ローカル変数(`digits` 等)は Phase 200+ に保留
|
|||
|
|
|
|||
|
|
**例(OK)**:
|
|||
|
|
```nyash
|
|||
|
|
if(ch == '\\') {
|
|||
|
|
escaped = true // ✅ BoolFlag
|
|||
|
|
} else {
|
|||
|
|
buffer = buffer + ch // ✅ StringAppend
|
|||
|
|
escaped = false // ✅ BoolFlag
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**例(NG - Phase 195 範囲外)**:
|
|||
|
|
```nyash
|
|||
|
|
if(cond) {
|
|||
|
|
digit = digits.indexOf(ch) // ❌ 外部ローカル (Phase 200+)
|
|||
|
|
sum = sum + digit
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 成果物
|
|||
|
|
- 複数キャリア対応の設計原則(本セクション)
|
|||
|
|
- UpdateKind 拡張候補(BoolFlag)の検討
|
|||
|
|
- CarrierInfo 構造の拡張仕様(擬似コード)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Task 195-3: Pattern3 lowerer の設計更新
|
|||
|
|
|
|||
|
|
### 現状の P3 lowerer(Phase 170-189)
|
|||
|
|
|
|||
|
|
**単一キャリアの処理フロー**:
|
|||
|
|
```
|
|||
|
|
1. If-Else AST を検出
|
|||
|
|
2. Then/Else 各分岐で carrier 更新式を抽出
|
|||
|
|
3. JoinIR で Then/Else ブロック生成
|
|||
|
|
4. Merge 点で PHI 命令生成(1つの carrier のみ)
|
|||
|
|
5. ExitLine で PHI 結果を variable_map に接続
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Phase 195 での拡張設計
|
|||
|
|
|
|||
|
|
#### 1. 複数 Carrier の同時処理
|
|||
|
|
|
|||
|
|
**設計案**: 複数 PHI を同じ Merge 点で生成
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
// 擬似コード: Pattern3 lowerer 拡張
|
|||
|
|
|
|||
|
|
// Step 1: If-Else で更新される全 Carrier を収集
|
|||
|
|
let carriers_in_then = extract_carriers(&if_node.then_branch);
|
|||
|
|
let carriers_in_else = extract_carriers(&if_node.else_branch);
|
|||
|
|
let all_carriers = carriers_in_then.union(&carriers_in_else);
|
|||
|
|
|
|||
|
|
// Step 2: 各 Carrier について Then/Else の update 式を取得
|
|||
|
|
let mut carrier_updates = HashMap::new();
|
|||
|
|
for carrier in all_carriers {
|
|||
|
|
let then_update = get_update_or_unchanged(&if_node.then_branch, carrier);
|
|||
|
|
let else_update = get_update_or_unchanged(&if_node.else_branch, carrier);
|
|||
|
|
carrier_updates.insert(carrier, (then_update, else_update));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Step 3: JoinIR で Then/Else ブロック生成(複数 update を emit)
|
|||
|
|
let then_block = emit_then_branch(&carrier_updates);
|
|||
|
|
let else_block = emit_else_branch(&carrier_updates);
|
|||
|
|
|
|||
|
|
// Step 4: Merge 点で複数 PHI 生成
|
|||
|
|
let merge_block = create_merge_block();
|
|||
|
|
for (carrier, (then_val, else_val)) in carrier_updates {
|
|||
|
|
let phi_result = emit_phi(merge_block, then_val, else_val);
|
|||
|
|
// ExitLine で variable_map に接続
|
|||
|
|
exit_line.connect(carrier, phi_result);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 2. PhiGroupBox vs ExitLine/CarrierInfo 拡張
|
|||
|
|
|
|||
|
|
**Option A: PhiGroupBox(新規箱)**
|
|||
|
|
```rust
|
|||
|
|
pub struct PhiGroupBox {
|
|||
|
|
pub phis: Vec<PhiInfo>, // 複数 PHI の束
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
pub struct PhiInfo {
|
|||
|
|
pub carrier_name: String,
|
|||
|
|
pub then_value: ValueId,
|
|||
|
|
pub else_value: ValueId,
|
|||
|
|
pub result: ValueId,
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**メリット**:
|
|||
|
|
- 複数 PHI の関係性を明示的に管理
|
|||
|
|
- 単一責任の原則(PHI グループ専用)
|
|||
|
|
|
|||
|
|
**デメリット**:
|
|||
|
|
- 新規箱の追加(複雑度増加)
|
|||
|
|
- 既存の ExitLine との統合が必要
|
|||
|
|
|
|||
|
|
**Option B: ExitLine/CarrierInfo 拡張(既存箱再利用)**
|
|||
|
|
```rust
|
|||
|
|
// 既存の ExitLine を拡張
|
|||
|
|
pub struct ExitLine {
|
|||
|
|
pub phi_connections: HashMap<String, ValueId>, // carrier → PHI result
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// CarrierInfo は既に複数 Carrier 対応
|
|||
|
|
pub struct CarrierInfo {
|
|||
|
|
pub carriers: Vec<String>,
|
|||
|
|
pub updates: HashMap<String, ASTNode>,
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**メリット**:
|
|||
|
|
- 新規箱なし(既存インフラ再利用)
|
|||
|
|
- ExitLine が既に複数 Carrier 接続をサポート
|
|||
|
|
|
|||
|
|
**デメリット**:
|
|||
|
|
- ExitLine の責務が拡大
|
|||
|
|
|
|||
|
|
**Phase 195 での判断**:
|
|||
|
|
→ **Option B(既存箱拡張)を採用**
|
|||
|
|
|
|||
|
|
**理由**:
|
|||
|
|
- ExitLine は既に「variable_map への接続」を担当
|
|||
|
|
- 複数 Carrier → 複数 PHI は自然な拡張
|
|||
|
|
- 新規箱を作る必要性が低い(YAGNI 原則)
|
|||
|
|
|
|||
|
|
#### 3. 更新なし(不変)の扱い
|
|||
|
|
|
|||
|
|
**ケース**: 片方の分岐で Carrier が更新されない
|
|||
|
|
|
|||
|
|
```nyash
|
|||
|
|
if(ch == '\\') {
|
|||
|
|
escaped = true
|
|||
|
|
// buffer は更新なし(不変)
|
|||
|
|
} else {
|
|||
|
|
buffer = buffer + ch
|
|||
|
|
escaped = false
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**設計案**: PHI 生成時に「前の値」を使用
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
// Then 分岐で buffer 更新なし
|
|||
|
|
let then_buffer_value = previous_buffer_value; // ← ループ header の PHI param
|
|||
|
|
|
|||
|
|
// Else 分岐で buffer 更新あり
|
|||
|
|
let else_buffer_value = emit_string_append(...);
|
|||
|
|
|
|||
|
|
// Merge 点で PHI
|
|||
|
|
let buffer_phi = emit_phi(merge, then_buffer_value, else_buffer_value);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**実装詳細**:
|
|||
|
|
- `get_update_or_unchanged()` 関数で検出
|
|||
|
|
- 更新なし → `ValueId` として「前の値」を返す
|
|||
|
|
- PHI 生成時に自動的に接続
|
|||
|
|
|
|||
|
|
### 成果物
|
|||
|
|
- Pattern3 lowerer の擬似コード(本セクション)
|
|||
|
|
- PhiGroupBox vs ExitLine 拡張の判断(Option B 採用)
|
|||
|
|
- 更新なし(不変)の扱い方設計
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Task 195-4: 実装スコープの決定(どこまでやるか)
|
|||
|
|
|
|||
|
|
### Phase 195-impl の範囲
|
|||
|
|
|
|||
|
|
**✅ Phase 195-impl で実装する**:
|
|||
|
|
1. **複数 Carrier の P3 処理**(2-3個程度)
|
|||
|
|
- `escaped` + `buffer` のような flag + accumulation
|
|||
|
|
- `sum` + `count` のような accumulation + counter
|
|||
|
|
|
|||
|
|
2. **既存 UpdateKind 範囲内の update**:
|
|||
|
|
- CounterLike: `count + 1`
|
|||
|
|
- NumberAccumulation: `sum + i`
|
|||
|
|
- StringAppend: `buffer + ch`
|
|||
|
|
- BoolFlag: `true`, `false`(新規 UpdateKind 追加候補)
|
|||
|
|
|
|||
|
|
3. **両分岐での定義確認**:
|
|||
|
|
- Then/Else で同じ Carrier が定義されるケース
|
|||
|
|
- 更新なし(不変)の場合は自動的に「前の値」を使用
|
|||
|
|
|
|||
|
|
4. **E2E テスト**:
|
|||
|
|
- 簡易版 _parse_string(`ch = "x"` 定数版)
|
|||
|
|
- 簡易版 if-sum(配列アクセスなし版)
|
|||
|
|
|
|||
|
|
**❌ Phase 195-impl で実装しない**:
|
|||
|
|
1. **LoopBodyLocal + MethodCall 混在**:
|
|||
|
|
- `local ch = s[i]; if(...) { buf += ch }`
|
|||
|
|
- → Phase 191(body-local init)と Phase 193(MethodCall)の組み合わせ
|
|||
|
|
- → Phase 195+ に延期(複雑度高い)
|
|||
|
|
|
|||
|
|
2. **外部ローカル変数(ConditionEnv 拡張)**:
|
|||
|
|
- `local digits = "012..."; digit = digits.indexOf(ch)`
|
|||
|
|
- → Phase 200+ に保留(設計判断済み)
|
|||
|
|
|
|||
|
|
3. **ネストした If**:
|
|||
|
|
- `if(...) { if(...) { ... } }`
|
|||
|
|
- → Phase 196+ に延期(P3 の P3)
|
|||
|
|
|
|||
|
|
### ゴールの明確化
|
|||
|
|
|
|||
|
|
**Phase 195-impl のゴール**:
|
|||
|
|
> 「Named carrier が 2-3 個あっても P3 lowerer が壊れない」こと。
|
|||
|
|
>
|
|||
|
|
> JsonParser/simple selfhost で「flag + count」くらいの例が通ること。
|
|||
|
|
|
|||
|
|
**成功基準**:
|
|||
|
|
- [ ] 簡易版 _parse_string が JoinIR で動作(escaped + buffer)
|
|||
|
|
- [ ] 簡易版 if-sum が JoinIR で動作(sum + count)
|
|||
|
|
- [ ] 既存テスト(phase190-194)が退行しない
|
|||
|
|
- [ ] ドキュメント更新(Implementation Status セクション追加)
|
|||
|
|
|
|||
|
|
### テストケース設計
|
|||
|
|
|
|||
|
|
#### テスト 1: flag + buffer (BoolFlag + StringAppend)
|
|||
|
|
|
|||
|
|
**ファイル**: `apps/tests/phase195_flag_buffer.hako`
|
|||
|
|
|
|||
|
|
```nyash
|
|||
|
|
static box Main {
|
|||
|
|
main() {
|
|||
|
|
local buffer = ""
|
|||
|
|
local escaped = false
|
|||
|
|
local i = 0
|
|||
|
|
|
|||
|
|
loop(i < 3) {
|
|||
|
|
local ch = "a" // ← 定数(配列アクセス回避)
|
|||
|
|
|
|||
|
|
if(i == 1) {
|
|||
|
|
escaped = true
|
|||
|
|
} else {
|
|||
|
|
buffer = buffer + ch
|
|||
|
|
escaped = false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
i = i + 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
print(buffer) // Expected: "aa" (i=0,2 で追加)
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### テスト 2: sum + count (NumberAccumulation + CounterLike)
|
|||
|
|
|
|||
|
|
**ファイル**: `apps/tests/phase195_sum_count.hako`
|
|||
|
|
|
|||
|
|
```nyash
|
|||
|
|
static box Main {
|
|||
|
|
main() {
|
|||
|
|
local sum = 0
|
|||
|
|
local count = 0
|
|||
|
|
local i = 0
|
|||
|
|
|
|||
|
|
loop(i < 5) {
|
|||
|
|
if(i > 2) {
|
|||
|
|
sum = sum + i // i=3,4 で加算
|
|||
|
|
count = count + 1
|
|||
|
|
}
|
|||
|
|
i = i + 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
print(sum) // Expected: 7 (3+4)
|
|||
|
|
print(count) // Expected: 2
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Task 195-5: CURRENT_TASK / overview 更新
|
|||
|
|
|
|||
|
|
### CURRENT_TASK.md 更新内容
|
|||
|
|
|
|||
|
|
```markdown
|
|||
|
|
## Phase 195: Pattern 3 拡張(設計フェーズ)(完了予定: 2025-12-XX)
|
|||
|
|
|
|||
|
|
**目的**: P3(If-Else PHI)を複数キャリア対応に拡張する設計
|
|||
|
|
|
|||
|
|
**タスク**:
|
|||
|
|
- [ ] 195-1: 対象ループ絞り込み(_parse_string 簡易版、if-sum)
|
|||
|
|
- [ ] 195-2: LoopUpdateSummary/CarrierInfo 設計整理(複数キャリア対応)
|
|||
|
|
- [ ] 195-3: Pattern3 lowerer 設計更新(PhiGroup vs ExitLine 拡張判断)
|
|||
|
|
- [ ] 195-4: 実装スコープ決定(2-3キャリア、既存 UpdateKind 範囲内)
|
|||
|
|
- [ ] 195-5: ドキュメント更新(本項目 + overview 更新)
|
|||
|
|
|
|||
|
|
**設計判断**:
|
|||
|
|
- ✅ ExitLine 拡張で対応(PhiGroupBox は作らない)
|
|||
|
|
- ✅ 両分岐での Carrier 定義確認(更新なし = 前の値使用)
|
|||
|
|
- ❌ ConditionEnv 拡張なし(Phase 200+ 保留)
|
|||
|
|
- ❌ LoopBodyLocal + MethodCall 混在は Phase 195+ 延期
|
|||
|
|
|
|||
|
|
**期待成果**:
|
|||
|
|
- phase195-pattern3-extension-design.md(完全設計書)
|
|||
|
|
- Phase 195-impl の実装スコープ明確化
|
|||
|
|
- JsonParser カバレッジ 40% → 60% への道筋
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### joinir-architecture-overview.md 更新内容
|
|||
|
|
|
|||
|
|
Section 7.2 "残タスク" に追記:
|
|||
|
|
|
|||
|
|
```markdown
|
|||
|
|
- [ ] **Phase 195**: Pattern 3 拡張(複数キャリア対応)
|
|||
|
|
- 設計フェーズ: P3 で 2-3 個の Carrier を同時処理
|
|||
|
|
- ExitLine 拡張で複数 PHI 生成(PhiGroupBox は不要)
|
|||
|
|
- 対象: _parse_string(flag+buffer)、if-sum(sum+count)
|
|||
|
|
- Phase 195-impl で実装予定
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 成功基準(設計フェーズ)
|
|||
|
|
|
|||
|
|
- [x] 対象ループ選定完了(_parse_string 簡易版、if-sum)
|
|||
|
|
- [x] キャリア分析表作成(UpdateKind 分類済み)
|
|||
|
|
- [x] 複数キャリア対応の設計原則明確化
|
|||
|
|
- [x] Pattern3 lowerer の擬似コード作成
|
|||
|
|
- [x] PhiGroupBox vs ExitLine 拡張の判断(Option B 採用)
|
|||
|
|
- [x] 実装スコープ決定(Phase 195-impl 範囲明確化)
|
|||
|
|
- [x] テストケース設計(2ケース)
|
|||
|
|
- [x] ドキュメント更新計画作成
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 関連ファイル
|
|||
|
|
|
|||
|
|
### 設計対象
|
|||
|
|
- `src/mir/builder/control_flow/joinir/patterns/pattern3_with_if.rs`(Phase 195-impl で実装)
|
|||
|
|
- `src/mir/join_ir/lowering/loop_update_summary.rs`(BoolFlag UpdateKind 追加候補)
|
|||
|
|
- `src/mir/join_ir/lowering/carrier_info.rs`(複数 Carrier 対応確認)
|
|||
|
|
|
|||
|
|
### テストファイル(Phase 195-impl で作成)
|
|||
|
|
- `apps/tests/phase195_flag_buffer.hako`(BoolFlag + StringAppend)
|
|||
|
|
- `apps/tests/phase195_sum_count.hako`(NumberAccumulation + CounterLike)
|
|||
|
|
|
|||
|
|
### ドキュメント
|
|||
|
|
- `docs/development/current/main/phase195-pattern3-extension-design.md`(本ファイル)
|
|||
|
|
- `docs/development/current/main/joinir-architecture-overview.md`(更新予定)
|
|||
|
|
- `CURRENT_TASK.md`(Phase 195 設計タスク追加)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 次のステップ
|
|||
|
|
|
|||
|
|
### Phase 195-impl: Pattern3 拡張実装
|
|||
|
|
|
|||
|
|
設計書(本ファイル)に基づいて実装:
|
|||
|
|
1. Pattern3 lowerer に複数 Carrier 処理追加
|
|||
|
|
2. ExitLine で複数 PHI 接続
|
|||
|
|
3. BoolFlag UpdateKind 追加(必要に応じて)
|
|||
|
|
4. E2E テスト(phase195_flag_buffer.hako, phase195_sum_count.hako)
|
|||
|
|
5. ドキュメント更新(Implementation Status セクション)
|
|||
|
|
|
|||
|
|
### Phase 196+: 候補
|
|||
|
|
|
|||
|
|
- Pattern 3 のネスト対応(if-in-if)
|
|||
|
|
- LoopBodyLocal + MethodCall + P3 の統合
|
|||
|
|
- JsonParser の _parse_string 完全版(配列アクセス対応 = Phase 200+)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 設計原則(Phase 195)
|
|||
|
|
|
|||
|
|
1. **既存箱再利用**:
|
|||
|
|
- PhiGroupBox を作らず、ExitLine/CarrierInfo を拡張
|
|||
|
|
- YAGNI(You Aren't Gonna Need It)原則
|
|||
|
|
|
|||
|
|
2. **段階的拡張**:
|
|||
|
|
- 単一キャリア(Phase 170-189)→ 複数キャリア(Phase 195)
|
|||
|
|
- 2-3 個程度に限定(無理に全部対応しない)
|
|||
|
|
|
|||
|
|
3. **Fail-Fast 継承**:
|
|||
|
|
- ConditionEnv 拡張なし(Phase 200+ 保留)
|
|||
|
|
- 複雑なパターンは Phase 195+ に延期
|
|||
|
|
|
|||
|
|
4. **箱理論の実践**:
|
|||
|
|
- 設計フェーズで構造を固める
|
|||
|
|
- 実装フェーズは lowerer のみに集中
|
|||
|
|
- ドキュメント駆動開発
|