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,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)