diff --git a/local_tests/basic_mir.nyash b/local_tests/basic_mir.nyash new file mode 100644 index 00000000..8af57f9e --- /dev/null +++ b/local_tests/basic_mir.nyash @@ -0,0 +1,5 @@ +// Basic MIR test - minimal scope only +x = 42 +y = 10 +result = x + y +print("Result: " + result) \ No newline at end of file diff --git a/src/backend/mod.rs b/src/backend/mod.rs new file mode 100644 index 00000000..ade22e44 --- /dev/null +++ b/src/backend/mod.rs @@ -0,0 +1,7 @@ +/*! + * Backend module - Different execution backends for MIR + */ + +pub mod vm; + +pub use vm::{VM, VMError}; \ No newline at end of file diff --git a/src/backend/vm.rs b/src/backend/vm.rs new file mode 100644 index 00000000..3fc58241 --- /dev/null +++ b/src/backend/vm.rs @@ -0,0 +1,409 @@ +/*! + * VM Backend - Execute MIR instructions in a virtual machine + * + * Simple stack-based VM for executing MIR code + */ + +use crate::mir::{MirModule, MirFunction, MirInstruction, ConstValue, BinaryOp, CompareOp, UnaryOp, ValueId, BasicBlockId}; +use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox}; +use std::collections::HashMap; + +/// VM execution error +#[derive(Debug)] +pub enum VMError { + InvalidValue(String), + InvalidInstruction(String), + InvalidBasicBlock(String), + DivisionByZero, + StackUnderflow, + TypeError(String), +} + +impl std::fmt::Display for VMError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + VMError::InvalidValue(msg) => write!(f, "Invalid value: {}", msg), + VMError::InvalidInstruction(msg) => write!(f, "Invalid instruction: {}", msg), + VMError::InvalidBasicBlock(msg) => write!(f, "Invalid basic block: {}", msg), + VMError::DivisionByZero => write!(f, "Division by zero"), + VMError::StackUnderflow => write!(f, "Stack underflow"), + VMError::TypeError(msg) => write!(f, "Type error: {}", msg), + } + } +} + +impl std::error::Error for VMError {} + +/// VM value representation +#[derive(Debug, Clone)] +pub enum VMValue { + Integer(i64), + Float(f64), + Bool(bool), + String(String), + Void, +} + +impl VMValue { + /// Convert to NyashBox for output + pub fn to_nyash_box(&self) -> Box { + match self { + VMValue::Integer(i) => Box::new(IntegerBox::new(*i)), + VMValue::Float(f) => Box::new(StringBox::new(&f.to_string())), // Simplified for now + VMValue::Bool(b) => Box::new(BoolBox::new(*b)), + VMValue::String(s) => Box::new(StringBox::new(s)), + VMValue::Void => Box::new(VoidBox::new()), + } + } + + /// Get string representation for printing + pub fn to_string(&self) -> String { + match self { + VMValue::Integer(i) => i.to_string(), + VMValue::Float(f) => f.to_string(), + VMValue::Bool(b) => b.to_string(), + VMValue::String(s) => s.clone(), + VMValue::Void => "void".to_string(), + } + } + + /// Attempt to convert to integer + pub fn as_integer(&self) -> Result { + match self { + VMValue::Integer(i) => Ok(*i), + _ => Err(VMError::TypeError(format!("Expected integer, got {:?}", self))), + } + } + + /// Attempt to convert to bool + pub fn as_bool(&self) -> Result { + match self { + VMValue::Bool(b) => Ok(*b), + VMValue::Integer(i) => Ok(*i != 0), + _ => Err(VMError::TypeError(format!("Expected bool, got {:?}", self))), + } + } +} + +impl From<&ConstValue> for VMValue { + fn from(const_val: &ConstValue) -> Self { + match const_val { + ConstValue::Integer(i) => VMValue::Integer(*i), + ConstValue::Float(f) => VMValue::Float(*f), + ConstValue::Bool(b) => VMValue::Bool(*b), + ConstValue::String(s) => VMValue::String(s.clone()), + ConstValue::Null => VMValue::Void, // Simplified + ConstValue::Void => VMValue::Void, + } + } +} + +/// Virtual Machine state +pub struct VM { + /// Value storage (maps ValueId to actual values) + values: HashMap, + /// Current function being executed + current_function: Option, + /// Current basic block + current_block: Option, + /// Program counter within current block + pc: usize, + /// Return value from last execution + last_result: Option, +} + +impl VM { + /// Create a new VM instance + pub fn new() -> Self { + Self { + values: HashMap::new(), + current_function: None, + current_block: None, + pc: 0, + last_result: None, + } + } + + /// Execute a MIR module + pub fn execute_module(&mut self, module: &MirModule) -> Result, VMError> { + // Find main function + let main_function = module.get_function("main") + .ok_or_else(|| VMError::InvalidInstruction("No main function found".to_string()))?; + + // Execute main function + let result = self.execute_function(main_function)?; + + // Convert result to NyashBox + Ok(result.to_nyash_box()) + } + + /// Execute a single function + fn execute_function(&mut self, function: &MirFunction) -> Result { + self.current_function = Some(function.signature.name.clone()); + + // Start at entry block + let mut current_block = function.entry_block; + + loop { + let block = function.get_block(current_block) + .ok_or_else(|| VMError::InvalidBasicBlock(format!("Block {} not found", current_block)))?; + + self.current_block = Some(current_block); + self.pc = 0; + + let mut next_block = None; + let mut should_return = None; + + // Execute instructions in this block + for (index, instruction) in block.instructions.iter().enumerate() { + self.pc = index; + + match self.execute_instruction(instruction)? { + ControlFlow::Continue => continue, + ControlFlow::Jump(target) => { + next_block = Some(target); + break; + }, + ControlFlow::Return(value) => { + should_return = Some(value); + break; + }, + } + } + + // Handle control flow + if let Some(return_value) = should_return { + return Ok(return_value); + } else if let Some(target) = next_block { + current_block = target; + } else { + // Block ended without terminator - this shouldn't happen in well-formed MIR + // but let's handle it gracefully by returning void + return Ok(VMValue::Void); + } + } + } + + /// Execute a single instruction + fn execute_instruction(&mut self, instruction: &MirInstruction) -> Result { + match instruction { + MirInstruction::Const { dst, value } => { + let vm_value = VMValue::from(value); + self.values.insert(*dst, vm_value); + Ok(ControlFlow::Continue) + }, + + MirInstruction::BinOp { dst, op, lhs, rhs } => { + let left = self.get_value(*lhs)?; + let right = self.get_value(*rhs)?; + let result = self.execute_binary_op(op, &left, &right)?; + self.values.insert(*dst, result); + Ok(ControlFlow::Continue) + }, + + MirInstruction::UnaryOp { dst, op, operand } => { + let operand_val = self.get_value(*operand)?; + let result = self.execute_unary_op(op, &operand_val)?; + self.values.insert(*dst, result); + Ok(ControlFlow::Continue) + }, + + MirInstruction::Compare { dst, op, lhs, rhs } => { + let left = self.get_value(*lhs)?; + let right = self.get_value(*rhs)?; + let result = self.execute_compare_op(op, &left, &right)?; + self.values.insert(*dst, VMValue::Bool(result)); + Ok(ControlFlow::Continue) + }, + + MirInstruction::Print { value, .. } => { + let val = self.get_value(*value)?; + println!("{}", val.to_string()); + Ok(ControlFlow::Continue) + }, + + MirInstruction::Return { value } => { + let return_value = if let Some(val_id) = value { + self.get_value(*val_id)? + } else { + VMValue::Void + }; + Ok(ControlFlow::Return(return_value)) + }, + + MirInstruction::Jump { target } => { + Ok(ControlFlow::Jump(*target)) + }, + + MirInstruction::Branch { condition, then_bb, else_bb } => { + let cond_val = self.get_value(*condition)?; + let cond_bool = cond_val.as_bool()?; + + if cond_bool { + Ok(ControlFlow::Jump(*then_bb)) + } else { + Ok(ControlFlow::Jump(*else_bb)) + } + }, + + MirInstruction::Phi { dst, inputs } => { + // For now, simplified phi - use first available input + // In a real implementation, we'd need to track which block we came from + if let Some((_, value_id)) = inputs.first() { + let value = self.get_value(*value_id)?; + self.values.insert(*dst, value); + } + Ok(ControlFlow::Continue) + }, + + _ => { + Err(VMError::InvalidInstruction(format!("Unsupported instruction: {:?}", instruction))) + } + } + } + + /// Get a value from storage + fn get_value(&self, value_id: ValueId) -> Result { + self.values.get(&value_id) + .cloned() + .ok_or_else(|| VMError::InvalidValue(format!("Value {} not found", value_id))) + } + + /// Execute binary operation + fn execute_binary_op(&self, op: &BinaryOp, left: &VMValue, right: &VMValue) -> Result { + match (left, right) { + (VMValue::Integer(l), VMValue::Integer(r)) => { + let result = match op { + BinaryOp::Add => *l + *r, + BinaryOp::Sub => *l - *r, + BinaryOp::Mul => *l * *r, + BinaryOp::Div => { + if *r == 0 { + return Err(VMError::DivisionByZero); + } + *l / *r + }, + _ => return Err(VMError::InvalidInstruction(format!("Unsupported integer operation: {:?}", op))), + }; + Ok(VMValue::Integer(result)) + }, + + (VMValue::String(l), VMValue::Integer(r)) => { + // String + Integer concatenation + match op { + BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))), + _ => Err(VMError::TypeError("String-integer operations only support addition".to_string())), + } + }, + + (VMValue::String(l), VMValue::String(r)) => { + // String concatenation + match op { + BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))), + _ => Err(VMError::TypeError("String operations only support addition".to_string())), + } + }, + + _ => Err(VMError::TypeError(format!("Unsupported binary operation: {:?} on {:?} and {:?}", op, left, right))), + } + } + + /// Execute unary operation + fn execute_unary_op(&self, op: &UnaryOp, operand: &VMValue) -> Result { + match (op, operand) { + (UnaryOp::Neg, VMValue::Integer(i)) => Ok(VMValue::Integer(-i)), + (UnaryOp::Not, VMValue::Bool(b)) => Ok(VMValue::Bool(!b)), + _ => Err(VMError::TypeError(format!("Unsupported unary operation: {:?} on {:?}", op, operand))), + } + } + + /// Execute comparison operation + fn execute_compare_op(&self, op: &CompareOp, left: &VMValue, right: &VMValue) -> Result { + match (left, right) { + (VMValue::Integer(l), VMValue::Integer(r)) => { + let result = match op { + CompareOp::Eq => l == r, + CompareOp::Ne => l != r, + CompareOp::Lt => l < r, + CompareOp::Le => l <= r, + CompareOp::Gt => l > r, + CompareOp::Ge => l >= r, + }; + Ok(result) + }, + + (VMValue::String(l), VMValue::String(r)) => { + let result = match op { + CompareOp::Eq => l == r, + CompareOp::Ne => l != r, + CompareOp::Lt => l < r, + CompareOp::Le => l <= r, + CompareOp::Gt => l > r, + CompareOp::Ge => l >= r, + }; + Ok(result) + }, + + _ => Err(VMError::TypeError(format!("Unsupported comparison: {:?} on {:?} and {:?}", op, left, right))), + } + } +} + +/// Control flow result from instruction execution +enum ControlFlow { + Continue, + Jump(BasicBlockId), + Return(VMValue), +} + +impl Default for VM { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mir::{MirModule, MirFunction, FunctionSignature, MirType, EffectMask, BasicBlock}; + + #[test] + fn test_basic_vm_execution() { + let mut vm = VM::new(); + + // Test constant loading + let const_instr = MirInstruction::Const { + dst: ValueId(1), + value: ConstValue::Integer(42), + }; + + let result = vm.execute_instruction(&const_instr); + assert!(result.is_ok()); + + let value = vm.get_value(ValueId(1)).unwrap(); + assert_eq!(value.as_integer().unwrap(), 42); + } + + #[test] + fn test_binary_operations() { + let mut vm = VM::new(); + + // Load constants + vm.values.insert(ValueId(1), VMValue::Integer(10)); + vm.values.insert(ValueId(2), VMValue::Integer(32)); + + // Test addition + let add_instr = MirInstruction::BinOp { + dst: ValueId(3), + op: BinaryOp::Add, + lhs: ValueId(1), + rhs: ValueId(2), + }; + + let result = vm.execute_instruction(&add_instr); + assert!(result.is_ok()); + + let value = vm.get_value(ValueId(3)).unwrap(); + assert_eq!(value.as_integer().unwrap(), 42); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 4d9bad3c..6db0f846 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,6 +31,10 @@ use interpreter::NyashInterpreter; // 🚀 MIR Infrastructure pub mod mir; use mir::{MirCompiler, MirPrinter}; + +// 🚀 Backend Infrastructure +pub mod backend; +use backend::VM; use std::env; use std::fs; use std::process; @@ -73,6 +77,13 @@ fn main() { .help("Show verbose MIR output with statistics") .action(clap::ArgAction::SetTrue) ) + .arg( + Arg::new("backend") + .long("backend") + .value_name("BACKEND") + .help("Choose execution backend: 'interpreter' (default) or 'vm'") + .default_value("interpreter") + ) .get_matches(); // ãƒ‡ãƒãƒƒã‚°į‡ƒæ–™ãŪč§Ģ析 @@ -82,12 +93,16 @@ fn main() { let dump_mir = matches.get_flag("dump-mir"); let verify_mir = matches.get_flag("verify"); let mir_verbose = matches.get_flag("mir-verbose"); + let backend = matches.get_one::("backend").unwrap(); if let Some(filename) = matches.get_one::("file") { // File mode: parse and execute the provided .nyash file if dump_mir || verify_mir { println!("🚀 Nyash MIR Compiler - Processing file: {} 🚀", filename); execute_mir_mode(filename, dump_mir, verify_mir, mir_verbose); + } else if backend == "vm" { + println!("🚀 Nyash VM Backend - Executing file: {} 🚀", filename); + execute_vm_mode(filename); } else { println!("ðŸĶ€ Nyash Rust Implementation - Executing file: {} ðŸĶ€", filename); if let Some(fuel) = debug_fuel { @@ -1170,6 +1185,59 @@ fn execute_mir_mode(filename: &str, dump_mir: bool, verify_mir: bool, verbose: b } } +/// Execute VM mode +fn execute_vm_mode(filename: &str) { + // Read the source file + let source = match fs::read_to_string(filename) { + Ok(content) => content, + Err(e) => { + eprintln!("❌ Error reading file '{}': {}", filename, e); + process::exit(1); + } + }; + + // Parse to AST + let ast = match NyashParser::parse_from_string(&source) { + Ok(ast) => ast, + Err(e) => { + eprintln!("❌ Parse error: {}", e); + process::exit(1); + } + }; + + // Compile to MIR + let mut compiler = MirCompiler::new(); + let compile_result = match compiler.compile(ast) { + Ok(result) => result, + Err(e) => { + eprintln!("❌ MIR compilation error: {}", e); + process::exit(1); + } + }; + + // Check for verification errors + if let Err(errors) = &compile_result.verification_result { + eprintln!("❌ MIR verification failed with {} error(s):", errors.len()); + for (i, error) in errors.iter().enumerate() { + eprintln!(" {}: {}", i + 1, error); + } + // Continue execution anyway for now + } + + // Execute with VM + let mut vm = VM::new(); + match vm.execute_module(&compile_result.module) { + Ok(result) => { + println!("✅ VM execution completed successfully!"); + println!("Result: {}", result.to_string_box().value); + }, + Err(e) => { + eprintln!("❌ VM runtime error: {}", e); + process::exit(1); + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/mir/builder.rs b/src/mir/builder.rs index 69ae98c0..10fc9913 100644 --- a/src/mir/builder.rs +++ b/src/mir/builder.rs @@ -132,6 +132,10 @@ impl MirBuilder { self.build_function_call(name.clone(), arguments.clone()) }, + ASTNode::Print { expression, .. } => { + self.build_print_statement(*expression.clone()) + }, + ASTNode::Program { statements, .. } => { self.build_block(statements.clone()) }, @@ -271,6 +275,20 @@ impl MirBuilder { Ok(dst) } + /// Build print statement - converts to console output + fn build_print_statement(&mut self, expression: ASTNode) -> Result { + let value = self.build_expression(expression)?; + + // For now, use a special Print instruction (minimal scope) + self.emit_instruction(MirInstruction::Print { + value, + effects: EffectMask::PURE.add(Effect::IO), + })?; + + // Return the value that was printed + Ok(value) + } + /// Build a block of statements fn build_block(&mut self, statements: Vec) -> Result { let mut last_value = None; diff --git a/src/mir/instruction.rs b/src/mir/instruction.rs index c1064e18..56b9ee75 100644 --- a/src/mir/instruction.rs +++ b/src/mir/instruction.rs @@ -4,7 +4,7 @@ * SSA-form instructions with effect tracking for optimization */ -use super::{ValueId, LocalId, EffectMask, Effect}; +use super::{ValueId, EffectMask, Effect}; // use crate::value::NyashValue; // Commented out to avoid circular dependency use std::fmt; @@ -169,6 +169,13 @@ pub enum MirInstruction { message: String, }, + /// Print instruction for console output + /// `print %value` + Print { + value: ValueId, + effects: EffectMask, + }, + /// No-op instruction (for optimization placeholders) Nop, } @@ -264,6 +271,9 @@ impl MirInstruction { // Debug has debug effect MirInstruction::Debug { .. } => EffectMask::PURE.add(Effect::Debug), + + // Print has external write effect + MirInstruction::Print { effects, .. } => *effects, } } @@ -291,6 +301,7 @@ impl MirInstruction { MirInstruction::Return { .. } | MirInstruction::ArraySet { .. } | MirInstruction::Debug { .. } | + MirInstruction::Print { .. } | MirInstruction::Nop => None, } } @@ -307,7 +318,8 @@ impl MirInstruction { MirInstruction::TypeCheck { value: operand, .. } | MirInstruction::Cast { value: operand, .. } | MirInstruction::Copy { src: operand, .. } | - MirInstruction::Debug { value: operand, .. } => vec![*operand], + MirInstruction::Debug { value: operand, .. } | + MirInstruction::Print { value: operand, .. } => vec![*operand], MirInstruction::BinOp { lhs, rhs, .. } | MirInstruction::Compare { lhs, rhs, .. } | diff --git a/src/mir/printer.rs b/src/mir/printer.rs index 79400174..636c7892 100644 --- a/src/mir/printer.rs +++ b/src/mir/printer.rs @@ -294,6 +294,10 @@ impl MirPrinter { format!("debug {} \"{}\"", value, message) }, + MirInstruction::Print { value, effects: _ } => { + format!("print {}", value) + }, + MirInstruction::Nop => { "nop".to_string() }, diff --git a/tests/mir_snapshots/basic_arithmetic.mir b/tests/mir_snapshots/basic_arithmetic.mir new file mode 100644 index 00000000..85df4c03 --- /dev/null +++ b/tests/mir_snapshots/basic_arithmetic.mir @@ -0,0 +1,12 @@ +; MIR Module: main + +define void @main() { +bb0: + 0: %0 = const 42 + 1: %1 = const 10 + 2: %2 = %0 Add %1 + 3: print %2 + 4: ret %2 +} + + diff --git a/tests/mir_snapshots/basic_arithmetic.nyash b/tests/mir_snapshots/basic_arithmetic.nyash new file mode 100644 index 00000000..7a77ae7b --- /dev/null +++ b/tests/mir_snapshots/basic_arithmetic.nyash @@ -0,0 +1,2 @@ +// Basic arithmetic test - expression statement +print(42 + 10) \ No newline at end of file diff --git a/tests/mir_snapshots/comparison.mir b/tests/mir_snapshots/comparison.mir new file mode 100644 index 00000000..62191f5e --- /dev/null +++ b/tests/mir_snapshots/comparison.mir @@ -0,0 +1,12 @@ +; MIR Module: main + +define void @main() { +bb0: + 0: %0 = const 42 + 1: %1 = const 42 + 2: %2 = icmp Eq %0, %1 + 3: print %2 + 4: ret %2 +} + + diff --git a/tests/mir_snapshots/comparison.nyash b/tests/mir_snapshots/comparison.nyash new file mode 100644 index 00000000..0efeaf16 --- /dev/null +++ b/tests/mir_snapshots/comparison.nyash @@ -0,0 +1,2 @@ +// Comparison test +print(42 == 42) \ No newline at end of file diff --git a/tests/mir_snapshots/string_concat.mir b/tests/mir_snapshots/string_concat.mir new file mode 100644 index 00000000..2cced2e7 --- /dev/null +++ b/tests/mir_snapshots/string_concat.mir @@ -0,0 +1,12 @@ +; MIR Module: main + +define void @main() { +bb0: + 0: %0 = const "Hello" + 1: %1 = const "World" + 2: %2 = %0 Add %1 + 3: print %2 + 4: ret %2 +} + + diff --git a/tests/mir_snapshots/string_concat.nyash b/tests/mir_snapshots/string_concat.nyash new file mode 100644 index 00000000..68277b71 --- /dev/null +++ b/tests/mir_snapshots/string_concat.nyash @@ -0,0 +1,2 @@ +// String concatenation test +print("Hello" + "World") \ No newline at end of file