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:
@ -131,6 +131,16 @@
|
||||
- docs:
|
||||
- `docs/private/roadmap2/phases/phase-46-ifmerge-loop-reassign/README.md`
|
||||
|
||||
### 1-00i. Phase 49–56 — JoinIR Frontend 本線統合(print_tokens / filter)✅ 進行中
|
||||
|
||||
- Phase 49–52: `cf_loop` に JoinIR Frontend ルートを追加し(dev フラグ付き)、`LoopFrontendBinding` / JSON v0 / expr タイプ(Field/NewBox)を整備。
|
||||
- `HAKO_JOINIR_PRINT_TOKENS_MAIN` / `HAKO_JOINIR_ARRAY_FILTER_MAIN` で対象ループだけを Frontend 経由にルーティング。
|
||||
- `merge_joinir_mir_blocks` で JoinIR→MIR のブロック/値 ID リマップと制御フロー接続を実装。
|
||||
- Phase 53–55: statement lowering(Local/Assignment/Print/Method/If)と AST→JSON の `"type"`/`"expr"` 整備により、`JsonTokenizer.print_tokens/1` が JoinIR Frontend→Bridge→VM 経由で最後まで実行可能に(Route A/B 等価、dev フラグ ON 限定)。
|
||||
- Phase 56: `ArrayExtBox.filter/2` 向けに `LoopFrontendBinding::for_array_filter` を MethodCall ベース(`arr.size()`)に修正し、外部参照 `arr/pred` を Binding 経由で渡す構造に統一。
|
||||
- JoinIR に `ConditionalMethodCall` / Unary / Call を追加し、filter の「pred が true のときだけ push する」パターンを 4 ブロック構造で表現。
|
||||
- `HAKO_JOINIR_ARRAY_FILTER_MAIN=1` で Route B(JoinIR Frontend 経路)がフォールバックなし完走(テスト済み、既定は従来ルート)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 中期 TODO(ざっくり)
|
||||
|
||||
@ -30,8 +30,16 @@ pub fn ast_to_json(ast: &ASTNode) -> Value {
|
||||
"type": "Return", // JoinIR Frontend expects "type"
|
||||
"value": value.as_ref().map(|v| ast_to_json(v)),
|
||||
}),
|
||||
ASTNode::Break { .. } => json!({"kind":"Break"}),
|
||||
ASTNode::Continue { .. } => json!({"kind":"Continue"}),
|
||||
// Phase 56: Break with JoinIR-compatible type field
|
||||
ASTNode::Break { .. } => json!({
|
||||
"kind": "Break",
|
||||
"type": "Break" // JoinIR Frontend expects "type"
|
||||
}),
|
||||
// Phase 56: Continue with JoinIR-compatible type field
|
||||
ASTNode::Continue { .. } => json!({
|
||||
"kind": "Continue",
|
||||
"type": "Continue" // JoinIR Frontend expects "type"
|
||||
}),
|
||||
// Phase 54: Assignment with JoinIR-compatible fields
|
||||
ASTNode::Assignment { target, value, .. } => {
|
||||
// Extract variable name if target is a simple Variable
|
||||
@ -152,10 +160,12 @@ pub fn ast_to_json(ast: &ASTNode) -> Value {
|
||||
"right": ast_to_json(&right),
|
||||
})
|
||||
}
|
||||
// Phase 56: UnaryOp → Unary ノード(JoinIR Frontend 互換)
|
||||
ASTNode::UnaryOp {
|
||||
operator, operand, ..
|
||||
} => json!({
|
||||
"kind":"UnaryOp",
|
||||
"kind": "UnaryOp",
|
||||
"type": "Unary", // Phase 56: JoinIR Frontend expects "type" field
|
||||
"op": un_to_str(&operator),
|
||||
"operand": ast_to_json(&operand),
|
||||
}),
|
||||
@ -176,19 +186,27 @@ pub fn ast_to_json(ast: &ASTNode) -> Value {
|
||||
"args": arguments.iter().map(|a| ast_to_json(a)).collect::<Vec<_>>(),
|
||||
"arguments": arguments.into_iter().map(|a| ast_to_json(&a)).collect::<Vec<_>>() // Keep for backward compatibility
|
||||
}),
|
||||
// Phase 56: FunctionCall with JoinIR-compatible type field
|
||||
ASTNode::FunctionCall {
|
||||
name, arguments, ..
|
||||
} => json!({
|
||||
"kind":"FunctionCall",
|
||||
"kind": "FunctionCall",
|
||||
"type": "Call", // JoinIR Frontend expects "type": "Call"
|
||||
"name": name,
|
||||
"arguments": arguments.into_iter().map(|a| ast_to_json(&a)).collect::<Vec<_>>()
|
||||
"func": name.clone(), // JoinIR expects "func" for function name
|
||||
"args": arguments.iter().map(|a| ast_to_json(a)).collect::<Vec<_>>(), // JoinIR expects "args"
|
||||
"arguments": arguments.into_iter().map(|a| ast_to_json(&a)).collect::<Vec<_>>() // Keep for backward compatibility
|
||||
}),
|
||||
// Phase 56: ArrayLiteral with JoinIR-compatible type field
|
||||
ASTNode::ArrayLiteral { elements, .. } => json!({
|
||||
"kind":"Array",
|
||||
"kind": "Array",
|
||||
"type": "Array", // JoinIR Frontend expects "type"
|
||||
"elements": elements.into_iter().map(|e| ast_to_json(&e)).collect::<Vec<_>>()
|
||||
}),
|
||||
// Phase 56: MapLiteral with JoinIR-compatible type field
|
||||
ASTNode::MapLiteral { entries, .. } => json!({
|
||||
"kind":"Map",
|
||||
"kind": "Map",
|
||||
"type": "Map", // JoinIR Frontend expects "type"
|
||||
"entries": entries.into_iter().map(|(k,v)| json!({"k":k,"v":ast_to_json(&v)})).collect::<Vec<_>>()
|
||||
}),
|
||||
ASTNode::MatchExpr {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
//! Control-flow entrypoints (if/loop/try/throw) centralized here.
|
||||
use super::{Effect, EffectMask, MirInstruction, ValueId};
|
||||
use crate::ast::{ASTNode, Span};
|
||||
use crate::ast::ASTNode;
|
||||
|
||||
impl super::MirBuilder {
|
||||
/// Control-flow: block
|
||||
@ -173,16 +173,29 @@ impl super::MirBuilder {
|
||||
// Phase 50: Generate Local declarations from binding
|
||||
let (i_local, acc_local, n_local) = binding.generate_local_declarations();
|
||||
|
||||
// Phase 52: Check if `me` receiver is needed
|
||||
// Instance methods (like print_tokens) need `me` to be passed as a parameter
|
||||
let params: Vec<serde_json::Value> = if binding.needs_me_receiver() {
|
||||
// Phase 52/56: Build params from external_refs
|
||||
// Instance methods need `me`, static methods need their parameters (arr, pred, etc.)
|
||||
let mut params: Vec<serde_json::Value> = Vec::new();
|
||||
|
||||
// Phase 52: Add 'me' for instance methods
|
||||
if binding.needs_me_receiver() {
|
||||
if debug {
|
||||
eprintln!("[cf_loop/joinir] Adding 'me' to params (instance method)");
|
||||
}
|
||||
vec![serde_json::json!("me")]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
params.push(serde_json::json!("me"));
|
||||
}
|
||||
|
||||
// Phase 56: Add external_refs as parameters (arr, pred for filter)
|
||||
for ext_ref in &binding.external_refs {
|
||||
// Skip "me" and "me.*" as they're handled above
|
||||
if ext_ref == "me" || ext_ref.starts_with("me.") {
|
||||
continue;
|
||||
}
|
||||
if debug {
|
||||
eprintln!("[cf_loop/joinir] Adding '{}' to params (external_ref)", ext_ref);
|
||||
}
|
||||
params.push(serde_json::json!(ext_ref));
|
||||
}
|
||||
|
||||
// Step 2: Construct JSON v0 format with "defs" array
|
||||
// The function is named "simple" to match JoinIR Frontend's pattern matching
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -246,6 +246,82 @@ impl AstToJoinIrLowerer {
|
||||
(dst, insts)
|
||||
}
|
||||
|
||||
// Phase 56: Unary 対応(not 等)
|
||||
"Unary" => {
|
||||
let op = expr["op"]
|
||||
.as_str()
|
||||
.expect("Unary must have 'op' field");
|
||||
let operand_expr = &expr["operand"];
|
||||
|
||||
// operand を再帰的に extract_value
|
||||
let (operand_var, operand_insts) = self.extract_value(operand_expr, ctx);
|
||||
|
||||
// 結果変数を割り当て
|
||||
let dst = ctx.alloc_var();
|
||||
|
||||
// UnaryOp 命令を生成
|
||||
let unary_op = match op {
|
||||
"not" => crate::mir::join_ir::UnaryOp::Not,
|
||||
"-" => crate::mir::join_ir::UnaryOp::Neg,
|
||||
_ => panic!("Unsupported unary op: {}", op),
|
||||
};
|
||||
|
||||
let unary_inst = JoinInst::Compute(crate::mir::join_ir::MirLikeInst::UnaryOp {
|
||||
dst,
|
||||
op: unary_op,
|
||||
operand: operand_var,
|
||||
});
|
||||
|
||||
let mut insts = operand_insts;
|
||||
insts.push(unary_inst);
|
||||
|
||||
(dst, insts)
|
||||
}
|
||||
|
||||
// Phase 56: Call(関数呼び出し、pred(v) 等)
|
||||
// 変数参照の関数呼び出しは MethodCall で代替(receiver=func_var, method="__call__")
|
||||
"Call" => {
|
||||
let func_name = expr["func"]
|
||||
.as_str()
|
||||
.or_else(|| expr["name"].as_str())
|
||||
.expect("Call must have 'func' or 'name' field");
|
||||
let empty_args = vec![];
|
||||
let args_array = expr["args"]
|
||||
.as_array()
|
||||
.or_else(|| expr["arguments"].as_array())
|
||||
.unwrap_or(&empty_args);
|
||||
|
||||
// func_name を変数として取得(pred, callback 等)
|
||||
let func_var = ctx
|
||||
.get_var(func_name)
|
||||
.unwrap_or_else(|| panic!("Undefined function variable: {}", func_name));
|
||||
|
||||
// args を extract_value で処理
|
||||
let mut arg_vars = Vec::new();
|
||||
let mut arg_insts = Vec::new();
|
||||
for arg_expr in args_array {
|
||||
let (arg_var, arg_inst) = self.extract_value(arg_expr, ctx);
|
||||
arg_vars.push(arg_var);
|
||||
arg_insts.extend(arg_inst);
|
||||
}
|
||||
|
||||
// 結果変数を割り当て
|
||||
let dst = ctx.alloc_var();
|
||||
|
||||
// MethodCall で代替(func_var.__call__(args))
|
||||
let call_inst = JoinInst::MethodCall {
|
||||
dst,
|
||||
receiver: func_var,
|
||||
method: "__call__".to_string(),
|
||||
args: arg_vars,
|
||||
};
|
||||
|
||||
let mut insts = arg_insts;
|
||||
insts.push(call_inst);
|
||||
|
||||
(dst, insts)
|
||||
}
|
||||
|
||||
_ => panic!("Unsupported expr type: {}", expr_type),
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,6 +211,24 @@ impl AstToJoinIrLowerer {
|
||||
// Call(loop_step) 末尾再帰
|
||||
// - k_exit: 結果を返す
|
||||
|
||||
// Phase 56: Collect external_refs BEFORE entry_call_args construction
|
||||
// These are params that are not loop-carried variables (me, i, acc, n)
|
||||
// They need to be passed through to the step function for filter(arr, pred) etc.
|
||||
let reserved_vars = ["me", "i", "acc", "n"];
|
||||
let external_refs: Vec<(String, crate::mir::ValueId)> = params
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(idx, param)| {
|
||||
param.as_str().and_then(|name| {
|
||||
if !reserved_vars.contains(&name) {
|
||||
Some((name.to_string(), crate::mir::ValueId(idx as u32)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
let entry_id = self.next_func_id();
|
||||
let loop_step_id = self.next_func_id();
|
||||
let k_exit_id = self.next_func_id();
|
||||
@ -225,12 +243,19 @@ impl AstToJoinIrLowerer {
|
||||
|
||||
let loop_result = ctx.alloc_var();
|
||||
|
||||
// Phase 52: Include me in args when present
|
||||
let entry_call_args = if let Some(me_id) = me_param {
|
||||
// Phase 52/56: Include me and external_refs in args when present
|
||||
let mut entry_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]
|
||||
};
|
||||
// Phase 56: Add external_refs to entry call args
|
||||
// Get them from ctx using their original names
|
||||
for (name, _original_id) in &external_refs {
|
||||
if let Some(var_id) = ctx.get_var(name) {
|
||||
entry_call_args.push(var_id);
|
||||
}
|
||||
}
|
||||
|
||||
let mut entry_body = init_insts;
|
||||
entry_body.push(JoinInst::Call {
|
||||
@ -277,8 +302,15 @@ impl AstToJoinIrLowerer {
|
||||
)
|
||||
};
|
||||
|
||||
let num_step_params = if has_me { 4 } else { 3 };
|
||||
// Phase 56: external_refs was already collected above (before entry_call_args)
|
||||
// Calculate step function param count including external_refs
|
||||
let base_params = if has_me { 4 } else { 3 }; // me?, i, acc, n
|
||||
let num_step_params = base_params + external_refs.len() as u32;
|
||||
let mut step_ctx = ExtractCtx::new(num_step_params);
|
||||
|
||||
// Register standard params with adjusted offsets
|
||||
// Order: me?, i, acc, n, ...external_refs
|
||||
let ext_offset = if has_me { 4 } else { 3 };
|
||||
if let Some(me_id) = step_me {
|
||||
step_ctx.register_param("me".to_string(), me_id);
|
||||
}
|
||||
@ -286,6 +318,12 @@ impl AstToJoinIrLowerer {
|
||||
step_ctx.register_param("acc".to_string(), step_acc);
|
||||
step_ctx.register_param("n".to_string(), step_n);
|
||||
|
||||
// Phase 56: Register external_refs with new ValueIds starting after n
|
||||
for (i, (name, _original_id)) in external_refs.iter().enumerate() {
|
||||
let new_id = crate::mir::ValueId(ext_offset + i as u32);
|
||||
step_ctx.register_param(name.clone(), new_id);
|
||||
}
|
||||
|
||||
// 条件式を評価(i < n)
|
||||
let (cond_var, cond_insts) = self.extract_value(loop_cond_expr, &mut step_ctx);
|
||||
|
||||
@ -332,12 +370,18 @@ impl AstToJoinIrLowerer {
|
||||
.expect("acc must be updated in loop body");
|
||||
|
||||
// loop_step を再帰的に Call(末尾再帰)
|
||||
// Phase 52: Include me in args when present
|
||||
let recurse_args = if let Some(me_id) = step_me {
|
||||
// Phase 52/56: Include me and external_refs in args when present
|
||||
let mut recurse_args = if let Some(me_id) = step_me {
|
||||
vec![me_id, i_next, acc_next, step_n]
|
||||
} else {
|
||||
vec![i_next, acc_next, step_n]
|
||||
};
|
||||
// Phase 56: Add external_refs to recurse args (they are passed through unchanged)
|
||||
for (name, _) in &external_refs {
|
||||
if let Some(var_id) = step_ctx.get_var(name) {
|
||||
recurse_args.push(var_id);
|
||||
}
|
||||
}
|
||||
|
||||
let recurse_result = step_ctx.alloc_var();
|
||||
loop_step_body.push(JoinInst::Call {
|
||||
@ -351,12 +395,17 @@ impl AstToJoinIrLowerer {
|
||||
value: Some(recurse_result),
|
||||
});
|
||||
|
||||
// Phase 52: Include me in params when present
|
||||
let loop_step_params = if let Some(me_id) = step_me {
|
||||
// Phase 52/56: Include me and external_refs in params when present
|
||||
let ext_offset = if has_me { 4 } else { 3 };
|
||||
let mut loop_step_params = if let Some(me_id) = step_me {
|
||||
vec![me_id, step_i, step_acc, step_n]
|
||||
} else {
|
||||
vec![step_i, step_acc, step_n]
|
||||
};
|
||||
// Phase 56: Add external_refs to step params
|
||||
for (i, _) in external_refs.iter().enumerate() {
|
||||
loop_step_params.push(crate::mir::ValueId(ext_offset + i as u32));
|
||||
}
|
||||
|
||||
let loop_step_func = JoinFunction {
|
||||
id: loop_step_id,
|
||||
|
||||
@ -233,7 +233,65 @@ impl AstToJoinIrLowerer {
|
||||
}
|
||||
}
|
||||
|
||||
// ケース 4: 複雑なケース(Phase 54 で対応)
|
||||
// ケース 4: Phase 56 条件付き側効果パターン(filter 用)
|
||||
// if pred(v) { acc.push(v) }
|
||||
// then: 1 statement (MethodCall/Method), else: empty
|
||||
if then_stmts.len() == 1 && else_stmts.is_empty() {
|
||||
let stmt = &then_stmts[0];
|
||||
let stmt_type = stmt["type"].as_str();
|
||||
|
||||
// MethodCall/Method 形式のステートメントをチェック
|
||||
if matches!(stmt_type, Some("Method") | Some("MethodCall")) {
|
||||
let receiver_expr = stmt.get("receiver").or_else(|| stmt.get("object"));
|
||||
let method_name = stmt["method"].as_str();
|
||||
let args_array = stmt.get("args").or_else(|| stmt.get("arguments"));
|
||||
|
||||
if let (Some(receiver_expr), Some(method_name), Some(args_array)) =
|
||||
(receiver_expr, method_name, args_array)
|
||||
{
|
||||
// receiver を評価
|
||||
let (receiver_var, receiver_insts) = self.extract_value(receiver_expr, ctx);
|
||||
insts.extend(receiver_insts);
|
||||
|
||||
// args を評価
|
||||
let mut arg_vars = Vec::new();
|
||||
if let Some(args) = args_array.as_array() {
|
||||
for arg_expr in args {
|
||||
let (arg_var, arg_insts) = self.extract_value(arg_expr, ctx);
|
||||
arg_vars.push(arg_var);
|
||||
insts.extend(arg_insts);
|
||||
}
|
||||
}
|
||||
|
||||
// 結果変数を割り当て(push は配列を返すので結果は無視されることが多い)
|
||||
let dst = ctx.alloc_var();
|
||||
|
||||
// ConditionalMethodCall 命令を生成
|
||||
insts.push(JoinInst::ConditionalMethodCall {
|
||||
cond: cond_id,
|
||||
dst,
|
||||
receiver: receiver_var,
|
||||
method: method_name.to_string(),
|
||||
args: arg_vars,
|
||||
});
|
||||
|
||||
// receiver 変数を更新された値で登録(push は receiver を返す)
|
||||
// この場合、accumulator 変数 "acc" または "out" を更新する必要がある
|
||||
let receiver_name = receiver_expr["name"].as_str().unwrap_or("acc");
|
||||
ctx.register_param(receiver_name.to_string(), dst);
|
||||
|
||||
return (
|
||||
insts,
|
||||
StatementEffect::VarUpdate {
|
||||
name: receiver_name.to_string(),
|
||||
value_id: dst,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ケース 5: 複雑なケース(未対応)
|
||||
panic!(
|
||||
"Complex If statement in loop body not yet supported (Phase 54). \
|
||||
then: {} stmts, else: {} stmts",
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
use super::{BinOpKind, CompareOp, ConstValue, JoinFunction, JoinInst, JoinModule, MirLikeInst};
|
||||
use super::{BinOpKind, CompareOp, ConstValue, JoinFunction, JoinInst, JoinModule, MirLikeInst, UnaryOp};
|
||||
|
||||
/// JoinModule を JSON としてシリアライズする
|
||||
///
|
||||
@ -195,6 +195,29 @@ fn write_inst<W: Write>(inst: &JoinInst, out: &mut W) -> std::io::Result<()> {
|
||||
write!(out, "]")?;
|
||||
write!(out, "}}")?;
|
||||
}
|
||||
// Phase 56: ConditionalMethodCall instruction JSON serialization
|
||||
JoinInst::ConditionalMethodCall {
|
||||
cond,
|
||||
dst,
|
||||
receiver,
|
||||
method,
|
||||
args,
|
||||
} => {
|
||||
write!(out, "{{\"type\":\"conditional_method_call\"")?;
|
||||
write!(out, ",\"cond\":{}", cond.0)?;
|
||||
write!(out, ",\"dst\":{}", dst.0)?;
|
||||
write!(out, ",\"receiver\":{}", receiver.0)?;
|
||||
write!(out, ",\"method\":\"{}\"", escape_json_string(method))?;
|
||||
write!(out, ",\"args\":[")?;
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(out, ",")?;
|
||||
}
|
||||
write!(out, "{}", arg.0)?;
|
||||
}
|
||||
write!(out, "]")?;
|
||||
write!(out, "}}")?;
|
||||
}
|
||||
// Phase 41-4: NestedIfMerge instruction JSON serialization
|
||||
JoinInst::NestedIfMerge {
|
||||
conds,
|
||||
@ -331,6 +354,14 @@ fn write_mir_like_inst<W: Write>(inst: &MirLikeInst, out: &mut W) -> std::io::Re
|
||||
write!(out, "]")?;
|
||||
write!(out, "}}")?;
|
||||
}
|
||||
// Phase 56: UnaryOp
|
||||
MirLikeInst::UnaryOp { dst, op, operand } => {
|
||||
write!(out, "{{\"kind\":\"unaryop\"")?;
|
||||
write!(out, ",\"dst\":{}", dst.0)?;
|
||||
write!(out, ",\"op\":\"{}\"", unaryop_to_str(*op))?;
|
||||
write!(out, ",\"operand\":{}", operand.0)?;
|
||||
write!(out, "}}")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -357,6 +388,14 @@ fn compare_to_str(op: CompareOp) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 56: UnaryOp to string
|
||||
fn unaryop_to_str(op: UnaryOp) -> &'static str {
|
||||
match op {
|
||||
UnaryOp::Not => "not",
|
||||
UnaryOp::Neg => "neg",
|
||||
}
|
||||
}
|
||||
|
||||
/// JSON 文字列のエスケープ
|
||||
fn escape_json_string(s: &str) -> String {
|
||||
let mut escaped = String::with_capacity(s.len());
|
||||
|
||||
@ -322,6 +322,19 @@ pub enum JoinInst {
|
||||
args: Vec<VarId>,
|
||||
},
|
||||
|
||||
/// Phase 56: 条件付きメソッド呼び出し(filter パターン用)
|
||||
/// cond が true の場合のみ receiver.method(args) を実行
|
||||
/// cond が false の場合は dst = receiver(変更なし)
|
||||
///
|
||||
/// 使用例: `if pred(v) { acc.push(v) }` → ConditionalMethodCall
|
||||
ConditionalMethodCall {
|
||||
cond: VarId,
|
||||
dst: VarId,
|
||||
receiver: VarId,
|
||||
method: String,
|
||||
args: Vec<VarId>,
|
||||
},
|
||||
|
||||
/// Phase 51: フィールドアクセス
|
||||
/// object.field の構造を JoinIR で表現
|
||||
/// MIR 変換時に Load 命令に変換
|
||||
@ -405,6 +418,22 @@ pub enum MirLikeInst {
|
||||
method: String,
|
||||
args: Vec<VarId>,
|
||||
},
|
||||
|
||||
/// Phase 56: 単項演算(not, 負号)
|
||||
UnaryOp {
|
||||
dst: VarId,
|
||||
op: UnaryOp,
|
||||
operand: VarId,
|
||||
},
|
||||
}
|
||||
|
||||
/// Phase 56: 単項演算種別
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum UnaryOp {
|
||||
/// 論理否定
|
||||
Not,
|
||||
/// 算術否定(負号)
|
||||
Neg,
|
||||
}
|
||||
|
||||
/// 定数値(MIR の ConstValue を簡略化)
|
||||
|
||||
@ -217,6 +217,14 @@ fn execute_function(
|
||||
"MethodCall is not supported in JoinIR Runner (use JoinIR→MIR→VM bridge instead)"
|
||||
));
|
||||
}
|
||||
// Phase 56: ConditionalMethodCall instruction execution
|
||||
JoinInst::ConditionalMethodCall { .. } => {
|
||||
// Phase 56: ConditionalMethodCall は JoinIR Runner では未対応
|
||||
// JoinIR → MIR 変換経由で VM が実行する
|
||||
return Err(JoinRuntimeError::new(
|
||||
"ConditionalMethodCall is not supported in JoinIR Runner (use JoinIR→MIR→VM bridge instead)"
|
||||
));
|
||||
}
|
||||
// Phase 41-4: NestedIfMerge instruction execution
|
||||
JoinInst::NestedIfMerge { .. } => {
|
||||
// Phase 41-4: NestedIfMerge は JoinIR Runner では未対応
|
||||
@ -323,6 +331,32 @@ fn eval_compute(
|
||||
locals.insert(*dst_var, result_jv);
|
||||
}
|
||||
}
|
||||
// Phase 56: UnaryOp
|
||||
MirLikeInst::UnaryOp { dst, op, operand } => {
|
||||
let operand_val = read_var(locals, *operand)?;
|
||||
let result = match op {
|
||||
crate::mir::join_ir::UnaryOp::Not => {
|
||||
match operand_val {
|
||||
JoinValue::Bool(b) => JoinValue::Bool(!b),
|
||||
JoinValue::Int(i) => JoinValue::Bool(i == 0),
|
||||
_ => return Err(JoinRuntimeError::new(format!(
|
||||
"Cannot apply 'not' to {:?}",
|
||||
operand_val
|
||||
))),
|
||||
}
|
||||
}
|
||||
crate::mir::join_ir::UnaryOp::Neg => {
|
||||
match operand_val {
|
||||
JoinValue::Int(i) => JoinValue::Int(-i),
|
||||
_ => return Err(JoinRuntimeError::new(format!(
|
||||
"Cannot apply '-' to {:?}",
|
||||
operand_val
|
||||
))),
|
||||
}
|
||||
}
|
||||
};
|
||||
locals.insert(*dst, result);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -217,6 +217,89 @@ pub(crate) fn convert_join_function_to_mir(
|
||||
};
|
||||
current_instructions.push(mir_inst);
|
||||
}
|
||||
// Phase 56: ConditionalMethodCall → MIR (cond ? method : no-op)
|
||||
JoinInst::ConditionalMethodCall {
|
||||
cond,
|
||||
dst,
|
||||
receiver,
|
||||
method,
|
||||
args,
|
||||
} => {
|
||||
// Phase 56: ConditionalMethodCall を MIR の if/phi に変換
|
||||
// cond が true の場合: receiver.method(args) → dst
|
||||
// cond が false の場合: receiver → dst (変更なし)
|
||||
//
|
||||
// 4 ブロック構造: cond -> then/else -> merge
|
||||
|
||||
debug_log!(
|
||||
"[joinir_vm_bridge] Converting ConditionalMethodCall: dst={:?}, cond={:?}, receiver={:?}, method={}",
|
||||
dst, cond, receiver, method
|
||||
);
|
||||
|
||||
// 1. cond ブロック(現在のブロック)
|
||||
let cond_block = current_block_id;
|
||||
|
||||
// 2. then ブロック作成
|
||||
let then_block = BasicBlockId(next_block_id);
|
||||
next_block_id += 1;
|
||||
|
||||
// 3. else ブロック作成
|
||||
let else_block = BasicBlockId(next_block_id);
|
||||
next_block_id += 1;
|
||||
|
||||
// 4. merge ブロック作成
|
||||
let merge_block = BasicBlockId(next_block_id);
|
||||
next_block_id += 1;
|
||||
|
||||
// 5. cond ブロックで分岐
|
||||
let branch_terminator = MirInstruction::Branch {
|
||||
condition: *cond,
|
||||
then_bb: then_block,
|
||||
else_bb: else_block,
|
||||
};
|
||||
finalize_block(
|
||||
&mut mir_func,
|
||||
cond_block,
|
||||
current_instructions,
|
||||
branch_terminator,
|
||||
);
|
||||
|
||||
// 6. then ブロック: dst = receiver.method(args); jump merge
|
||||
let mut then_block_obj = crate::mir::BasicBlock::new(then_block);
|
||||
then_block_obj.instructions.push(MirInstruction::BoxCall {
|
||||
dst: Some(*dst),
|
||||
box_val: *receiver,
|
||||
method: method.clone(),
|
||||
method_id: None,
|
||||
args: args.clone(),
|
||||
effects: EffectMask::PURE,
|
||||
});
|
||||
then_block_obj.instruction_spans.push(Span::unknown());
|
||||
then_block_obj.terminator = Some(MirInstruction::Jump {
|
||||
target: merge_block,
|
||||
});
|
||||
mir_func.blocks.insert(then_block, then_block_obj);
|
||||
|
||||
// 7. else ブロック: dst = receiver (no-op, keep original); jump merge
|
||||
let mut else_block_obj = crate::mir::BasicBlock::new(else_block);
|
||||
else_block_obj.instructions.push(MirInstruction::Copy {
|
||||
dst: *dst,
|
||||
src: *receiver,
|
||||
});
|
||||
else_block_obj.instruction_spans.push(Span::unknown());
|
||||
else_block_obj.terminator = Some(MirInstruction::Jump {
|
||||
target: merge_block,
|
||||
});
|
||||
mir_func.blocks.insert(else_block, else_block_obj);
|
||||
|
||||
// 8. merge ブロック作成(空)
|
||||
let merge_block_obj = crate::mir::BasicBlock::new(merge_block);
|
||||
mir_func.blocks.insert(merge_block, merge_block_obj);
|
||||
|
||||
// 9. merge ブロックに移動
|
||||
current_block_id = merge_block;
|
||||
current_instructions = Vec::new();
|
||||
}
|
||||
// Phase 51: FieldAccess → MIR BoxCall (getter pattern)
|
||||
JoinInst::FieldAccess { dst, object, field } => {
|
||||
// object.field を BoxCall(object, field, []) に変換
|
||||
@ -832,5 +915,17 @@ pub(crate) fn convert_mir_like_inst(
|
||||
effects: EffectMask::PURE, // Phase 27-shortterm: assume pure
|
||||
})
|
||||
}
|
||||
// Phase 56: UnaryOp
|
||||
MirLikeInst::UnaryOp { dst, op, operand } => {
|
||||
let mir_op = match op {
|
||||
crate::mir::join_ir::UnaryOp::Not => crate::mir::types::UnaryOp::Not,
|
||||
crate::mir::join_ir::UnaryOp::Neg => crate::mir::types::UnaryOp::Neg,
|
||||
};
|
||||
Ok(MirInstruction::UnaryOp {
|
||||
dst: *dst,
|
||||
op: mir_op,
|
||||
operand: *operand,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user