feat(joinir): Phase P2-P4 Loop Pattern boxification

- P2: Create loop_patterns/ module structure
  - common.rs: shared helpers (ParsedProgram, LoopContext, etc.)
  - simple.rs: generic loop lowering
  - filter.rs, print_tokens.rs: delegate to simple
  - break_pattern.rs, continue_pattern.rs: new modules

- P3: Create LoopFrontendBinding layer
  - Separate function name detection from pattern lowering
  - detect_loop_pattern() for pattern classification

- P4: Break/Continue pattern migration
  - Move Break/Continue handling from loop_patterns_old.rs
  - Add LoopPattern::Break and LoopPattern::Continue variants

- Design: 3-layer architecture
  - LoopFrontendBinding: function name → LoopPattern
  - loop_patterns: LoopPattern → JoinIR
  - Bridge/VM: Box semantics

All 56 JoinIR tests pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-29 09:04:18 +09:00
parent fd83903f87
commit 3af98964ed
12 changed files with 1753 additions and 8 deletions

View File

@ -0,0 +1,230 @@
# Phase P2: Loop Pattern enum化 実装計画書
## 目次
1. [現状分析](#1-現状分析)
2. [LoopPattern enum 設計案](#2-looppattern-enum-設計案)
3. [モジュール構造案](#3-モジュール構造案)
4. [削減見込み](#4-削減見込み)
5. [実装ステップ](#5-実装ステップ)
6. [リスク評価](#6-リスク評価)
7. [テスト戦略](#7-テスト戦略)
8. [チェックリスト](#8-チェックリスト)
---
## 1. 現状分析
### 1.1 対象ファイル概要
**ファイル**: `src/mir/join_ir/frontend/ast_lowerer/loop_patterns.rs`
**行数**: 895行
**主要関数**:
1. `lower_loop_with_break_continue()` (43行) - **ディスパッチャー**
2. `lower_loop_case_a_simple()` (329行) - **Simple pattern**
3. `lower_loop_break_pattern()` (202行) - **Break pattern**
4. `lower_loop_continue_pattern()` (241行) - **Continue pattern**
### 1.2 ループパターンの分類
| パターン | 条件 | 関数 | 行数 | 複雑度 |
|---------|------|------|------|--------|
| **Simple** | `!has_break && !has_continue` | `lower_loop_case_a_simple()` | 329 | 高 |
| **Break** | `has_break && !has_continue` | `lower_loop_break_pattern()` | 202 | 中 |
| **Continue** | `has_continue && !has_break` | `lower_loop_continue_pattern()` | 241 | 中 |
| **Mixed** | `has_break && has_continue` | panic! | 0 | 未実装 |
### 1.3 P1 との比較
| 項目 | P1 (If Handler) | P2 (Loop Patterns) |
|------|-----------------|-------------------|
| **対象ファイル** | `stmt_handlers.rs` | `loop_patterns.rs` |
| **元の行数** | 154行 | 895行 |
| **パターン数** | 5個 | 4個1個未実装 |
| **最大パターン** | 57行 | 329行 |
| **共通処理** | 少ない | 多い(~85行 |
---
## 2. LoopPattern enum 設計案
```rust
/// ループパターンの分類
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LoopPattern {
/// Simple pattern: 条件付きループbreak/continue なし)
Simple {
cond_expr: Value,
has_me: bool,
external_refs: Vec<(String, ValueId)>,
},
/// Break pattern: 早期 return ループ
Break {
break_cond_expr: Value,
},
/// Continue pattern: 条件付きスキップループ
Continue {
loop_cond_expr: Value,
continue_cond_expr: Value,
},
/// Mixed pattern: break と continue 両方(未実装)
Mixed {
_marker: (),
},
}
```
---
## 3. モジュール構造案
```
src/mir/join_ir/frontend/ast_lowerer/
├── loop_patterns.rs (縮小版: ~60行)
└── loop_patterns/
├── mod.rs (~50行)
├── pattern.rs (~180行) - パターン検出ロジック
├── common.rs (~150行) - 共通処理ヘルパー
├── lowering/
│ ├── mod.rs (~30行)
│ ├── simple.rs (~180行)
│ ├── break_pattern.rs (~120行)
│ ├── continue_pattern.rs (~140行)
│ └── mixed.rs (~30行)
└── tests.rs (~150行)
```
---
## 4. 削減見込み
| カテゴリ | Before | After | 削減 | 削減率 |
|---------|--------|-------|------|--------|
| **loop_patterns.rs** | 895 | 60 | -835 | **-93.3%** |
| **新規モジュール** | 0 | 1,090 | +1,090 | - |
| **純増減** | 895 | 1,150 | +255 | +28.5% |
### P1 との比較
| 項目 | P1 (If Handler) | P2 (Loop Patterns) |
|------|-----------------|-------------------|
| **削減率** | 93.5% | 93.3% |
| **新規コスト** | +336行 (+218%) | +255行 (+28.5%) |
**P2 の優位性**: 共通処理の再利用により、新規コストが P1 より効率的
---
## 5. 実装ステップ
### Step 1: 基礎インフラ構築3-4時間
- ディレクトリ構造作成
- `loop_patterns/mod.rs` 作成
- `loop_patterns/pattern.rs` 実装LoopPattern enum + 検出ロジック)
- `loop_patterns/common.rs` 実装(共通処理ヘルパー)
### Step 2: 各パターン lowering 実装6-8時間
1. Break pattern (2時間)
2. Continue pattern (2.5時間)
3. Simple pattern (3時間) - 最複雑
4. Mixed pattern (0.5時間)
5. lowering/mod.rs 統合 (1時間)
### Step 3: loop_patterns.rs リファクタリング1-2時間
- 895行 → 60行に削減
- パターン検出 → lowering 委譲
### Step 4: テスト追加2-3時間
- パターン検出テスト: 10個
- 共通処理テスト: 5個
- lowering テスト: 12個
- 統合テスト: 5個
### Step 5: 統合・検証1-2時間
- 既存テスト全通過
- 回帰テスト11個の .hako ファイル)
**総推定時間**: 13-19時間
---
## 6. リスク評価
### 6.1 P1 より複雑な点
1. **Simple pattern の複雑性**
- me/external_refs の動的パラメータ計算
- P1 最大57行 vs P2 最大329行5.8倍)
2. **共通処理の量**
- 3関数構造生成entry/loop_step/k_exit
- P1: 共通処理ほぼなし vs P2: 150行
3. **Phase 52/56 機能の保持**
- me パラメータ対応
- external_refs 対応filter/map
### 6.2 回避策
環境変数制御による段階的移行P1 と同じ戦略)
---
## 7. テスト戦略
### 7.1 回帰テストリスト
```bash
# 基本ループ
apps/tests/loop_min_while.hako
apps/tests/joinir_min_loop.hako
# Break pattern
apps/tests/loopform_break_and_return.hako
apps/tests/nested_loop_inner_break_isolated.hako
# Continue pattern
apps/tests/loop-continue-demo/main.hako
apps/tests/loopform_continue_break_scan.hako
```
---
## 8. チェックリスト
### 基礎インフラ (Step 1)
- [ ] ディレクトリ構造作成完了
- [ ] `loop_patterns/mod.rs` 作成完了
- [ ] `loop_patterns/pattern.rs` 実装完了
- [ ] `loop_patterns/common.rs` 実装完了
### パターン lowering (Step 2)
- [ ] `lowering/break_pattern.rs` 実装完了
- [ ] `lowering/continue_pattern.rs` 実装完了
- [ ] `lowering/simple.rs` 実装完了
- [ ] `lowering/mixed.rs` 実装完了
### リファクタリング (Step 3)
- [ ] `loop_patterns.rs` 修正完了895行 → 60行
- [ ] 既存テスト全通過
### テスト (Step 4)
- [ ] パターン検出テスト10個
- [ ] 共通処理テスト5個
- [ ] lowering テスト12個
### 統合・検証 (Step 5)
- [ ] 全ユニットテスト通過
- [ ] 既存回帰テスト通過
- [ ] パフォーマンス劣化なし
---
**作成日**: 2025-11-29
**Phase**: P2 (Loop Pattern enum化)
**前提**: P1 (If Handler 箱化モジュール化) 完了
**作成者**: Claude Code + Task agent (Plan mode)

View File

@ -0,0 +1,146 @@
//! Phase P3: LoopFrontendBinding - 関数名 → LoopPattern 変換層
//!
//! ## 責務1行で表現
//! **関数名から適切な LoopPattern を決定し、loop_patterns 層にディスパッチする**
//!
//! ## この層の責務
//!
//! - 関数名("simple", "filter" 等)から `LoopPattern` enum を決定
//! - Break/Continue の有無を検出してパターンを調整
//! - `loop_patterns::lower_loop_with_pattern()` に委譲
//!
//! ## やらないこと
//!
//! - JSON body の詳細解析(それは loop_patterns 層)
//! - Box 名・メソッド名での判定(それは Bridge/VM 層)
//!
//! ## 設計原則
//!
//! ChatGPT 推奨の責務分離:
//! ```
//! LoopFrontendBinding → 関数名ベースの判定
//! loop_patterns → LoopPattern enum での lowering
//! Bridge/VM → Box 名・メソッド名での最適化
//! ```
use super::loop_patterns::{self, LoopPattern, LoweringError};
use super::loop_patterns_old;
use super::{AstToJoinIrLowerer, JoinModule};
/// 関数名から LoopPattern を検出
///
/// # Arguments
/// * `func_name` - 関数名
/// * `loop_body` - Loop body の JSONBreak/Continue 検出用)
///
/// # Returns
/// 検出された LoopPattern
pub fn detect_loop_pattern(
func_name: &str,
loop_body: Option<&[serde_json::Value]>,
) -> LoopPattern {
// Phase P3: 関数名ベースの判定
// 将来的には Box 名やアノテーションも考慮可能
match func_name {
// Phase 55: PrintTokens パターン
"print_tokens" => LoopPattern::PrintTokens,
// Phase 56: Filter パターン
"filter" => LoopPattern::Filter,
// Phase 57+: Map パターン(未実装)
"map" => LoopPattern::Map,
// Phase 58+: Reduce パターン(未実装)
"reduce" | "fold" => LoopPattern::Reduce,
// デフォルト: Simple パターン
// ただし Break/Continue があれば別パターン
_ => {
if let Some(body) = loop_body {
if AstToJoinIrLowerer::has_break_in_loop_body(body) {
LoopPattern::Break
} else if AstToJoinIrLowerer::has_continue_in_loop_body(body) {
LoopPattern::Continue
} else {
LoopPattern::Simple
}
} else {
LoopPattern::Simple
}
}
}
}
/// ループパターンに基づいて lowering を実行
///
/// 関数名から LoopPattern を決定し、適切な lowering を実行する。
///
/// # Arguments
/// * `lowerer` - AstToJoinIrLowerer インスタンス
/// * `program_json` - Program(JSON v0)
///
/// # Returns
/// JoinModule または panic未対応パターン
pub fn lower_loop_by_function_name(
lowerer: &mut AstToJoinIrLowerer,
program_json: &serde_json::Value,
) -> JoinModule {
// 1. 関数名を取得
let defs = program_json["defs"]
.as_array()
.expect("Program(JSON v0) must have 'defs' array");
let func_def = defs
.get(0)
.expect("At least one function definition required");
let func_name = func_def["name"]
.as_str()
.expect("Function must have 'name'");
// 2. Loop body を取得Break/Continue 検出用)
let body = &func_def["body"]["body"];
let stmts = body.as_array().expect("Function body must be array");
let loop_body: Option<&[serde_json::Value]> = stmts
.iter()
.find(|stmt| stmt["type"].as_str() == Some("Loop"))
.and_then(|loop_node| loop_node["body"].as_array())
.map(|v| v.as_slice());
// 3. LoopPattern を決定Break/Continue も新モジュールで処理)
let pattern = detect_loop_pattern(func_name, loop_body);
// 4. loop_patterns 層に委譲
match loop_patterns::lower_loop_with_pattern(pattern.clone(), lowerer, program_json) {
Ok(module) => module,
Err(LoweringError::UnimplementedPattern { pattern, reason }) => {
// 未実装パターンは loop_patterns_old にフォールバック
eprintln!(
"[LoopFrontendBinding] Pattern {:?} not implemented: {}. Falling back to old route.",
pattern, reason
);
lowerer.lower_loop_with_break_continue(program_json)
}
Err(e) => panic!("LoopFrontendBinding error: {:?}", e),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_detect_loop_pattern_by_name() {
assert_eq!(detect_loop_pattern("filter", None), LoopPattern::Filter);
assert_eq!(
detect_loop_pattern("print_tokens", None),
LoopPattern::PrintTokens
);
assert_eq!(detect_loop_pattern("map", None), LoopPattern::Map);
assert_eq!(detect_loop_pattern("reduce", None), LoopPattern::Reduce);
assert_eq!(detect_loop_pattern("simple", None), LoopPattern::Simple);
assert_eq!(detect_loop_pattern("unknown", None), LoopPattern::Simple);
}
}

View File

@ -0,0 +1,160 @@
# Loop Patterns - JoinIR Frontend ループパターン処理層
## 📋 この層の責務
**JSON v0 のループ body を、LoopFrontendBinding が渡してきた LoopPattern に従って JoinIR 命令列に変換する**
- LoopFrontendBinding から受け取った `LoopPattern` enum に基づいて適切な lowering 処理を選択
- ループ body の JSON AST を JoinIR 命令Jump/Call/Select/MethodCall/ConditionalMethodCall 等)に変換
- 各パターンに特化した最適な JoinIR 構造を生成3関数構造: entry/loop_step/k_exit
## 🚫 やらないこと(重要!)
### ❌ 関数名ベースの判定
- **理由**: それは LoopFrontendBinding 層の責務
- **例**: `"simple"``"filter"` という関数名で分岐する処理は**書かない**
- **正しい設計**: LoopPattern enum を受け取って、enum の variant で分岐
### ❌ Box 名・メソッド名で意味論を変える
- **理由**: それは JoinIR→MIR Bridge / VM 側の責務
- **例**: `ArrayExtBox.filter` だから特別処理、という判定は**書かない**
- **正しい設計**: JSON の "type" フィールドVar/Method/Call等だけで判定
### ❌ AST 構造に踏み込みすぎる
- **理由**: JSON v0 の "type" フィールドで十分判定できる
- **例**: "kind" フィールドを見て詳細な AST ノード型を判定する処理は**書かない**
- **正しい設計**: "type": "Method" / "Var" / "Int" 等だけで処理
## 🏗️ 設計原則
### 1. パターン = 1箱 = 1責務
各 LoopPattern の lowering 処理は、独立したモジュール(ファイル)に分離:
```
loop_patterns/
├── filter.rs # Filter パターン: pred が true のときだけ push
├── print_tokens.rs # PrintTokens パターン: token を順番に print
├── map.rs # Map パターン(未実装)
├── reduce.rs # Reduce パターン(未実装)
└── simple.rs # Simple パターン: 汎用ループ
```
**各ファイルの責務を 1 行で表現**
- `filter.rs`: 「pred が true のときだけ push するループを ConditionalMethodCall に落とす」
- `print_tokens.rs`: 「token を順番に取り出して print するループを Jump/Call/MethodCall に落とす」
### 2. 共通処理は最小限
- JSON パース、ExtractCtx 初期化、3関数構造生成の**本当に共通な部分だけ**をヘルパー化
- パターン固有のロジックは各モジュールに閉じる
### 3. エラーハンドリング
- 複雑すぎるパターンは素直に `Err(UnimplementedPattern)` を返す
- フェーズを分けて段階的に拡張しやすくする
### 4. trait による統一インターフェース
```rust
pub trait LoopPatternLowerer {
fn lower(
&self,
lowerer: &mut AstToJoinIrLowerer,
program_json: &serde_json::Value,
) -> Result<JoinModule, LoweringError>;
}
```
## 📊 LoopPattern の分類
### ユースケースベースの分類Phase 55/56 実装済み)
| Pattern | 実装状況 | 責務 | 代表関数 |
|---------|---------|------|---------|
| **PrintTokens** | ✅ Phase 55 | token を順番に print | `JsonTokenizer.print_tokens` |
| **Filter** | ✅ Phase 56 | pred が true のときだけ push | `ArrayExtBox.filter` |
| **Map** | 🔜 Phase 57+ | 各要素を変換して新配列作成 | `ArrayExtBox.map` |
| **Reduce** | 🔜 Phase 58+ | 累積計算fold | `ArrayExtBox.reduce` |
| **Simple** | ✅ Phase 34 | 汎用ループ(上記以外) | 各種ループ |
### 制御構造ベースの分類(参考)
実際の実装では**ユースケース優先**だが、内部的には制御構造も考慮:
- **Simple**: break/continue なし
- **Break**: 早期 return ループ
- **Continue**: 条件付きスキップループ
- **Mixed**: break + continue 両方(未実装)
## 🔄 データフロー
```
1. LoopFrontendBinding 層
↓ (関数名ベース判定)
LoopPattern enum を決定
2. loop_patterns.rs (ディスパッチ箱)
↓ (enum の variant で分岐)
適切な lowering モジュールを選択
3. 各 lowering モジュール (filter.rs 等)
↓ (JSON "type" フィールドで判定)
JoinIR 命令列を生成
4. JoinIR→MIR Bridge
↓ (Box 名・メソッド名で最適化)
MIR 命令に変換
```
## 🎯 今後の拡張
新しいループパターンを追加する手順:
1. `LoopPattern` enum に variant 追加
2. 新しい lowering モジュール作成(例: `map.rs`
3. 責務を 1 行で定義
4. `trait LoopPatternLowerer` を実装
5. `loop_patterns.rs` のディスパッチに 1 行追加
**設計思想**: 各パターンが独立した箱なので、追加・削除・置き換えが容易
## 📦 現在の状態と移行計画
### 現在の状態Phase P2 完了)
```
loop_patterns/ # 新モジュール構造(将来の呼び出し用)
├── mod.rs # LoopPattern enum + ディスパッチ
├── common.rs # 共通処理parse/ctx/entry/k_exit 生成)
├── filter.rs # Filter パターン→simple に委譲)
├── print_tokens.rs # PrintTokens パターン→simple に委譲)
└── simple.rs # Simple パターン(実装済み)
loop_patterns_old.rs # 現行コード(関数名ベース判定を含む)
```
### 移行計画Future Work
1. **LoopFrontendBinding 層の作成** (Phase P3+ 予定)
- 関数名 → LoopPattern enum の変換を担当
- `lower_program_json()` から呼び出し
2. **loop_patterns_old.rs の廃止**
- Break/Continue パターンも新モジュールに移行後
- `loop_patterns_old.rs` を削除
3. **完全移行後の呼び出し構造**
```rust
// LoopFrontendBinding 層(新設)
let pattern = detect_loop_pattern(func_name);
// loop_patterns 層(新モジュール)
loop_patterns::lower_loop_with_pattern(pattern, lowerer, json)
```
---
**Phase**: P2 (Loop Pattern 箱化モジュール化)
**作成日**: 2025-11-29
**原則**: 関数名判定は Binding 層、Box名判定は Bridge/VM 層、この層は LoopPattern だけを見る

View File

@ -0,0 +1,185 @@
//! Phase P4: Break パターン lowering
//!
//! ## 責務1行で表現
//! **if break 条件で早期 return するループを Jump(k_exit, cond) に落とす**
//!
//! ## パターン例
//! ```nyash
//! loop {
//! if i >= n { break }
//! acc = acc + i
//! i = i + 1
//! }
//! ```
//!
//! ## 生成する JoinIR 構造
//! - entry 関数: Call(loop_step)
//! - loop_step 関数:
//! - break 条件評価
//! - true: Jump(k_exit, acc)
//! - false: body 処理 + 再帰
//! - k_exit 関数: Return(acc)
use super::common::{
build_join_module, build_step_params, create_k_exit_function, create_loop_context,
parse_program_json, process_local_inits,
};
use super::{AstToJoinIrLowerer, JoinModule, LoweringError};
use crate::mir::join_ir::{JoinFunction, JoinInst};
use crate::mir::ValueId;
/// Break パターンを JoinModule に変換
///
/// # Arguments
/// * `lowerer` - AstToJoinIrLowerer インスタンス
/// * `program_json` - Program(JSON v0)
pub fn lower(
lowerer: &mut AstToJoinIrLowerer,
program_json: &serde_json::Value,
) -> Result<JoinModule, LoweringError> {
// 1. Program(JSON) をパース
let parsed = parse_program_json(program_json);
// 2. LoopContext と entry_ctx を作成
let (ctx, mut entry_ctx) = create_loop_context(lowerer, &parsed);
// 3. Local 初期化を処理
let init_insts = process_local_inits(lowerer, &parsed, &mut entry_ctx);
// 4. Loop body から Break If を探す
let loop_node = &parsed.stmts[parsed.loop_node_idx];
let loop_body = loop_node["body"]
.as_array()
.ok_or_else(|| LoweringError::InvalidLoopBody {
message: "Loop must have 'body' array".to_string(),
})?;
let break_if_stmt = loop_body
.iter()
.find(|stmt| {
stmt["type"].as_str() == Some("If")
&& stmt["then"].as_array().map_or(false, |then| {
then.iter().any(|s| s["type"].as_str() == Some("Break"))
})
})
.ok_or_else(|| LoweringError::InvalidLoopBody {
message: "Break pattern must have If + Break".to_string(),
})?;
let break_cond_expr = &break_if_stmt["cond"];
// 5. entry 関数を生成
let entry_func = create_entry_function_break(&ctx, &parsed, init_insts, &mut entry_ctx);
// 6. loop_step 関数を生成
let loop_step_func =
create_loop_step_function_break(lowerer, &ctx, &parsed.func_name, break_cond_expr, loop_body)?;
// 7. k_exit 関数を生成
let k_exit_func = create_k_exit_function(&ctx, &parsed.func_name);
// 8. JoinModule を構築
Ok(build_join_module(entry_func, loop_step_func, k_exit_func))
}
/// Break パターン用 entry 関数を生成
fn create_entry_function_break(
ctx: &super::common::LoopContext,
parsed: &super::common::ParsedProgram,
init_insts: Vec<JoinInst>,
entry_ctx: &mut super::super::context::ExtractCtx,
) -> JoinFunction {
// i, acc, n を取得
let i_init = entry_ctx.get_var("i").expect("i must be initialized");
let acc_init = entry_ctx.get_var("acc").expect("acc must be initialized");
let n_param = entry_ctx.get_var("n").expect("n must be parameter");
let loop_result = entry_ctx.alloc_var();
let mut body = init_insts;
body.push(JoinInst::Call {
func: ctx.loop_step_id,
args: vec![i_init, acc_init, n_param],
k_next: None,
dst: Some(loop_result),
});
body.push(JoinInst::Ret {
value: Some(loop_result),
});
JoinFunction {
id: ctx.entry_id,
name: parsed.func_name.clone(),
params: (0..parsed.param_names.len())
.map(|i| ValueId(i as u32))
.collect(),
body,
exit_cont: None,
}
}
/// Break パターン用 loop_step 関数を生成
fn create_loop_step_function_break(
lowerer: &mut AstToJoinIrLowerer,
ctx: &super::common::LoopContext,
func_name: &str,
break_cond_expr: &serde_json::Value,
loop_body: &[serde_json::Value],
) -> Result<JoinFunction, LoweringError> {
use super::super::context::ExtractCtx;
// step_ctx を作成Break パターンは me/external_refs なし)
let step_i = ValueId(0);
let step_acc = ValueId(1);
let step_n = ValueId(2);
let mut step_ctx = ExtractCtx::new(3);
step_ctx.register_param("i".to_string(), step_i);
step_ctx.register_param("acc".to_string(), step_acc);
step_ctx.register_param("n".to_string(), step_n);
// Break 条件を評価
let (break_cond_var, break_cond_insts) = lowerer.extract_value(break_cond_expr, &mut step_ctx);
let mut body = break_cond_insts;
// 早期 return: break_cond が true なら k_exit へ Jump
body.push(JoinInst::Jump {
cont: ctx.k_exit_id.as_cont(),
args: vec![step_acc],
cond: Some(break_cond_var),
});
// Loop body を処理If + Break はスキップ)
for body_stmt in loop_body {
if body_stmt["type"].as_str() == Some("If") {
continue;
}
let (insts, _effect) = lowerer.lower_statement(body_stmt, &mut step_ctx);
body.extend(insts);
}
// 再帰呼び出し
let i_next = step_ctx.get_var("i").expect("i must be updated");
let acc_next = step_ctx.get_var("acc").expect("acc must be updated");
let recurse_result = step_ctx.alloc_var();
body.push(JoinInst::Call {
func: ctx.loop_step_id,
args: vec![i_next, acc_next, step_n],
k_next: None,
dst: Some(recurse_result),
});
body.push(JoinInst::Ret {
value: Some(recurse_result),
});
Ok(JoinFunction {
id: ctx.loop_step_id,
name: format!("{}_loop_step", func_name),
params: vec![step_i, step_acc, step_n],
body,
exit_cont: None,
})
}

View File

@ -0,0 +1,407 @@
//! Phase P2: Loop Patterns 共通処理
//!
//! ## 責務
//! 各 LoopPattern lowering で共通する処理を提供する。
//!
//! ## 提供する機能
//! - `ParsedProgram`: Program(JSON) のパース結果を保持
//! - `LoopContext`: 3関数構造entry/loop_step/k_exitのコンテキスト
//! - `parse_program_json()`: Program(JSON) からパース結果を抽出
//! - `process_local_inits()`: Loop 前の Local 初期化を処理
//! - `collect_external_refs()`: Phase 56 external_refs 収集
//! - `create_entry_function()`: entry 関数生成
//! - `create_k_exit_function()`: k_exit 関数生成
use super::{AstToJoinIrLowerer, JoinModule};
use crate::mir::join_ir::{ConstValue, JoinFuncId, JoinFunction, JoinInst};
use crate::mir::ValueId;
use std::collections::BTreeMap;
use super::super::context::ExtractCtx;
/// Program(JSON) のパース結果
pub struct ParsedProgram {
/// 関数名
pub func_name: String,
/// パラメータ名リスト
pub param_names: Vec<String>,
/// 関数 body の statements
pub stmts: Vec<serde_json::Value>,
/// Loop ノードのインデックス
pub loop_node_idx: usize,
}
/// 3関数構造のコンテキスト
pub struct LoopContext {
/// entry 関数 ID
pub entry_id: JoinFuncId,
/// loop_step 関数 ID
pub loop_step_id: JoinFuncId,
/// k_exit 関数 ID
pub k_exit_id: JoinFuncId,
/// "me" パラメータが存在するか
pub has_me: bool,
/// external_refsPhase 56: arr, pred など)
pub external_refs: Vec<(String, ValueId)>,
}
/// Program(JSON) をパースして ParsedProgram を返す
///
/// # Arguments
/// * `program_json` - Program(JSON v0)
///
/// # Returns
/// パース結果
pub fn parse_program_json(program_json: &serde_json::Value) -> ParsedProgram {
// 1. defs 配列を取得
let defs = program_json["defs"]
.as_array()
.expect("Program(JSON v0) must have 'defs' array");
// 2. 最初の関数定義を取得
let func_def = defs
.get(0)
.expect("At least one function definition required");
let func_name = func_def["name"]
.as_str()
.expect("Function must have 'name'")
.to_string();
let params = func_def["params"]
.as_array()
.expect("Function must have 'params' array");
let param_names: Vec<String> = params
.iter()
.map(|p| p.as_str().expect("Parameter must be string").to_string())
.collect();
// 3. body を取得
let body = &func_def["body"]["body"];
let stmts: Vec<serde_json::Value> = body
.as_array()
.expect("Function body must be array")
.clone();
// 4. Loop ノードのインデックスを探す
let loop_node_idx = stmts
.iter()
.position(|stmt| stmt["type"].as_str() == Some("Loop"))
.expect("Loop node not found");
ParsedProgram {
func_name,
param_names,
stmts,
loop_node_idx,
}
}
/// Loop 前の Local 初期化を処理
///
/// # Arguments
/// * `lowerer` - AstToJoinIrLowerer インスタンス
/// * `parsed` - パース済み Program
/// * `ctx` - ExtractCtx
///
/// # Returns
/// 初期化命令列
pub fn process_local_inits(
lowerer: &mut AstToJoinIrLowerer,
parsed: &ParsedProgram,
ctx: &mut ExtractCtx,
) -> Vec<JoinInst> {
let mut init_insts = Vec::new();
for stmt in &parsed.stmts[..parsed.loop_node_idx] {
let stmt_type = stmt["type"].as_str().expect("Statement must have type");
match stmt_type {
"Local" => {
let var_name = stmt["name"]
.as_str()
.expect("Local must have 'name'")
.to_string();
let expr = &stmt["expr"];
// extract_value で式を評価
let (var_id, insts) = lowerer.extract_value(expr, ctx);
init_insts.extend(insts);
// 同名再宣言 = var_map を更新(再代入の意味論)
ctx.register_param(var_name, var_id);
}
_ => panic!("Unexpected statement type before Loop: {}", stmt_type),
}
}
init_insts
}
/// Phase 56: external_refs を収集
///
/// パラメータのうち、ループ制御変数me, i, acc, n以外を収集する。
/// filter(arr, pred) などで使用。
///
/// # Arguments
/// * `param_names` - パラメータ名リスト
///
/// # Returns
/// external_refs のリスト(名前, ValueId
pub fn collect_external_refs(param_names: &[String]) -> Vec<(String, ValueId)> {
const RESERVED_VARS: [&str; 4] = ["me", "i", "acc", "n"];
param_names
.iter()
.enumerate()
.filter_map(|(idx, name)| {
if !RESERVED_VARS.contains(&name.as_str()) {
Some((name.clone(), ValueId(idx as u32)))
} else {
None
}
})
.collect()
}
/// LoopContext と entry 用 ExtractCtx を作成
///
/// 3関数構造の ID 生成と ExtractCtx 初期化を行う。
///
/// # Arguments
/// * `lowerer` - AstToJoinIrLowerer インスタンス
/// * `parsed` - パース済み Program
///
/// # Returns
/// (LoopContext, ExtractCtx)
pub fn create_loop_context(
lowerer: &mut AstToJoinIrLowerer,
parsed: &ParsedProgram,
) -> (LoopContext, ExtractCtx) {
// 3関数 ID 生成
let entry_id = lowerer.next_func_id();
let loop_step_id = lowerer.next_func_id();
let k_exit_id = lowerer.next_func_id();
// ExtractCtx 作成とパラメータ登録
let mut entry_ctx = ExtractCtx::new(parsed.param_names.len() as u32);
for (i, name) in parsed.param_names.iter().enumerate() {
entry_ctx.register_param(name.clone(), ValueId(i as u32));
}
// me パラメータ確認
let has_me = parsed.param_names.iter().any(|n| n == "me");
// external_refs 収集
let external_refs = collect_external_refs(&parsed.param_names);
let ctx = LoopContext {
entry_id,
loop_step_id,
k_exit_id,
has_me,
external_refs,
};
(ctx, entry_ctx)
}
/// entry 関数を生成
///
/// # Arguments
/// * `ctx` - LoopContext
/// * `parsed` - パース済み Program
/// * `init_insts` - 初期化命令列
///
/// # Returns
/// entry JoinFunction
pub fn create_entry_function(
ctx: &LoopContext,
parsed: &ParsedProgram,
init_insts: Vec<JoinInst>,
entry_ctx: &mut ExtractCtx,
) -> JoinFunction {
// i, acc, n を取得
let i_init = entry_ctx.get_var("i").expect("i must be initialized");
let acc_init = entry_ctx.get_var("acc").expect("acc must be initialized");
let n_param = entry_ctx.get_var("n").expect("n must be parameter");
// me パラメータ取得
let me_param = entry_ctx.get_var("me");
let loop_result = entry_ctx.alloc_var();
// Call args 構築me?, i, acc, n, ...external_refs
let mut call_args = if let Some(me_id) = me_param {
vec![me_id, i_init, acc_init, n_param]
} else {
vec![i_init, acc_init, n_param]
};
// external_refs を追加
for (name, _) in &ctx.external_refs {
if let Some(var_id) = entry_ctx.get_var(name) {
call_args.push(var_id);
}
}
let mut body = init_insts;
body.push(JoinInst::Call {
func: ctx.loop_step_id,
args: call_args,
k_next: None,
dst: Some(loop_result),
});
body.push(JoinInst::Ret {
value: Some(loop_result),
});
JoinFunction {
id: ctx.entry_id,
name: parsed.func_name.clone(),
params: (0..parsed.param_names.len())
.map(|i| ValueId(i as u32))
.collect(),
body,
exit_cont: None,
}
}
/// k_exit 関数を生成
///
/// # Arguments
/// * `ctx` - LoopContext
/// * `func_name` - 関数名
///
/// # Returns
/// k_exit JoinFunction
pub fn create_k_exit_function(ctx: &LoopContext, func_name: &str) -> JoinFunction {
let k_exit_acc = ValueId(0);
JoinFunction {
id: ctx.k_exit_id,
name: format!("{}_k_exit", func_name),
params: vec![k_exit_acc],
body: vec![JoinInst::Ret {
value: Some(k_exit_acc),
}],
exit_cont: None,
}
}
/// loop_step 用の ExtractCtx を作成
///
/// # Arguments
/// * `ctx` - LoopContext
///
/// # Returns
/// loop_step 用 ExtractCtx
pub fn create_step_ctx(ctx: &LoopContext) -> ExtractCtx {
// パラメータ数: me?, i, acc, n, ...external_refs
let base_params: u32 = if ctx.has_me { 4 } else { 3 };
let num_params = base_params + ctx.external_refs.len() as u32;
let mut step_ctx = ExtractCtx::new(num_params);
// ValueId 割り当て
let (step_i, step_acc, step_n) = if ctx.has_me {
step_ctx.register_param("me".to_string(), ValueId(0));
(ValueId(1), ValueId(2), ValueId(3))
} else {
(ValueId(0), ValueId(1), ValueId(2))
};
step_ctx.register_param("i".to_string(), step_i);
step_ctx.register_param("acc".to_string(), step_acc);
step_ctx.register_param("n".to_string(), step_n);
// external_refs 登録
let ext_offset = base_params;
for (i, (name, _)) in ctx.external_refs.iter().enumerate() {
step_ctx.register_param(name.clone(), ValueId(ext_offset + i as u32));
}
step_ctx
}
/// loop_step 関数のパラメータリストを構築
///
/// # Arguments
/// * `ctx` - LoopContext
///
/// # Returns
/// パラメータ ValueId リスト
pub fn build_step_params(ctx: &LoopContext) -> Vec<ValueId> {
let base_params: u32 = if ctx.has_me { 4 } else { 3 };
let mut params = if ctx.has_me {
vec![ValueId(0), ValueId(1), ValueId(2), ValueId(3)]
} else {
vec![ValueId(0), ValueId(1), ValueId(2)]
};
// external_refs を追加
for (i, _) in ctx.external_refs.iter().enumerate() {
params.push(ValueId(base_params + i as u32));
}
params
}
/// 再帰呼び出し args を構築
///
/// # Arguments
/// * `ctx` - LoopContext
/// * `step_ctx` - loop_step 用 ExtractCtx更新後の i, acc を持つ)
///
/// # Returns
/// 再帰呼び出し args
pub fn build_recurse_args(ctx: &LoopContext, step_ctx: &ExtractCtx) -> Vec<ValueId> {
let i_next = step_ctx.get_var("i").expect("i must exist");
let acc_next = step_ctx.get_var("acc").expect("acc must exist");
let step_n = step_ctx.get_var("n").expect("n must exist");
let mut args = if ctx.has_me {
let me_id = step_ctx.get_var("me").expect("me must exist");
vec![me_id, i_next, acc_next, step_n]
} else {
vec![i_next, acc_next, step_n]
};
// external_refs を追加
for (name, _) in &ctx.external_refs {
if let Some(var_id) = step_ctx.get_var(name) {
args.push(var_id);
}
}
args
}
/// JoinModule を構築
///
/// # Arguments
/// * `entry_func` - entry 関数
/// * `loop_step_func` - loop_step 関数
/// * `k_exit_func` - k_exit 関数
///
/// # Returns
/// JoinModule
pub fn build_join_module(
entry_func: JoinFunction,
loop_step_func: JoinFunction,
k_exit_func: JoinFunction,
) -> JoinModule {
let entry_id = entry_func.id;
let mut functions = BTreeMap::new();
functions.insert(entry_func.id, entry_func);
functions.insert(loop_step_func.id, loop_step_func);
functions.insert(k_exit_func.id, k_exit_func);
JoinModule {
functions,
entry: Some(entry_id),
}
}

View File

@ -0,0 +1,240 @@
//! Phase P4: Continue パターン lowering
//!
//! ## 責務1行で表現
//! **if continue 条件で処理をスキップするループを Select に落とす**
//!
//! ## パターン例
//! ```nyash
//! loop(i < n) {
//! i = i + 1
//! if i == 3 { continue }
//! acc = acc + i
//! }
//! ```
//!
//! ## 生成する JoinIR 構造
//! - entry 関数: Call(loop_step)
//! - loop_step 関数:
//! - exit 条件チェック
//! - i++ 処理
//! - continue 条件評価
//! - Select: continue なら acc そのまま、そうでなければ更新
//! - 再帰
//! - k_exit 関数: Return(acc)
use super::common::{
build_join_module, create_k_exit_function, create_loop_context, parse_program_json,
process_local_inits,
};
use super::{AstToJoinIrLowerer, JoinModule, LoweringError};
use crate::mir::join_ir::{CompareOp, ConstValue, JoinFunction, JoinInst, MirLikeInst};
use crate::mir::ValueId;
/// Continue パターンを JoinModule に変換
///
/// # Arguments
/// * `lowerer` - AstToJoinIrLowerer インスタンス
/// * `program_json` - Program(JSON v0)
pub fn lower(
lowerer: &mut AstToJoinIrLowerer,
program_json: &serde_json::Value,
) -> Result<JoinModule, LoweringError> {
// 1. Program(JSON) をパース
let parsed = parse_program_json(program_json);
// 2. LoopContext と entry_ctx を作成
let (ctx, mut entry_ctx) = create_loop_context(lowerer, &parsed);
// 3. Local 初期化を処理
let init_insts = process_local_inits(lowerer, &parsed, &mut entry_ctx);
// 4. Loop の cond を取得
let loop_node = &parsed.stmts[parsed.loop_node_idx];
let loop_cond_expr = &loop_node["cond"];
// 5. Loop body から Continue If を探す
let loop_body = loop_node["body"]
.as_array()
.ok_or_else(|| LoweringError::InvalidLoopBody {
message: "Loop must have 'body' array".to_string(),
})?;
let continue_if_stmt = loop_body
.iter()
.find(|stmt| {
stmt["type"].as_str() == Some("If")
&& stmt["then"].as_array().map_or(false, |then| {
then.iter().any(|s| s["type"].as_str() == Some("Continue"))
})
})
.ok_or_else(|| LoweringError::InvalidLoopBody {
message: "Continue pattern must have If + Continue".to_string(),
})?;
let continue_cond_expr = &continue_if_stmt["cond"];
// 6. entry 関数を生成
let entry_func = create_entry_function_continue(&ctx, &parsed, init_insts, &mut entry_ctx);
// 7. loop_step 関数を生成
let loop_step_func = create_loop_step_function_continue(
lowerer,
&ctx,
&parsed.func_name,
loop_cond_expr,
continue_cond_expr,
loop_body,
)?;
// 8. k_exit 関数を生成
let k_exit_func = create_k_exit_function(&ctx, &parsed.func_name);
// 9. JoinModule を構築
Ok(build_join_module(entry_func, loop_step_func, k_exit_func))
}
/// Continue パターン用 entry 関数を生成
fn create_entry_function_continue(
ctx: &super::common::LoopContext,
parsed: &super::common::ParsedProgram,
init_insts: Vec<JoinInst>,
entry_ctx: &mut super::super::context::ExtractCtx,
) -> JoinFunction {
// i, acc, n を取得
let i_init = entry_ctx.get_var("i").expect("i must be initialized");
let acc_init = entry_ctx.get_var("acc").expect("acc must be initialized");
let n_param = entry_ctx.get_var("n").expect("n must be parameter");
let loop_result = entry_ctx.alloc_var();
let mut body = init_insts;
body.push(JoinInst::Call {
func: ctx.loop_step_id,
args: vec![i_init, acc_init, n_param],
k_next: None,
dst: Some(loop_result),
});
body.push(JoinInst::Ret {
value: Some(loop_result),
});
JoinFunction {
id: ctx.entry_id,
name: parsed.func_name.clone(),
params: (0..parsed.param_names.len())
.map(|i| ValueId(i as u32))
.collect(),
body,
exit_cont: None,
}
}
/// Continue パターン用 loop_step 関数を生成
fn create_loop_step_function_continue(
lowerer: &mut AstToJoinIrLowerer,
ctx: &super::common::LoopContext,
func_name: &str,
loop_cond_expr: &serde_json::Value,
continue_cond_expr: &serde_json::Value,
loop_body: &[serde_json::Value],
) -> Result<JoinFunction, LoweringError> {
use super::super::context::ExtractCtx;
let step_i = ValueId(0);
let step_acc = ValueId(1);
let step_n = ValueId(2);
let mut step_ctx = ExtractCtx::new(3);
step_ctx.register_param("i".to_string(), step_i);
step_ctx.register_param("acc".to_string(), step_acc);
step_ctx.register_param("n".to_string(), step_n);
let mut body = Vec::new();
// 1. exit 条件チェック(!(i < n) = i >= n で抜ける)
let (cond_var, cond_insts) = lowerer.extract_value(loop_cond_expr, &mut step_ctx);
body.extend(cond_insts);
let false_const = step_ctx.alloc_var();
let exit_cond = step_ctx.alloc_var();
body.push(JoinInst::Compute(MirLikeInst::Const {
dst: false_const,
value: ConstValue::Bool(false),
}));
body.push(JoinInst::Compute(MirLikeInst::Compare {
dst: exit_cond,
op: CompareOp::Eq,
lhs: cond_var,
rhs: false_const,
}));
body.push(JoinInst::Jump {
cont: ctx.k_exit_id.as_cont(),
args: vec![step_acc],
cond: Some(exit_cond),
});
// 2. Continue pattern 特有: i のインクリメントが先
let first_local = loop_body
.iter()
.find(|stmt| {
stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("i")
})
.ok_or_else(|| LoweringError::InvalidLoopBody {
message: "Continue pattern must have i increment as first Local".to_string(),
})?;
let i_expr = &first_local["expr"];
let (i_next, i_insts) = lowerer.extract_value(i_expr, &mut step_ctx);
body.extend(i_insts);
step_ctx.register_param("i".to_string(), i_next);
// 3. Continue 条件を評価
let (continue_cond_var, continue_cond_insts) =
lowerer.extract_value(continue_cond_expr, &mut step_ctx);
body.extend(continue_cond_insts);
// 4. acc の更新値を計算
let acc_update_local = loop_body
.iter()
.find(|stmt| {
stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("acc")
})
.ok_or_else(|| LoweringError::InvalidLoopBody {
message: "Continue pattern must have acc update Local".to_string(),
})?;
let acc_expr = &acc_update_local["expr"];
let (acc_increment, acc_insts) = lowerer.extract_value(acc_expr, &mut step_ctx);
body.extend(acc_insts);
// 5. Select: Continue なら acc そのまま、そうでなければ更新
let acc_next = step_ctx.alloc_var();
body.push(JoinInst::Select {
dst: acc_next,
cond: continue_cond_var,
then_val: step_acc, // Continue: 更新しない
else_val: acc_increment, // 通常: 更新
});
// 6. 末尾再帰
let recurse_result = step_ctx.alloc_var();
body.push(JoinInst::Call {
func: ctx.loop_step_id,
args: vec![i_next, acc_next, step_n],
k_next: None,
dst: Some(recurse_result),
});
body.push(JoinInst::Ret {
value: Some(recurse_result),
});
Ok(JoinFunction {
id: ctx.loop_step_id,
name: format!("{}_loop_step", func_name),
params: vec![step_i, step_acc, step_n],
body,
exit_cont: None,
})
}

View File

@ -0,0 +1,53 @@
//! Phase P2/56: Filter パターン lowering
//!
//! ## 責務1行で表現
//! **pred が true のときだけ push するループを ConditionalMethodCall に落とす**
//!
//! ## パターン例
//! ```nyash
//! box ArrayExtBox {
//! filter(pred) {
//! local out = new ArrayBox()
//! local i = 0
//! loop(i < me.size()) {
//! local v = me.get(i)
//! if pred(v) {
//! out.push(v) // ← ConditionalMethodCall に変換
//! }
//! i = i + 1
//! }
//! return out
//! }
//! }
//! ```
//!
//! ## 生成する JoinIR 構造
//! - entry 関数: Call(loop_step)
//! - loop_step 関数:
//! - 条件チェック: `i < tokens.size()`
//! - true: v取得 + ConditionalMethodCall(if pred(v) then push) + i++ + 再帰
//! - false: Jump(k_exit)
//! - k_exit 関数: Return(out)
//!
//! ## 現在の実装
//! Simple パターンに委譲stmt_handlers が if 内 MethodCall を適切に処理)
use super::{AstToJoinIrLowerer, JoinModule, LoweringError};
/// Filter パターンを JoinModule に変換
///
/// Phase 56 で実装済みの filter ループを JoinIR に変換する。
/// 現在は Simple パターンに委譲し、stmt_handlers が if 内の処理を適切に行う。
///
/// # Arguments
/// * `lowerer` - AstToJoinIrLowerer インスタンス
/// * `program_json` - Program(JSON v0)
pub fn lower(
lowerer: &mut AstToJoinIrLowerer,
program_json: &serde_json::Value,
) -> Result<JoinModule, LoweringError> {
// Phase 56: Filter パターンは Simple パターンと同じ構造
// 差分は stmt_handlers での if 内 MethodCall 処理のみ
// → Simple に委譲
super::simple::lower(lowerer, program_json)
}

View File

@ -0,0 +1,120 @@
//! Phase P2: Loop Patterns - JoinIR Frontend ループパターン処理層
//!
//! ## 責務
//! JSON v0 のループ body を、LoopFrontendBinding が渡してきた LoopPattern に従って
//! JoinIR 命令列に変換する。
//!
//! ## やらないこと
//! - 関数名ベースの判定(それは LoopFrontendBinding 層)
//! - Box 名・メソッド名で意味論を変える(それは Bridge/VM 層)
//!
//! ## 設計原則
//! - パターン = 1箱 = 1責務
//! - 共通処理は common.rs に集約
//! - エラーは Err(UnimplementedPattern) で返す
use super::{AstToJoinIrLowerer, JoinModule};
pub mod break_pattern;
pub mod common;
pub mod continue_pattern;
pub mod filter;
pub mod print_tokens;
pub mod simple;
/// ループパターンの分類(ユースケースベース + 制御構造)
///
/// Phase 55/56 で実装済みのパターンと、今後実装予定のパターンを含む。
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LoopPattern {
/// PrintTokens パターンPhase 55
/// 責務: token を順番に取り出して print するループを Jump/Call/MethodCall に落とす
PrintTokens,
/// Filter パターンPhase 56
/// 責務: pred が true のときだけ push するループを ConditionalMethodCall に落とす
Filter,
/// Map パターンPhase 57+ 予定)
/// 責務: 各要素を変換して新配列作成
Map,
/// Reduce パターンPhase 58+ 予定)
/// 責務: 累積計算fold
Reduce,
/// Simple パターン(汎用ループ)
/// 責務: 上記以外の汎用的なループ処理
Simple,
/// Break パターンPhase P4
/// 責務: if break 条件で早期 return するループを Jump(k_exit, cond) に落とす
Break,
/// Continue パターンPhase P4
/// 責務: if continue 条件で処理をスキップするループを Select に落とす
Continue,
}
/// ループパターン lowering エラー
#[derive(Debug, Clone)]
pub enum LoweringError {
/// 未実装のパターン
UnimplementedPattern {
pattern: LoopPattern,
reason: String,
},
/// JSON パースエラー
JsonParseError { message: String },
/// ループ body が不正
InvalidLoopBody { message: String },
}
/// LoopPattern lowering の統一インターフェース
///
/// 各パターンの lowering モジュールはこの trait を実装する。
pub trait LoopPatternLowerer {
/// LoopPattern を JoinModule に変換
///
/// # Arguments
/// * `lowerer` - AstToJoinIrLowerer インスタンス
/// * `program_json` - Program(JSON v0)
///
/// # Returns
/// JoinModule または LoweringError
fn lower(
lowerer: &mut AstToJoinIrLowerer,
program_json: &serde_json::Value,
) -> Result<JoinModule, LoweringError>;
}
/// LoopPattern に応じた lowering を実行(ディスパッチ箱)
///
/// Phase P2: この関数は薄いディスパッチのみを行い、実際の lowering は
/// 各パターンのモジュールfilter.rs, print_tokens.rs 等)に委譲する。
///
/// # Arguments
/// * `pattern` - LoopPattern enum
/// * `lowerer` - AstToJoinIrLowerer インスタンス
/// * `program_json` - Program(JSON v0)
pub fn lower_loop_with_pattern(
pattern: LoopPattern,
lowerer: &mut AstToJoinIrLowerer,
program_json: &serde_json::Value,
) -> Result<JoinModule, LoweringError> {
match pattern {
LoopPattern::PrintTokens => print_tokens::lower(lowerer, program_json),
LoopPattern::Filter => filter::lower(lowerer, program_json),
LoopPattern::Map => Err(LoweringError::UnimplementedPattern {
pattern: LoopPattern::Map,
reason: "Map pattern is not yet implemented (Phase 57+)".to_string(),
}),
LoopPattern::Reduce => Err(LoweringError::UnimplementedPattern {
pattern: LoopPattern::Reduce,
reason: "Reduce pattern is not yet implemented (Phase 58+)".to_string(),
}),
LoopPattern::Simple => simple::lower(lowerer, program_json),
LoopPattern::Break => break_pattern::lower(lowerer, program_json),
LoopPattern::Continue => continue_pattern::lower(lowerer, program_json),
}
}

View File

@ -0,0 +1,49 @@
//! Phase P2/55: PrintTokens パターン lowering
//!
//! ## 責務1行で表現
//! **token を順番に取り出して print するループを Jump/Call/MethodCall に落とす**
//!
//! ## パターン例
//! ```nyash
//! box JsonTokenizer {
//! print_tokens() {
//! local i = 0
//! loop(i < me.tokens.size()) {
//! local token = me.tokens.get(i)
//! print(token) // ← MethodCall に変換
//! i = i + 1
//! }
//! }
//! }
//! ```
//!
//! ## 生成する JoinIR 構造
//! - entry 関数: Call(loop_step)
//! - loop_step 関数:
//! - 条件チェック: `i < tokens.size()`
//! - true: token取得 + print + i++ + 再帰
//! - false: Jump(k_exit)
//! - k_exit 関数: Return(void)
//!
//! ## 現在の実装
//! Simple パターンに委譲stmt_handlers が MethodCall を適切に処理)
use super::{AstToJoinIrLowerer, JoinModule, LoweringError};
/// PrintTokens パターンを JoinModule に変換
///
/// Phase 55 で実装済みの print_tokens ループを JoinIR に変換する。
/// 現在は Simple パターンに委譲し、stmt_handlers が MethodCall を適切に処理。
///
/// # Arguments
/// * `lowerer` - AstToJoinIrLowerer インスタンス
/// * `program_json` - Program(JSON v0)
pub fn lower(
lowerer: &mut AstToJoinIrLowerer,
program_json: &serde_json::Value,
) -> Result<JoinModule, LoweringError> {
// Phase 55: PrintTokens パターンは Simple パターンと同じ構造
// 差分は stmt_handlers での MethodCall 処理のみ
// → Simple に委譲
super::simple::lower(lowerer, program_json)
}

View File

@ -0,0 +1,151 @@
//! Phase P2: Simple パターン lowering
//!
//! ## 責務1行で表現
//! **汎用的なループ処理を Jump/Call/Select に落とす**
//!
//! ## パターン例
//! ```nyash
//! // Filter/Map/Reduce/PrintTokens 以外の汎用ループ
//! local sum = 0
//! local i = 0
//! loop(i < n) {
//! sum = sum + i
//! i = i + 1
//! }
//! ```
//!
//! ## 生成する JoinIR 構造
//! - entry 関数: Call(loop_step)
//! - loop_step 関数:
//! - 条件チェック
//! - true: body 処理 + 再帰
//! - false: Jump(k_exit)
//! - k_exit 関数: Return
use super::common::{
build_join_module, build_recurse_args, build_step_params, create_entry_function,
create_k_exit_function, create_loop_context, create_step_ctx, parse_program_json,
process_local_inits,
};
use super::{AstToJoinIrLowerer, JoinModule, LoweringError, LoopPattern};
use crate::mir::join_ir::{ConstValue, JoinFunction, JoinInst, MirLikeInst};
use crate::mir::join_ir::CompareOp;
/// Simple パターンを JoinModule に変換
///
/// 汎用的なループ処理を JoinIR に変換する。
/// Filter/Map/Reduce/PrintTokens 以外のすべてのループがこれに該当する。
///
/// # Arguments
/// * `lowerer` - AstToJoinIrLowerer インスタンス
/// * `program_json` - Program(JSON v0)
pub fn lower(
lowerer: &mut AstToJoinIrLowerer,
program_json: &serde_json::Value,
) -> Result<JoinModule, LoweringError> {
// 1. Program(JSON) をパース
let parsed = parse_program_json(program_json);
// 2. LoopContext と entry_ctx を作成
let (ctx, mut entry_ctx) = create_loop_context(lowerer, &parsed);
// 3. Local 初期化を処理
let init_insts = process_local_inits(lowerer, &parsed, &mut entry_ctx);
// 4. entry 関数を生成
let entry_func = create_entry_function(&ctx, &parsed, init_insts, &mut entry_ctx);
// 5. Loop ノードを取得
let loop_node = &parsed.stmts[parsed.loop_node_idx];
let loop_cond_expr = &loop_node["cond"];
let loop_body_stmts = loop_node["body"]
.as_array()
.ok_or_else(|| LoweringError::InvalidLoopBody {
message: "Loop must have 'body' array".to_string(),
})?;
// 6. loop_step 関数を生成
let loop_step_func = create_loop_step_function(
lowerer,
&ctx,
&parsed.func_name,
loop_cond_expr,
loop_body_stmts,
)?;
// 7. k_exit 関数を生成
let k_exit_func = create_k_exit_function(&ctx, &parsed.func_name);
// 8. JoinModule を構築
Ok(build_join_module(entry_func, loop_step_func, k_exit_func))
}
/// loop_step 関数を生成
fn create_loop_step_function(
lowerer: &mut AstToJoinIrLowerer,
ctx: &super::common::LoopContext,
func_name: &str,
loop_cond_expr: &serde_json::Value,
loop_body_stmts: &[serde_json::Value],
) -> Result<JoinFunction, LoweringError> {
// step_ctx を作成
let mut step_ctx = create_step_ctx(ctx);
// 条件式を評価i < n
let (cond_var, cond_insts) = lowerer.extract_value(loop_cond_expr, &mut step_ctx);
// !cond を計算i >= n なら抜ける)
let false_const = step_ctx.alloc_var();
let exit_cond = step_ctx.alloc_var();
let mut body = cond_insts;
body.push(JoinInst::Compute(MirLikeInst::Const {
dst: false_const,
value: ConstValue::Bool(false),
}));
body.push(JoinInst::Compute(MirLikeInst::Compare {
dst: exit_cond,
op: CompareOp::Eq,
lhs: cond_var,
rhs: false_const,
}));
// 早期 return: exit_cond が truei >= nなら k_exit へ Jump
let step_acc = step_ctx.get_var("acc").expect("acc must exist");
body.push(JoinInst::Jump {
cont: ctx.k_exit_id.as_cont(),
args: vec![step_acc],
cond: Some(exit_cond),
});
// Loop body を処理(汎用 statement handler を使用)
for body_stmt in loop_body_stmts {
let (insts, _effect) = lowerer.lower_statement(body_stmt, &mut step_ctx);
body.extend(insts);
}
// 再帰呼び出し
let recurse_args = build_recurse_args(ctx, &step_ctx);
let recurse_result = step_ctx.alloc_var();
body.push(JoinInst::Call {
func: ctx.loop_step_id,
args: recurse_args,
k_next: None,
dst: Some(recurse_result),
});
body.push(JoinInst::Ret {
value: Some(recurse_result),
});
// パラメータリストを構築
let params = build_step_params(ctx);
Ok(JoinFunction {
id: ctx.loop_step_id,
name: format!("{}_loop_step", func_name),
params,
body,
exit_cont: None,
})
}

View File

@ -30,7 +30,9 @@ mod context;
mod expr;
mod if_in_loop;
mod if_return;
mod loop_frontend_binding;
mod loop_patterns;
mod loop_patterns_old;
mod nested_if;
mod read_quoted;
mod stmt_handlers;
@ -83,18 +85,20 @@ impl AstToJoinIrLowerer {
.as_str()
.expect("Function must have 'name'");
// 3. 関数名で分岐Phase 34-2/34-3/34-4/34-5/34-7/34-8/41-4/45
// test/local/_read_value_from_pair: If Return pattern
// simple: Loop pattern (Phase 34-7/34-8)
// parse_loop: Phase 41-4 NestedIfMerge pattern
// read_quoted_from: Phase 45 Guard if + Loop with break + accumulator
// 3. 関数名で分岐Phase P3: LoopFrontendBinding 層導入
//
// パターン分類:
// - If Return pattern: test/local/_read_value_from_pair
// - Loop pattern: simple 等 → LoopFrontendBinding 経由
// - NestedIfMerge pattern: parse_loop (dev flag gated)
// - ReadQuoted pattern: read_quoted_from (dev flag gated)
match func_name {
"test" | "local" | "_read_value_from_pair" => {
self.lower_if_return_pattern(program_json)
}
"simple" => {
// Phase 34-8: Loop パターンの詳細分析break/continue 検出)
self.lower_loop_with_break_continue(program_json)
"simple" | "filter" | "print_tokens" | "map" | "reduce" | "fold" => {
// Phase P3: LoopFrontendBinding 層経由でディスパッチ
loop_frontend_binding::lower_loop_by_function_name(self, program_json)
}
"parse_loop" => {
// Phase 41-4: NestedIfMerge pattern (dev flag gated)