Extends DigitPos promotion to generate TWO carriers for Pattern A/B support: - Boolean carrier (is_digit_pos) for break conditions - Integer carrier (digit_value) for NumberAccumulation ## Implementation 1. **DigitPosPromoter** (loop_body_digitpos_promoter.rs) - Generates dual carriers: is_<var> (bool) + <base>_value (int) - Smart naming: "digit_pos" → "digit" (removes "_pos" suffix) 2. **UpdateEnv** (update_env.rs) - Context-aware promoted variable resolution - Priority: <base>_value (int) → is_<var> (bool) → standard - Pass promoted_loopbodylocals from CarrierInfo 3. **Integration** (loop_with_break_minimal.rs) - UpdateEnv constructor updated to pass promoted list ## Test Results - **Before**: 925 tests PASS - **After**: 931 tests PASS (+6 new tests, 0 failures) ## New Tests - test_promoted_variable_resolution_digit_pos - Full dual-value - test_promoted_variable_resolution_fallback_to_bool - Fallback - test_promoted_variable_not_a_carrier - Error handling ## Impact | Pattern | Before | After | |---------|--------|-------| | _parse_number | ✅ Works (bool only) | ✅ Works (bool used, int unused) | | _atoi | ❌ Failed (missing int) | ✅ READY (int carrier available!) | 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
554 lines
16 KiB
Markdown
554 lines
16 KiB
Markdown
# Phase 247-EX: DigitPos Dual-Value Design
|
||
|
||
Status: Active
|
||
Scope: DigitPos パターンを「1つの質問→2つの値」に整理し、break 条件と NumberAccumulation 両方に対応。
|
||
|
||
## 1. DigitPos の二重値化モデル
|
||
|
||
### 1.1 問題の本質
|
||
|
||
**Phase 224 の現状**:
|
||
```rust
|
||
// Phase 224: digit_pos (integer) → is_digit_pos (boolean) に変換
|
||
digit_pos = digits.indexOf(ch) // -1 or 0-9
|
||
↓ (DigitPosPromoter)
|
||
is_digit_pos (boolean carrier) // indexOf() の成否のみ
|
||
```
|
||
|
||
**何が失われるか**:
|
||
- indexOf() の戻り値(0-9 の digit 値)が消える
|
||
- break 条件 `digit_pos < 0` は `!is_digit_pos` で対応できる(Phase 224-E で実装済み)
|
||
- しかし NumberAccumulation `result = result * 10 + digit_pos` で digit 値が必要!
|
||
|
||
**具体例**:
|
||
```nyash
|
||
// _atoi ループ (_parse_number も同様)
|
||
loop(i < n) {
|
||
local ch = s.substring(i, i+1)
|
||
local digit_pos = digits.indexOf(ch) // -1 (not found) or 0-9 (digit value)
|
||
|
||
if digit_pos < 0 { break } // Break 用途: 成否判定(boolean)
|
||
|
||
v = v * 10 + digit_pos // Accumulation 用途: digit 値(integer)
|
||
i = i + 1
|
||
}
|
||
```
|
||
|
||
### 1.2 解決: 二重値化アーキテクチャ
|
||
|
||
```
|
||
digit_pos = digits.indexOf(ch)
|
||
↓ (DigitPosPromoter 拡張)
|
||
|
||
Output A: is_digit_pos (boolean carrier) ← break 条件用
|
||
Output B: digit_value (integer carrier) ← accumulation 用
|
||
```
|
||
|
||
**質問と出力の対応**:
|
||
- **質問**: この ch は digits に含まれているか? index はいくつか?
|
||
- **出力A**: `is_digit_pos: bool` - 含まれているか(condition 側)
|
||
- **出力B**: `digit_value: i64` - index 値(accumulation 側)
|
||
|
||
**両方を CarrierInfo に含める**:
|
||
```rust
|
||
// Phase 247-EX: DigitPos dual-value promotion
|
||
CarrierInfo {
|
||
carriers: vec![
|
||
CarrierVar {
|
||
name: "is_digit_pos",
|
||
role: CarrierRole::ConditionOnly, // Exit PHI 不要
|
||
init: CarrierInit::BoolConst(false),
|
||
},
|
||
CarrierVar {
|
||
name: "digit_value",
|
||
role: CarrierRole::LoopState, // Exit PHI 必要
|
||
init: CarrierInit::FromHost,
|
||
},
|
||
],
|
||
promoted_loopbodylocals: vec!["digit_pos".to_string()],
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 箱の責務分割
|
||
|
||
### 2.1 DigitPosPromoter (拡張)
|
||
|
||
**Phase 224 の責務**:
|
||
- Input: `digit_pos = digits.indexOf(ch)` (LoopBodyLocal)
|
||
- Output: `is_digit_pos` (boolean carrier)
|
||
|
||
**Phase 247-EX の拡張**:
|
||
- Input: 同上
|
||
- Output:
|
||
- `is_digit_pos` (boolean carrier) - 既存
|
||
- `digit_value` (integer carrier) - **新規追加**
|
||
|
||
**実装方針**:
|
||
```rust
|
||
// Phase 247-EX: Dual-value carrier creation
|
||
let promoted_carrier_bool = CarrierVar {
|
||
name: format!("is_{}", var_in_cond), // "is_digit_pos"
|
||
host_id: ValueId(0),
|
||
join_id: None,
|
||
role: CarrierRole::ConditionOnly,
|
||
init: CarrierInit::BoolConst(false),
|
||
};
|
||
|
||
let promoted_carrier_int = CarrierVar {
|
||
name: format!("{}_value", var_in_cond), // "digit_pos_value" or "digit_value"
|
||
host_id: ValueId(0),
|
||
join_id: None,
|
||
role: CarrierRole::LoopState,
|
||
init: CarrierInit::FromHost,
|
||
};
|
||
|
||
let mut carrier_info = CarrierInfo::with_carriers(
|
||
"__dummy_loop_var__".to_string(),
|
||
ValueId(0),
|
||
vec![promoted_carrier_bool, promoted_carrier_int],
|
||
);
|
||
```
|
||
|
||
### 2.2 CarrierInfo
|
||
|
||
**Phase 247-EX で含まれるもの**:
|
||
```rust
|
||
CarrierInfo {
|
||
carriers: vec![
|
||
// Boolean carrier (condition 用)
|
||
CarrierVar {
|
||
name: "is_digit_pos",
|
||
role: ConditionOnly,
|
||
init: BoolConst(false),
|
||
},
|
||
// Integer carrier (accumulation 用)
|
||
CarrierVar {
|
||
name: "digit_value",
|
||
role: LoopState,
|
||
init: FromHost,
|
||
},
|
||
],
|
||
promoted_loopbodylocals: vec!["digit_pos"],
|
||
}
|
||
```
|
||
|
||
**ScopeManager による解決**:
|
||
- Break 条件: `digit_pos` → `is_digit_pos` (ConditionEnv)
|
||
- Update 式: `digit_pos` → `digit_value` (UpdateEnv)
|
||
|
||
### 2.3 ConditionEnv / ExprLowerer
|
||
|
||
**Phase 224-E の既存動作**:
|
||
```rust
|
||
// Break condition: digit_pos < 0
|
||
// ↓ (DigitPosConditionNormalizer)
|
||
// !is_digit_pos
|
||
```
|
||
|
||
**Phase 247-EX で不変**:
|
||
- Break 条件は引き続き `is_digit_pos` を参照
|
||
- ConditionEnv は `digit_pos` → `is_digit_pos` を解決
|
||
|
||
### 2.4 UpdateEnv / NumberAccumulation
|
||
|
||
**Phase 247-EX の新規動作**:
|
||
```rust
|
||
// Update expression: v = v * 10 + digit_pos
|
||
// ↓ (UpdateEnv resolution)
|
||
// v = v * 10 + digit_value
|
||
```
|
||
|
||
**UpdateEnv の変更**:
|
||
- `digit_pos` 参照時に `digit_value` を解決
|
||
- NumberAccumulation 検出時に `digit_var: "digit_pos"` → `"digit_value"` に変換
|
||
|
||
**CarrierUpdateEmitter の変更**:
|
||
- `UpdateRhs::NumberAccumulation { digit_var }` で `digit_var` を UpdateEnv から解決
|
||
- UpdateEnv が既に `digit_pos` → `digit_value` を解決済みと仮定
|
||
|
||
---
|
||
|
||
## 3. 影響範囲(パターン別)
|
||
|
||
### 3.1 Pattern2: _parse_number
|
||
|
||
**ループ構造**:
|
||
```nyash
|
||
loop(p < s.length()) {
|
||
local ch = s.substring(p, p+1)
|
||
local digit_pos = digits.indexOf(ch)
|
||
|
||
if digit_pos < 0 { break } // Break 条件
|
||
|
||
num_str = num_str + ch // StringAppend (digit_pos 不使用)
|
||
p = p + 1
|
||
}
|
||
```
|
||
|
||
**変数の役割**:
|
||
| Variable | Type | Usage | Carrier Type |
|
||
|----------|------|-------|--------------|
|
||
| `p` | position | loop counter | LoopState |
|
||
| `num_str` | string | accumulator | LoopState |
|
||
| `ch` | string | LoopBodyLocal | - |
|
||
| `digit_pos` | integer | LoopBodyLocal | → `is_digit_pos` (ConditionOnly) + `digit_value` (unused) |
|
||
|
||
**Phase 247-EX での扱い**:
|
||
- Header: `p < s.length()` (ExprLowerer)
|
||
- Break: `digit_pos < 0` → `!is_digit_pos` (ConditionEnv + Normalizer)
|
||
- Update:
|
||
- `p = p + 1` (Increment)
|
||
- `num_str = num_str + ch` (StringAppend)
|
||
- **digit_value は生成されるが使用されない**(無駄だが害なし)
|
||
|
||
### 3.2 Pattern2: _atoi
|
||
|
||
**ループ構造**:
|
||
```nyash
|
||
loop(i < n) {
|
||
local ch = s.substring(i, i+1)
|
||
local pos = digits.indexOf(ch)
|
||
|
||
if pos < 0 { break } // Break 条件
|
||
|
||
v = v * 10 + pos // NumberAccumulation (pos 使用!)
|
||
i = i + 1
|
||
}
|
||
```
|
||
|
||
**変数の役割**:
|
||
| Variable | Type | Usage | Carrier Type |
|
||
|----------|------|-------|--------------|
|
||
| `i` | position | loop counter | LoopState |
|
||
| `v` | integer | number accumulator | LoopState |
|
||
| `ch` | string | LoopBodyLocal | - |
|
||
| `pos` | integer | LoopBodyLocal | → `is_pos` (ConditionOnly) + `pos_value` (LoopState) |
|
||
|
||
**Phase 247-EX での扱い**:
|
||
- Header: `i < n` (ExprLowerer)
|
||
- Break: `pos < 0` → `!is_pos` (ConditionEnv + Normalizer)
|
||
- Update:
|
||
- `i = i + 1` (Increment)
|
||
- `v = v * 10 + pos` → `v = v * 10 + pos_value` (NumberAccumulation)
|
||
- **両方の carrier が必要!**
|
||
|
||
### 3.3 Pattern2: _atof_loop [Future]
|
||
|
||
**ループ構造** (想定):
|
||
```nyash
|
||
loop(i < n) {
|
||
local ch = s.substring(i, i+1)
|
||
local digit_pos = digits.indexOf(ch)
|
||
|
||
if digit_pos < 0 { break }
|
||
|
||
result = result + digit_pos * place_value
|
||
place_value = place_value / 10
|
||
i = i + 1
|
||
}
|
||
```
|
||
|
||
**Phase 247-EX での扱い**:
|
||
- 同じ二重値化モデルが適用可能
|
||
- `digit_value` を乗算・累積に使用
|
||
|
||
---
|
||
|
||
## 4. 実装アーキテクチャ
|
||
|
||
### 4.1 データフロー図
|
||
|
||
```
|
||
DigitPosPromoter
|
||
├→ is_digit_pos (boolean)
|
||
│ ↓
|
||
│ ConditionEnv (Phase 245C capture)
|
||
│ ├→ ExprLowerer (break condition)
|
||
│ └→ is_digit_pos → boolean in scope
|
||
│
|
||
└→ digit_value (integer)
|
||
↓
|
||
UpdateEnv
|
||
├→ digit_pos reference → digit_value (integer)
|
||
└→ NumberAccumulation pattern resolution
|
||
```
|
||
|
||
### 4.2 命名規則
|
||
|
||
**Phase 247-EX の命名戦略**:
|
||
```rust
|
||
// Option A: Separate naming (推奨)
|
||
is_digit_pos // boolean carrier (condition 用)
|
||
digit_value // integer carrier (accumulation 用)
|
||
|
||
// Option B: Prefix naming
|
||
is_digit_pos // boolean
|
||
digit_pos_value // integer (冗長だが明示的)
|
||
|
||
// Option C: Short naming
|
||
is_digit_pos // boolean
|
||
digit_pos // integer (元の名前を保持、混乱の恐れ)
|
||
```
|
||
|
||
**採用**: Option A(Separate naming)
|
||
- 理由: 用途が明確、混乱が少ない、既存の `is_*` 命名規則と整合
|
||
|
||
### 4.3 ScopeManager の解決ルール
|
||
|
||
**ConditionEnv (break 条件)**:
|
||
```rust
|
||
// digit_pos 参照 → is_digit_pos (boolean carrier)
|
||
env.get("digit_pos") → Some(is_digit_pos_value_id)
|
||
```
|
||
|
||
**UpdateEnv (update 式)**:
|
||
```rust
|
||
// digit_pos 参照 → digit_value (integer carrier)
|
||
env.resolve("digit_pos") → Some(digit_value_value_id)
|
||
```
|
||
|
||
**実装方針**:
|
||
1. CarrierInfo に両方の carrier を登録
|
||
2. ScopeManager が context 依存で正しい carrier を返す
|
||
- ConditionEnv: `promoted_loopbodylocals` に含まれる名前 → `is_*` carrier
|
||
- UpdateEnv: `promoted_loopbodylocals` に含まれる名前 → `*_value` carrier
|
||
|
||
---
|
||
|
||
## 5. 責務の明確化
|
||
|
||
| Component | Input | Output | 用途 |
|
||
|-----------|-------|--------|------|
|
||
| **DigitPosPromoter** | indexOf() result | `is_digit_pos` + `digit_value` | 二重値化 |
|
||
| **CarrierInfo** | DigitPos values | Both as LoopState | キャリア登録 |
|
||
| **ConditionEnv** | Promoted carriers | `is_digit_pos` (bool) | Break 条件 |
|
||
| **UpdateEnv** | Promoted carriers | `digit_value` (i64) | Accumulation |
|
||
| **ExprLowerer** | Condition AST | boolean ValueId | Lowering |
|
||
| **DigitPosConditionNormalizer** | `digit_pos < 0` | `!is_digit_pos` | AST 変換 |
|
||
| **CarrierUpdateEmitter** | UpdateExpr | JoinIR instructions | Update 生成 |
|
||
|
||
**Phase 247-EX の変更箇所**:
|
||
- ✅ **DigitPosPromoter**: 2つの carrier を生成
|
||
- ✅ **CarrierInfo**: 両方を含める
|
||
- ✅ **ConditionEnv**: `digit_pos` → `is_digit_pos` 解決(既存)
|
||
- 🆕 **UpdateEnv**: `digit_pos` → `digit_value` 解決(新規)
|
||
- ✅ **DigitPosConditionNormalizer**: `digit_pos < 0` → `!is_digit_pos`(既存)
|
||
- 🆕 **CarrierUpdateEmitter**: `digit_value` を UpdateEnv から解決(新規)
|
||
|
||
---
|
||
|
||
## 6. テスト戦略
|
||
|
||
### 6.1 DigitPos Promoter 単体テスト
|
||
|
||
**既存テスト** (loop_body_digitpos_promoter.rs):
|
||
- ✅ Phase 224: `is_digit_pos` 側の生成を確認
|
||
- 🆕 Phase 247-EX: `digit_value` 側も生成されることを確認
|
||
|
||
**新規テスト**:
|
||
```rust
|
||
#[test]
|
||
fn test_digitpos_dual_value_carriers() {
|
||
// digit_pos = indexOf(ch) → is_digit_pos + digit_value
|
||
let result = DigitPosPromoter::try_promote(req);
|
||
match result {
|
||
Promoted { carrier_info, .. } => {
|
||
assert_eq!(carrier_info.carriers.len(), 2);
|
||
|
||
// Boolean carrier
|
||
let bool_carrier = &carrier_info.carriers[0];
|
||
assert_eq!(bool_carrier.name, "is_digit_pos");
|
||
assert_eq!(bool_carrier.role, CarrierRole::ConditionOnly);
|
||
|
||
// Integer carrier
|
||
let int_carrier = &carrier_info.carriers[1];
|
||
assert_eq!(int_carrier.name, "digit_value");
|
||
assert_eq!(int_carrier.role, CarrierRole::LoopState);
|
||
}
|
||
_ => panic!("Expected Promoted"),
|
||
}
|
||
}
|
||
```
|
||
|
||
### 6.2 _parse_number (Pattern2) E2E テスト
|
||
|
||
**テストファイル**: `apps/tests/phase189_parse_number_mini.hako`
|
||
|
||
**期待される動作**:
|
||
- Break 条件: `digit_pos < 0` が `is_digit_pos` で動作
|
||
- `digit_value` carrier は生成されるが未使用(害なし)
|
||
- String 累積(`num_str`)は別の carrier で処理
|
||
|
||
**検証コマンド**:
|
||
```bash
|
||
NYASH_JOINIR_DEBUG=1 ./target/release/hakorune apps/tests/phase189_parse_number_mini.hako
|
||
```
|
||
|
||
**期待出力**:
|
||
```
|
||
[digitpos_promoter] A-4 DigitPos pattern promoted: digit_pos → is_digit_pos + digit_value
|
||
[pattern2/lowering] Using promoted carrier: is_digit_pos (condition)
|
||
[pattern2/lowering] Carrier 'digit_value' registered but unused
|
||
```
|
||
|
||
### 6.3 _atoi (Pattern2) E2E テスト
|
||
|
||
**テストファイル**: `apps/tests/phase189_atoi_mini.hako` または `tests/phase246_json_atoi.rs`
|
||
|
||
**期待される動作**:
|
||
- Break 条件: `pos < 0` が `is_pos` で動作
|
||
- NumberAccumulation: `v = v * 10 + pos` が `v = v * 10 + pos_value` で動作
|
||
- 両方の carrier が正しく使用される
|
||
|
||
**検証コマンド**:
|
||
```bash
|
||
NYASH_JOINIR_DEBUG=1 cargo test --release phase246_json_atoi -- --nocapture
|
||
```
|
||
|
||
**期待出力**:
|
||
```
|
||
[digitpos_promoter] A-4 DigitPos pattern promoted: pos → is_pos + pos_value
|
||
[pattern2/lowering] Using promoted carrier: is_pos (condition)
|
||
[pattern2/lowering] Using promoted carrier: pos_value (accumulation)
|
||
[carrier_update] NumberAccumulation: digit_var='pos' resolved to pos_value
|
||
```
|
||
|
||
### 6.4 Regression テスト
|
||
|
||
**対象**:
|
||
- Phase 224 系テスト(DigitPos promotion 基本動作)
|
||
- Phase 245 系テスト(_parse_number 系)
|
||
- Phase 246 系テスト(_atoi 系)
|
||
|
||
**確認項目**:
|
||
- 既存の `is_digit_pos` carrier が引き続き動作
|
||
- 新規の `digit_value` carrier が追加されても退行なし
|
||
|
||
---
|
||
|
||
## 7. 成功条件
|
||
|
||
### 7.1 ビルド
|
||
|
||
- [ ] `cargo build --release` 成功(0 errors, 0 warnings)
|
||
|
||
### 7.2 単体テスト
|
||
|
||
- [ ] DigitPosPromoter テスト: 2つの carrier 生成を確認
|
||
- [ ] CarrierInfo テスト: 両方の carrier が含まれることを確認
|
||
- [ ] UpdateEnv テスト: `digit_pos` → `digit_value` 解決を確認
|
||
|
||
### 7.3 E2E テスト
|
||
|
||
- [ ] _parse_number: `digit_value` 未使用でも正常動作
|
||
- [ ] _atoi: `digit_value` を NumberAccumulation で使用
|
||
- [ ] Phase 224/245/246 既存テスト: 退行なし
|
||
|
||
### 7.4 ドキュメント
|
||
|
||
- [ ] このドキュメント完成
|
||
- [ ] 箱の責務が明確に書かれている
|
||
- [ ] テスト戦略が具体的
|
||
|
||
---
|
||
|
||
## 8. 将来への展開
|
||
|
||
### 8.1 他のパターンへの適用
|
||
|
||
同じ二重値化モデルを以下に適用可能:
|
||
|
||
**_atof_loop** (小数パース):
|
||
- `digit_pos` → `is_digit_pos` + `digit_value`
|
||
- 小数点以下の桁数計算にも使用可能
|
||
|
||
**汎用的な indexOf パターン**:
|
||
- 任意の indexOf() 呼び出しで同様の二重値化を適用
|
||
- condition-only 用途と value 用途を自動判定
|
||
|
||
### 8.2 Pattern3/4 への拡張
|
||
|
||
**Pattern3 (if-sum)**:
|
||
- 複数の条件分岐でも同じ二重値化モデルを適用
|
||
- Exit PHI で両方の carrier を適切に扱う
|
||
|
||
**Pattern4 (continue)**:
|
||
- continue 条件でも boolean carrier を使用
|
||
- update 式で integer carrier を使用
|
||
|
||
### 8.3 最適化の可能性
|
||
|
||
**未使用 carrier の削除**:
|
||
- `_parse_number` のように `digit_value` が未使用の場合、最適化で削除可能
|
||
- CarrierInfo に "used" フラグを追加して不要な carrier を省略
|
||
|
||
**命名の統一化**:
|
||
- `digit_pos` → `digit_pos` (integer) + `is_digit_pos` (boolean)
|
||
- UpdateEnv/ConditionEnv が型情報から自動判定
|
||
|
||
---
|
||
|
||
## 9. Box-First 原則の適用
|
||
|
||
### 9.1 Single Responsibility
|
||
|
||
- **DigitPosPromoter**: indexOf パターン検出と二重値化のみ
|
||
- **CarrierInfo**: Carrier メタデータ保持のみ
|
||
- **ConditionEnv**: Condition 側の解決のみ
|
||
- **UpdateEnv**: Update 側の解決のみ
|
||
|
||
### 9.2 Fail-Safe Design
|
||
|
||
- 未使用の carrier も生成(害なし)
|
||
- 解決失敗時は明示的エラー(panic なし)
|
||
- 退行テストで既存動作を保証
|
||
|
||
### 9.3 Boundary Clarity
|
||
|
||
```
|
||
Input: digit_pos = indexOf(ch) (AST)
|
||
↓
|
||
Output: is_digit_pos (bool) + digit_value (i64) (CarrierInfo)
|
||
↓
|
||
Usage: ConditionEnv → is_digit_pos
|
||
UpdateEnv → digit_value
|
||
```
|
||
|
||
---
|
||
|
||
## 10. 参考資料
|
||
|
||
### 10.1 Phase ドキュメント
|
||
|
||
- **Phase 224**: `phase224-digitpos-promoter-design.md` - DigitPos promotion 基本設計
|
||
- **Phase 224-E**: `phase224-digitpos-condition-normalizer.md` - AST 変換設計
|
||
- **Phase 245**: `phase245-jsonparser-parse-number-joinir-integration.md` - _parse_number 統合
|
||
- **Phase 246**: `phase246-jsonparser-atoi-joinir-integration.md` - _atoi 統合
|
||
- **Phase 227**: CarrierRole 導入(LoopState vs ConditionOnly)
|
||
- **Phase 228**: CarrierInit 導入(FromHost vs BoolConst)
|
||
|
||
### 10.2 コード参照
|
||
|
||
- **DigitPosPromoter**: `src/mir/loop_pattern_detection/loop_body_digitpos_promoter.rs`
|
||
- **CarrierInfo**: `src/mir/join_ir/lowering/carrier_info.rs`
|
||
- **ConditionEnv**: `src/mir/join_ir/lowering/condition_env.rs`
|
||
- **UpdateEnv**: `src/mir/join_ir/lowering/update_env.rs`
|
||
- **CarrierUpdateEmitter**: `src/mir/join_ir/lowering/carrier_update_emitter.rs`
|
||
- **LoopUpdateAnalyzer**: `src/mir/join_ir/lowering/loop_update_analyzer.rs`
|
||
|
||
### 10.3 テストファイル
|
||
|
||
- **_parse_number**: `apps/tests/phase189_parse_number_mini.hako`, `tests/phase245_json_parse_number.rs`
|
||
- **_atoi**: `apps/tests/phase189_atoi_mini.hako`, `tests/phase246_json_atoi.rs`
|
||
- **DigitPos unit**: `src/mir/loop_pattern_detection/loop_body_digitpos_promoter.rs` (tests module)
|
||
|
||
---
|
||
|
||
## 11. Revision History
|
||
|
||
- **2025-12-11**: Phase 247-EX 設計ドキュメント作成
|
||
- **Status**: Active - 設計確定、実装準備完了
|
||
- **Scope**: DigitPos 二重値化設計(ExprLowerer + NumberAccumulation ライン)
|