feat(joinir): Phase 56 ArrayExtBox.filter JoinIR lowering完全実装

## Summary
ArrayExtBox.filter/2 の JoinIR Frontend lowering を完全実装し、
ConditionalMethodCall 命令を導入して filter パターンに対応。
56 JoinIR テスト全て PASS(退行なし)。

## Technical Changes

### 1. ConditionalMethodCall 命令追加
- **新規命令**: `if pred(v) { acc.push(v) }` パターン用
- **構造**: cond が true なら method 実行、false なら no-op
- **MIR 変換**: 4ブロック構造 (cond→then/else→merge)

### 2. AST JSON 拡張
- Break/Continue/FunctionCall に "type" フィールド追加
- ArrayLiteral/MapLiteral に "type" フィールド追加
- JoinIR Frontend 互換性向上

### 3. Expression Handler 拡張
- Unary 演算子(not, 負号)サポート
- Call(変数関数呼び出し)を MethodCall に変換

### 4. Loop Pattern Binding 修正
- `BoundExpr::Variable("n")` 問題修正
- `MethodCall { receiver: "arr", method: "size" }` に変更
- external_refs (arr, pred) を step 関数に伝播

### 5. If Statement Handler 拡張
- 条件付き側効果パターン(ケース4)追加
- MethodCall/Method 形式の statement を ConditionalMethodCall に変換

## Files Modified (10 files, +456/-45 lines)
- ast_json.rs: AST JSON "type" フィールド追加
- loop_frontend_binding.rs: n バインディング修正
- control_flow.rs: external_refs params 追加
- loop_patterns.rs: external_refs step 関数伝播
- expr.rs: Unary, Call handler 追加
- stmt_handlers.rs: ConditionalMethodCall パターン追加
- mod.rs: ConditionalMethodCall, UnaryOp 定義
- json.rs: ConditionalMethodCall, UnaryOp シリアライズ
- join_ir_runner.rs: ConditionalMethodCall, UnaryOp スタブ
- convert.rs: ConditionalMethodCall → MIR 変換

## Test Results
- 56 JoinIR tests:  PASSED
- Regression:  None
- ArrayExtBox.filter/2:  JoinIR lowering 成功

## Milestone
JoinIR 2ループ完走達成:
-  JsonTokenizer.print_tokens/0
-  ArrayExtBox.filter/2 (NEW!)

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-29 06:51:43 +09:00
parent ee6e95b164
commit ad9daf37ac
11 changed files with 456 additions and 45 deletions

View File

@ -49,8 +49,6 @@ pub enum BoundExpr {
Variable(String),
/// メソッド呼び出し (e.g., "me.tokens", "length")
MethodCall { receiver: String, method: String },
/// 不明(フォールバック用)
Unknown,
}
/// ループパターン種別
@ -62,12 +60,6 @@ pub enum LoopPattern {
/// フィルターパターン (if + push)
Filter,
/// マップパターン (変換 + push)
Map,
/// 不明(フォールバック用)
Unknown,
}
impl LoopFrontendBinding {
@ -114,7 +106,7 @@ impl LoopFrontendBinding {
/// ```nyash
/// local out = new ArrayBox()
/// local i = 0
/// local n = arr.size()
/// local n = arr.size() // ← JoinIR では arr.size() を直接評価
/// loop(i < n) {
/// local v = arr.get(i)
/// if pred(v) { out.push(v) }
@ -122,12 +114,21 @@ impl LoopFrontendBinding {
/// }
/// return out
/// ```
///
/// Phase 56: `BoundExpr::Variable("n")` から `BoundExpr::MethodCall` に変更。
/// `n` はループ外で宣言されているため、JoinIR コンテキストに存在しない。
/// 代わりに `arr.size()` を直接呼び出して `n` を初期化する。
pub fn for_array_filter() -> Self {
Self {
counter_var: "i".to_string(),
counter_init: 0,
accumulator_var: Some("out".to_string()), // filter has "out" as accumulator
bound_expr: BoundExpr::Variable("n".to_string()),
// Phase 56: Variable("n") → MethodCall { arr, size }
// n はループ外で宣言されているため、arr.size() を直接呼び出す
bound_expr: BoundExpr::MethodCall {
receiver: "arr".to_string(),
method: "size".to_string(),
},
external_refs: vec!["arr".to_string(), "pred".to_string()],
pattern: LoopPattern::Filter,
}
@ -264,17 +265,6 @@ impl LoopFrontendBinding {
}
})
}
BoundExpr::Unknown => {
// フォールバック: 0 を使う
json!({
"type": "Local",
"name": "n",
"expr": {
"type": "Int",
"value": 0
}
})
}
};
(i_local, acc_local, n_local)