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:
@ -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)
|
||||||
146
src/mir/join_ir/frontend/ast_lowerer/loop_frontend_binding.rs
Normal file
146
src/mir/join_ir/frontend/ast_lowerer/loop_frontend_binding.rs
Normal 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 の 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
160
src/mir/join_ir/frontend/ast_lowerer/loop_patterns/README.md
Normal file
160
src/mir/join_ir/frontend/ast_lowerer/loop_patterns/README.md
Normal 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 だけを見る
|
||||||
@ -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,
|
||||||
|
})
|
||||||
|
}
|
||||||
407
src/mir/join_ir/frontend/ast_lowerer/loop_patterns/common.rs
Normal file
407
src/mir/join_ir/frontend/ast_lowerer/loop_patterns/common.rs
Normal 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_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<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),
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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,
|
||||||
|
})
|
||||||
|
}
|
||||||
53
src/mir/join_ir/frontend/ast_lowerer/loop_patterns/filter.rs
Normal file
53
src/mir/join_ir/frontend/ast_lowerer/loop_patterns/filter.rs
Normal 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)
|
||||||
|
}
|
||||||
120
src/mir/join_ir/frontend/ast_lowerer/loop_patterns/mod.rs
Normal file
120
src/mir/join_ir/frontend/ast_lowerer/loop_patterns/mod.rs
Normal 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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)
|
||||||
|
}
|
||||||
151
src/mir/join_ir/frontend/ast_lowerer/loop_patterns/simple.rs
Normal file
151
src/mir/join_ir/frontend/ast_lowerer/loop_patterns/simple.rs
Normal 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 が 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,
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -30,7 +30,9 @@ mod context;
|
|||||||
mod expr;
|
mod expr;
|
||||||
mod if_in_loop;
|
mod if_in_loop;
|
||||||
mod if_return;
|
mod if_return;
|
||||||
|
mod loop_frontend_binding;
|
||||||
mod loop_patterns;
|
mod loop_patterns;
|
||||||
|
mod loop_patterns_old;
|
||||||
mod nested_if;
|
mod nested_if;
|
||||||
mod read_quoted;
|
mod read_quoted;
|
||||||
mod stmt_handlers;
|
mod stmt_handlers;
|
||||||
@ -83,18 +85,20 @@ impl AstToJoinIrLowerer {
|
|||||||
.as_str()
|
.as_str()
|
||||||
.expect("Function must have 'name'");
|
.expect("Function must have 'name'");
|
||||||
|
|
||||||
// 3. 関数名で分岐(Phase 34-2/34-3/34-4/34-5/34-7/34-8/41-4/45)
|
// 3. 関数名で分岐(Phase P3: LoopFrontendBinding 層導入)
|
||||||
// test/local/_read_value_from_pair: If Return pattern
|
//
|
||||||
// simple: Loop pattern (Phase 34-7/34-8)
|
// パターン分類:
|
||||||
// parse_loop: Phase 41-4 NestedIfMerge pattern
|
// - If Return pattern: test/local/_read_value_from_pair
|
||||||
// read_quoted_from: Phase 45 Guard if + Loop with break + accumulator
|
// - Loop pattern: simple 等 → LoopFrontendBinding 経由
|
||||||
|
// - NestedIfMerge pattern: parse_loop (dev flag gated)
|
||||||
|
// - ReadQuoted pattern: read_quoted_from (dev flag gated)
|
||||||
match func_name {
|
match func_name {
|
||||||
"test" | "local" | "_read_value_from_pair" => {
|
"test" | "local" | "_read_value_from_pair" => {
|
||||||
self.lower_if_return_pattern(program_json)
|
self.lower_if_return_pattern(program_json)
|
||||||
}
|
}
|
||||||
"simple" => {
|
"simple" | "filter" | "print_tokens" | "map" | "reduce" | "fold" => {
|
||||||
// Phase 34-8: Loop パターンの詳細分析(break/continue 検出)
|
// Phase P3: LoopFrontendBinding 層経由でディスパッチ
|
||||||
self.lower_loop_with_break_continue(program_json)
|
loop_frontend_binding::lower_loop_by_function_name(self, program_json)
|
||||||
}
|
}
|
||||||
"parse_loop" => {
|
"parse_loop" => {
|
||||||
// Phase 41-4: NestedIfMerge pattern (dev flag gated)
|
// Phase 41-4: NestedIfMerge pattern (dev flag gated)
|
||||||
|
|||||||
Reference in New Issue
Block a user