diff --git a/docs/development/refactoring/p2-loop-pattern-boxification-plan.md b/docs/development/refactoring/p2-loop-pattern-boxification-plan.md new file mode 100644 index 00000000..c72b20f3 --- /dev/null +++ b/docs/development/refactoring/p2-loop-pattern-boxification-plan.md @@ -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) diff --git a/src/mir/join_ir/frontend/ast_lowerer/loop_frontend_binding.rs b/src/mir/join_ir/frontend/ast_lowerer/loop_frontend_binding.rs new file mode 100644 index 00000000..4f92f6e3 --- /dev/null +++ b/src/mir/join_ir/frontend/ast_lowerer/loop_frontend_binding.rs @@ -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 の JSON(Break/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); + } +} diff --git a/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/README.md b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/README.md new file mode 100644 index 00000000..0409ee7c --- /dev/null +++ b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/README.md @@ -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; +} +``` + +## 📊 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 だけを見る diff --git a/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/break_pattern.rs b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/break_pattern.rs new file mode 100644 index 00000000..0f241b6c --- /dev/null +++ b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/break_pattern.rs @@ -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 { + // 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, + 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 { + 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, + }) +} diff --git a/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/common.rs b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/common.rs new file mode 100644 index 00000000..c48f3911 --- /dev/null +++ b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/common.rs @@ -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, + /// 関数 body の statements + pub stmts: Vec, + /// 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_refs(Phase 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 = 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 = 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 { + 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, + 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 { + 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 { + 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), + } +} diff --git a/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/continue_pattern.rs b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/continue_pattern.rs new file mode 100644 index 00000000..c2c17858 --- /dev/null +++ b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/continue_pattern.rs @@ -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 { + // 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, + 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 { + 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, + }) +} diff --git a/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/filter.rs b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/filter.rs new file mode 100644 index 00000000..a9417481 --- /dev/null +++ b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/filter.rs @@ -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 { + // Phase 56: Filter パターンは Simple パターンと同じ構造 + // 差分は stmt_handlers での if 内 MethodCall 処理のみ + // → Simple に委譲 + super::simple::lower(lowerer, program_json) +} diff --git a/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/mod.rs b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/mod.rs new file mode 100644 index 00000000..9e2d4d5d --- /dev/null +++ b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/mod.rs @@ -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; +} + +/// 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 { + 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), + } +} diff --git a/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/print_tokens.rs b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/print_tokens.rs new file mode 100644 index 00000000..fbd59ada --- /dev/null +++ b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/print_tokens.rs @@ -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 { + // Phase 55: PrintTokens パターンは Simple パターンと同じ構造 + // 差分は stmt_handlers での MethodCall 処理のみ + // → Simple に委譲 + super::simple::lower(lowerer, program_json) +} diff --git a/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/simple.rs b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/simple.rs new file mode 100644 index 00000000..6e520c6b --- /dev/null +++ b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/simple.rs @@ -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 { + // 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 { + // 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 が true(i >= 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, + }) +} diff --git a/src/mir/join_ir/frontend/ast_lowerer/loop_patterns.rs b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns_old.rs similarity index 100% rename from src/mir/join_ir/frontend/ast_lowerer/loop_patterns.rs rename to src/mir/join_ir/frontend/ast_lowerer/loop_patterns_old.rs diff --git a/src/mir/join_ir/frontend/ast_lowerer/mod.rs b/src/mir/join_ir/frontend/ast_lowerer/mod.rs index b1604ab2..568c51ba 100644 --- a/src/mir/join_ir/frontend/ast_lowerer/mod.rs +++ b/src/mir/join_ir/frontend/ast_lowerer/mod.rs @@ -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)