diff --git a/src/mir/join_ir_ops.rs b/src/mir/join_ir_ops.rs index 6f052d9b..4c6736aa 100644 --- a/src/mir/join_ir_ops.rs +++ b/src/mir/join_ir_ops.rs @@ -271,3 +271,124 @@ mod tests { assert!(result.unwrap_err().message.contains("Type mismatch")); } } + +/// Phase 27-shortterm S-4.2: JoinValue ↔ VMValue conversion +/// +/// Provides bidirectional conversion between JoinIR's lightweight JoinValue +/// and VM's runtime VMValue representation. +/// +/// ## Conversion Strategy +/// - JoinValue::Int ↔ VMValue::Integer +/// - JoinValue::Bool ↔ VMValue::Bool +/// - JoinValue::Str ↔ VMValue::String +/// - JoinValue::Unit ↔ VMValue::Void +/// - VMValue::Float → Error (not supported in JoinValue) +/// - VMValue::Future → Error (not supported in JoinValue) +/// - VMValue::BoxRef → Error (requires NyashBox downcast) +impl JoinValue { + /// Convert JoinValue to VMValue for VM execution + /// + /// # Example + /// ```ignore + /// let join_val = JoinValue::Int(42); + /// let vm_val = join_val.to_vm_value(); + /// assert!(matches!(vm_val, VMValue::Integer(42))); + /// ``` + pub fn to_vm_value(&self) -> crate::backend::VMValue { + match self { + JoinValue::Int(i) => crate::backend::VMValue::Integer(*i), + JoinValue::Bool(b) => crate::backend::VMValue::Bool(*b), + JoinValue::Str(s) => crate::backend::VMValue::String(s.clone()), + JoinValue::Unit => crate::backend::VMValue::Void, + } + } + + /// Convert VMValue to JoinValue + /// + /// Returns error for VMValue types not representable in JoinValue + /// (Float, Future, BoxRef). + /// + /// # Example + /// ```ignore + /// let vm_val = VMValue::Integer(42); + /// let join_val = JoinValue::from_vm_value(&vm_val)?; + /// assert_eq!(join_val, JoinValue::Int(42)); + /// ``` + pub fn from_vm_value(vm_val: &crate::backend::VMValue) -> Result { + match vm_val { + crate::backend::VMValue::Integer(i) => Ok(JoinValue::Int(*i)), + crate::backend::VMValue::Bool(b) => Ok(JoinValue::Bool(*b)), + crate::backend::VMValue::String(s) => Ok(JoinValue::Str(s.clone())), + crate::backend::VMValue::Void => Ok(JoinValue::Unit), + crate::backend::VMValue::Float(_) => { + Err(JoinIrOpError::new("Float not supported in JoinValue")) + } + crate::backend::VMValue::Future(_) => { + Err(JoinIrOpError::new("Future not supported in JoinValue")) + } + crate::backend::VMValue::BoxRef(_) => { + Err(JoinIrOpError::new("BoxRef not directly convertible to JoinValue - requires NyashBox downcast")) + } + } + } +} + +#[cfg(test)] +mod vm_conversion_tests { + use super::*; + + #[test] + fn test_joinvalue_to_vmvalue() { + // Int → Integer + let join_int = JoinValue::Int(42); + let vm_val = join_int.to_vm_value(); + assert!(matches!(vm_val, crate::backend::VMValue::Integer(42))); + + // Bool → Bool + let join_bool = JoinValue::Bool(true); + let vm_val = join_bool.to_vm_value(); + assert!(matches!(vm_val, crate::backend::VMValue::Bool(true))); + + // Str → String + let join_str = JoinValue::Str("hello".to_string()); + let vm_val = join_str.to_vm_value(); + assert!(matches!(vm_val, crate::backend::VMValue::String(s) if s == "hello")); + + // Unit → Void + let join_unit = JoinValue::Unit; + let vm_val = join_unit.to_vm_value(); + assert!(matches!(vm_val, crate::backend::VMValue::Void)); + } + + #[test] + fn test_vmvalue_to_joinvalue() { + // Integer → Int + let vm_int = crate::backend::VMValue::Integer(42); + let join_val = JoinValue::from_vm_value(&vm_int).unwrap(); + assert_eq!(join_val, JoinValue::Int(42)); + + // Bool → Bool + let vm_bool = crate::backend::VMValue::Bool(false); + let join_val = JoinValue::from_vm_value(&vm_bool).unwrap(); + assert_eq!(join_val, JoinValue::Bool(false)); + + // String → Str + let vm_str = crate::backend::VMValue::String("world".to_string()); + let join_val = JoinValue::from_vm_value(&vm_str).unwrap(); + assert_eq!(join_val, JoinValue::Str("world".to_string())); + + // Void → Unit + let vm_void = crate::backend::VMValue::Void; + let join_val = JoinValue::from_vm_value(&vm_void).unwrap(); + assert_eq!(join_val, JoinValue::Unit); + } + + #[test] + fn test_vmvalue_to_joinvalue_unsupported() { + // Float → Error + let vm_float = crate::backend::VMValue::Float(3.14); + let result = JoinValue::from_vm_value(&vm_float); + assert!(result.is_err()); + assert!(result.unwrap_err().message.contains("Float not supported")); + } +} diff --git a/src/mir/join_ir_vm_bridge.rs b/src/mir/join_ir_vm_bridge.rs new file mode 100644 index 00000000..fcfba1c3 --- /dev/null +++ b/src/mir/join_ir_vm_bridge.rs @@ -0,0 +1,361 @@ +//! Phase 27-shortterm S-4: JoinIR → Rust VM Bridge +//! +//! 目的: JoinIR(正規化された IR)を Rust VM で実行するブリッジ層 +//! +//! ## Architecture +//! ```text +//! JoinIR (normalized) → MirModule → Rust VM → Result +//! ↑ ↑ ↑ +//! PHI bugs VM input Execution +//! eliminated format (GC, plugins) +//! ``` +//! +//! ## Design Principles +//! - JoinIR の正規化構造を保持したまま VM に渡す +//! - マッピングだけで済ませる(JoinIR でやった正規化は消えない) +//! - VM の機能(GC、プラグイン、エラーハンドリング)を活用 +//! +//! ## Minimal Instruction Set (S-4.3) +//! - **Compute**: Const, BinOp, Compare +//! - **BoxCall**: StringBox メソッド呼び出し +//! - **Call/Jump/Ret**: 制御フロー +//! +//! Phase 27-shortterm scope: skip_ws で green 化できれば成功 + +use crate::backend::{MirInterpreter, VMError, VMValue}; +use crate::mir::join_ir::{ + BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinInst, JoinModule, MirLikeInst, +}; +use crate::mir::join_ir_ops::JoinValue; +use crate::mir::{ + BasicBlockId, BinaryOp, CompareOp as MirCompareOp, ConstValue as MirConstValue, + EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule, MirType, ValueId, +}; +use std::collections::HashMap; + +/// Phase 27-shortterm S-4 エラー型 +#[derive(Debug, Clone)] +pub struct JoinIrVmBridgeError { + pub message: String, +} + +impl JoinIrVmBridgeError { + pub fn new(msg: impl Into) -> Self { + Self { + message: msg.into(), + } + } +} + +impl From for JoinIrVmBridgeError { + fn from(err: VMError) -> Self { + JoinIrVmBridgeError::new(format!("VM error: {:?}", err)) + } +} + +/// Phase 27-shortterm S-4.3: JoinIR → VM 実行のエントリーポイント +/// +/// ## Arguments +/// - `join_module`: JoinIR モジュール(正規化済み) +/// - `entry_func`: エントリーポイント関数ID +/// - `args`: 初期引数(JoinValue 形式) +/// +/// ## Returns +/// - `Ok(JoinValue)`: 実行結果 +/// - `Err(JoinIrVmBridgeError)`: 変換エラーまたは実行エラー +/// +/// ## Example +/// ```ignore +/// let join_module = lower_skip_ws_to_joinir(&mir_module)?; +/// let result = run_joinir_via_vm( +/// &join_module, +/// JoinFuncId::new(0), +/// &[JoinValue::Str(" hello".to_string()), JoinValue::Int(7)] +/// )?; +/// assert_eq!(result, JoinValue::Int(2)); +/// ``` +pub fn run_joinir_via_vm( + join_module: &JoinModule, + entry_func: JoinFuncId, + args: &[JoinValue], +) -> Result { + eprintln!("[joinir_vm_bridge] Phase 27-shortterm S-4.3"); + eprintln!( + "[joinir_vm_bridge] Converting JoinIR to MIR for VM execution" + ); + + // Step 1: JoinIR → MIR 変換 + let mir_module = convert_joinir_to_mir(join_module, entry_func)?; + + eprintln!( + "[joinir_vm_bridge] Converted {} JoinIR functions to MIR", + join_module.functions.len() + ); + + // Step 2: VM 実行 + let mut vm = MirInterpreter::new(); + + // 初期引数を VM に設定(暫定実装: 環境変数経由) + // TODO: S-4.4 で引数渡し機構を追加 + eprintln!( + "[joinir_vm_bridge] Executing via VM with {} arguments", + args.len() + ); + + let result_box = vm.execute_module(&mir_module)?; + + // Step 3: VMValue → JoinValue 変換 + let vm_value = VMValue::from_nyash_box(result_box); + let join_result = JoinValue::from_vm_value(&vm_value) + .map_err(|e| JoinIrVmBridgeError::new(format!("Result conversion error: {}", e.message)))?; + + eprintln!("[joinir_vm_bridge] Execution succeeded: {:?}", join_result); + + Ok(join_result) +} + +/// Phase 27-shortterm S-4.3: JoinIR → MIR 変換器 +/// +/// JoinIR の関数群を MIR モジュールに変換する。 +/// JoinIR の正規化構造(Pinned/Carrier、Exit φ)は関数引数として表現されているため、 +/// MIR に変換しても構造的意味は保持される。 +fn convert_joinir_to_mir( + join_module: &JoinModule, + entry_func: JoinFuncId, +) -> Result { + let mut mir_module = MirModule::new("joinir_bridge".to_string()); + + // すべての JoinIR 関数を MIR 関数に変換 + for (func_id, join_func) in &join_module.functions { + eprintln!( + "[joinir_vm_bridge] Converting JoinFunction {} ({})", + func_id.0, join_func.name + ); + + let mir_func = convert_join_function_to_mir(join_func)?; + + // Entry function を "main" として登録 + let func_name = if *func_id == entry_func { + "main".to_string() + } else { + format!("join_func_{}", func_id.0) + }; + + mir_module.functions.insert(func_name, mir_func); + } + + Ok(mir_module) +} + +/// JoinFunction → MirFunction 変換 +fn convert_join_function_to_mir( + join_func: &crate::mir::join_ir::JoinFunction, +) -> Result { + // Phase 27-shortterm S-4.3 最小実装: + // 1つの basic block に全命令を配置 + let entry_block = BasicBlockId(0); + + // Create minimal FunctionSignature for JoinIR function + // Phase 27-shortterm: すべて MirType::Unknown として扱う(型情報は JoinIR に無いため) + let param_types = join_func.params.iter() + .map(|_| MirType::Unknown) + .collect::>(); + + let signature = FunctionSignature { + name: join_func.name.clone(), + params: param_types, + return_type: MirType::Unknown, + effects: EffectMask::PURE, + }; + + let mut mir_func = MirFunction::new(signature, entry_block); + + let mut instructions = Vec::new(); + + // JoinInst → MirInstruction 変換 + for join_inst in &join_func.body { + match join_inst { + JoinInst::Compute(mir_like) => { + let mir_inst = convert_mir_like_inst(mir_like)?; + instructions.push(mir_inst); + } + JoinInst::Call { func, args, dst, .. } => { + // TODO: S-4.4 で関数呼び出し実装 + eprintln!( + "[joinir_vm_bridge] WARNING: Call instruction not yet implemented (func={:?}, args={:?}, dst={:?})", + func, args, dst + ); + } + JoinInst::Jump { cont, args, cond } => { + // TODO: S-4.4 で継続実装 + eprintln!( + "[joinir_vm_bridge] WARNING: Jump instruction not yet implemented (cont={:?}, args={:?}, cond={:?})", + cont, args, cond + ); + } + JoinInst::Ret { value } => { + if let Some(val_id) = value { + instructions.push(MirInstruction::Return { + value: Some(*val_id), + }); + } else { + instructions.push(MirInstruction::Return { value: None }); + } + } + } + } + + // Entry block に命令を登録 + if let Some(block) = mir_func.blocks.get_mut(&entry_block) { + block.instructions = instructions; + } + + Ok(mir_func) +} + +/// MirLikeInst → MirInstruction 変換 +fn convert_mir_like_inst( + mir_like: &MirLikeInst, +) -> Result { + match mir_like { + MirLikeInst::Const { dst, value } => { + let mir_const = match value { + ConstValue::Integer(i) => MirConstValue::Integer(*i), + ConstValue::Bool(b) => MirConstValue::Bool(*b), + ConstValue::String(s) => MirConstValue::String(s.clone()), + ConstValue::Null => MirConstValue::Null, + }; + Ok(MirInstruction::Const { + dst: *dst, + value: mir_const, + }) + } + MirLikeInst::BinOp { dst, op, lhs, rhs } => { + let mir_op = match op { + BinOpKind::Add => BinaryOp::Add, + BinOpKind::Sub => BinaryOp::Sub, + BinOpKind::Mul => BinaryOp::Mul, + BinOpKind::Div => BinaryOp::Div, + BinOpKind::Or => BinaryOp::Or, + BinOpKind::And => BinaryOp::And, + }; + Ok(MirInstruction::BinOp { + dst: *dst, + op: mir_op, + lhs: *lhs, + rhs: *rhs, + }) + } + MirLikeInst::Compare { dst, op, lhs, rhs } => { + let mir_cmp = match op { + CompareOp::Lt => MirCompareOp::Lt, + CompareOp::Le => MirCompareOp::Le, + CompareOp::Gt => MirCompareOp::Gt, + CompareOp::Ge => MirCompareOp::Ge, + CompareOp::Eq => MirCompareOp::Eq, + CompareOp::Ne => MirCompareOp::Ne, + }; + Ok(MirInstruction::Compare { + dst: *dst, + op: mir_cmp, + lhs: *lhs, + rhs: *rhs, + }) + } + MirLikeInst::BoxCall { + dst, + box_name, + method, + args, + } => { + // Phase 27-shortterm S-4.3: BoxCall → MIR BoxCall + // box_name は JoinIR で保持しているが、MIR BoxCall には receiver ValueId のみ + // 暫定: args[0] を receiver として扱う + if args.is_empty() { + return Err(JoinIrVmBridgeError::new(format!( + "BoxCall requires at least one argument (receiver), got: box_name={}, method={}", + box_name, method + ))); + } + + let receiver = args[0]; + let method_args = args[1..].to_vec(); + + Ok(MirInstruction::BoxCall { + dst: *dst, + box_val: receiver, + method: method.clone(), + method_id: None, // Phase 27-shortterm: no method ID resolution + args: method_args, + effects: EffectMask::PURE, // Phase 27-shortterm: assume pure + }) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_convert_const_inst() { + let join_const = MirLikeInst::Const { + dst: ValueId(10), + value: ConstValue::Integer(42), + }; + + let mir_inst = convert_mir_like_inst(&join_const).unwrap(); + + match mir_inst { + MirInstruction::Const { dst, value } => { + assert_eq!(dst, ValueId(10)); + assert!(matches!(value, MirConstValue::Integer(42))); + } + _ => panic!("Expected Const instruction"), + } + } + + #[test] + fn test_convert_binop_inst() { + let join_binop = MirLikeInst::BinOp { + dst: ValueId(20), + op: BinOpKind::Add, + lhs: ValueId(10), + rhs: ValueId(11), + }; + + let mir_inst = convert_mir_like_inst(&join_binop).unwrap(); + + match mir_inst { + MirInstruction::BinOp { dst, op, lhs, rhs } => { + assert_eq!(dst, ValueId(20)); + assert_eq!(op, BinaryOp::Add); + assert_eq!(lhs, ValueId(10)); + assert_eq!(rhs, ValueId(11)); + } + _ => panic!("Expected BinOp instruction"), + } + } + + #[test] + fn test_convert_compare_inst() { + let join_cmp = MirLikeInst::Compare { + dst: ValueId(30), + op: CompareOp::Ge, + lhs: ValueId(10), + rhs: ValueId(11), + }; + + let mir_inst = convert_mir_like_inst(&join_cmp).unwrap(); + + match mir_inst { + MirInstruction::Compare { dst, op, lhs, rhs } => { + assert_eq!(dst, ValueId(30)); + assert_eq!(op, MirCompareOp::Ge); + assert_eq!(lhs, ValueId(10)); + assert_eq!(rhs, ValueId(11)); + } + _ => panic!("Expected Compare instruction"), + } + } +} diff --git a/src/mir/mod.rs b/src/mir/mod.rs index 4f9f522e..b34e3dcb 100644 --- a/src/mir/mod.rs +++ b/src/mir/mod.rs @@ -40,6 +40,7 @@ pub mod query; // Phase 26-G: MIR read/write/CFGビュー (MirQuery) pub mod join_ir; // Phase 26-H: 関数正規化IR(JoinIR) pub mod join_ir_runner; // Phase 27.2: JoinIR 実行器(実験用) pub mod join_ir_ops; // Phase 27.8: JoinIR 命令意味箱(ops box) +pub mod join_ir_vm_bridge; // Phase 27-shortterm S-4: JoinIR → Rust VM ブリッジ pub mod verification; pub mod verification_types; // extracted error types // Optimization subpasses (e.g., type_hints) // Phase 25.1f: Loop/If 共通ビュー(ControlForm)