2025-08-13 05:59:10 +00:00
|
|
|
/*!
|
|
|
|
|
* 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),
|
2025-08-13 11:46:01 +00:00
|
|
|
Future(crate::boxes::future::FutureBox),
|
2025-08-13 05:59:10 +00:00
|
|
|
Void,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl VMValue {
|
|
|
|
|
/// Convert to NyashBox for output
|
|
|
|
|
pub fn to_nyash_box(&self) -> Box<dyn NyashBox> {
|
|
|
|
|
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)),
|
2025-08-13 11:46:01 +00:00
|
|
|
VMValue::Future(f) => Box::new(f.clone()),
|
2025-08-13 05:59:10 +00:00
|
|
|
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(),
|
2025-08-13 11:46:01 +00:00
|
|
|
VMValue::Future(f) => f.to_string_box().value,
|
2025-08-13 05:59:10 +00:00
|
|
|
VMValue::Void => "void".to_string(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Attempt to convert to integer
|
|
|
|
|
pub fn as_integer(&self) -> Result<i64, VMError> {
|
|
|
|
|
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<bool, VMError> {
|
|
|
|
|
match self {
|
|
|
|
|
VMValue::Bool(b) => Ok(*b),
|
|
|
|
|
VMValue::Integer(i) => Ok(*i != 0),
|
|
|
|
|
_ => Err(VMError::TypeError(format!("Expected bool, got {:?}", self))),
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-13 11:46:01 +00:00
|
|
|
|
|
|
|
|
/// Convert from NyashBox to VMValue
|
|
|
|
|
pub fn from_nyash_box(nyash_box: Box<dyn crate::box_trait::NyashBox>) -> VMValue {
|
|
|
|
|
// Try to downcast to known types
|
|
|
|
|
if let Some(int_box) = nyash_box.as_any().downcast_ref::<IntegerBox>() {
|
|
|
|
|
VMValue::Integer(int_box.value)
|
|
|
|
|
} else if let Some(bool_box) = nyash_box.as_any().downcast_ref::<BoolBox>() {
|
|
|
|
|
VMValue::Bool(bool_box.value)
|
|
|
|
|
} else if let Some(string_box) = nyash_box.as_any().downcast_ref::<StringBox>() {
|
|
|
|
|
VMValue::String(string_box.value.clone())
|
|
|
|
|
} else if let Some(future_box) = nyash_box.as_any().downcast_ref::<crate::boxes::future::FutureBox>() {
|
|
|
|
|
VMValue::Future(future_box.clone())
|
|
|
|
|
} else {
|
|
|
|
|
// For any other type, convert to string representation
|
|
|
|
|
VMValue::String(nyash_box.to_string_box().value)
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-13 05:59:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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<ValueId, VMValue>,
|
|
|
|
|
/// Current function being executed
|
|
|
|
|
current_function: Option<String>,
|
|
|
|
|
/// Current basic block
|
|
|
|
|
current_block: Option<BasicBlockId>,
|
|
|
|
|
/// Program counter within current block
|
|
|
|
|
pc: usize,
|
|
|
|
|
/// Return value from last execution
|
2025-08-16 17:39:04 +09:00
|
|
|
#[allow(dead_code)]
|
2025-08-13 05:59:10 +00:00
|
|
|
last_result: Option<VMValue>,
|
2025-08-13 10:20:37 +00:00
|
|
|
/// Simple field storage for objects (maps reference -> field -> value)
|
|
|
|
|
object_fields: HashMap<ValueId, HashMap<String, VMValue>>,
|
2025-08-13 05:59:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
2025-08-13 10:20:37 +00:00
|
|
|
object_fields: HashMap::new(),
|
2025-08-13 05:59:10 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Execute a MIR module
|
|
|
|
|
pub fn execute_module(&mut self, module: &MirModule) -> Result<Box<dyn NyashBox>, 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<VMValue, VMError> {
|
|
|
|
|
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;
|
|
|
|
|
|
2025-08-13 12:18:47 +00:00
|
|
|
// Execute instructions in this block (including terminator)
|
|
|
|
|
let all_instructions: Vec<_> = block.all_instructions().collect();
|
|
|
|
|
println!("Executing block {} with {} instructions", current_block, all_instructions.len());
|
|
|
|
|
for (index, instruction) in all_instructions.iter().enumerate() {
|
2025-08-13 05:59:10 +00:00
|
|
|
self.pc = index;
|
2025-08-13 11:59:22 +00:00
|
|
|
println!(" Instruction {}: {:?}", index, instruction);
|
2025-08-13 05:59:10 +00:00
|
|
|
|
|
|
|
|
match self.execute_instruction(instruction)? {
|
|
|
|
|
ControlFlow::Continue => continue,
|
|
|
|
|
ControlFlow::Jump(target) => {
|
|
|
|
|
next_block = Some(target);
|
|
|
|
|
break;
|
|
|
|
|
},
|
|
|
|
|
ControlFlow::Return(value) => {
|
|
|
|
|
should_return = Some(value);
|
|
|
|
|
break;
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-13 11:59:22 +00:00
|
|
|
println!("Block execution finished. should_return: {:?}, next_block: {:?}", should_return.is_some(), next_block.is_some());
|
|
|
|
|
|
2025-08-13 05:59:10 +00:00
|
|
|
// 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<ControlFlow, VMError> {
|
2025-08-13 11:59:22 +00:00
|
|
|
println!("Executing instruction: {:?}", instruction);
|
2025-08-13 05:59:10 +00:00
|
|
|
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)
|
|
|
|
|
},
|
|
|
|
|
|
2025-08-17 12:27:12 +09:00
|
|
|
// Phase 3: UnaryOp removed - now handled by Call intrinsics (@unary_neg, @unary_not, etc.)
|
2025-08-13 05:59:10 +00:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
},
|
|
|
|
|
|
2025-08-17 12:27:12 +09:00
|
|
|
// Phase 3: Print removed - now handled by Call intrinsic (@print)
|
2025-08-13 05:59:10 +00:00
|
|
|
|
|
|
|
|
MirInstruction::Return { value } => {
|
|
|
|
|
let return_value = if let Some(val_id) = value {
|
2025-08-13 11:59:22 +00:00
|
|
|
let val = self.get_value(*val_id)?;
|
|
|
|
|
println!("Return: returning value from {:?} = {:?}", val_id, val);
|
|
|
|
|
val
|
2025-08-13 05:59:10 +00:00
|
|
|
} else {
|
2025-08-13 11:59:22 +00:00
|
|
|
println!("Return: returning void (no value specified)");
|
2025-08-13 05:59:10 +00:00
|
|
|
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)
|
|
|
|
|
},
|
|
|
|
|
|
2025-08-17 12:27:12 +09:00
|
|
|
// Phase 3: Load/Store removed - now handled by BoxFieldLoad/BoxFieldStore
|
2025-08-13 09:45:22 +00:00
|
|
|
|
2025-08-17 12:27:12 +09:00
|
|
|
MirInstruction::Call { dst, func, args, effects: _ } => {
|
|
|
|
|
// Phase 2: Handle intrinsic function calls
|
|
|
|
|
let func_value = self.get_value(*func)?;
|
|
|
|
|
|
|
|
|
|
if let VMValue::String(func_name) = func_value {
|
|
|
|
|
if func_name.starts_with('@') {
|
|
|
|
|
// This is an intrinsic call
|
|
|
|
|
let result = self.execute_intrinsic(&func_name, args)?;
|
|
|
|
|
if let Some(dst_id) = dst {
|
|
|
|
|
self.values.insert(*dst_id, result);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Regular function call - not implemented yet
|
|
|
|
|
if let Some(dst_id) = dst {
|
|
|
|
|
self.values.insert(*dst_id, VMValue::Void);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Non-string function - not implemented yet
|
|
|
|
|
if let Some(dst_id) = dst {
|
|
|
|
|
self.values.insert(*dst_id, VMValue::Void);
|
|
|
|
|
}
|
2025-08-13 09:45:22 +00:00
|
|
|
}
|
|
|
|
|
Ok(ControlFlow::Continue)
|
|
|
|
|
},
|
|
|
|
|
|
2025-08-14 03:39:08 +00:00
|
|
|
MirInstruction::BoxCall { dst, box_val, method, args, effects: _ } => {
|
|
|
|
|
// Get the box value
|
|
|
|
|
let box_vm_value = self.get_value(*box_val)?;
|
|
|
|
|
let box_nyash = box_vm_value.to_nyash_box();
|
|
|
|
|
|
|
|
|
|
// Evaluate arguments
|
|
|
|
|
let mut arg_values = Vec::new();
|
|
|
|
|
for arg_id in args {
|
|
|
|
|
let arg_vm_value = self.get_value(*arg_id)?;
|
|
|
|
|
arg_values.push(arg_vm_value.to_nyash_box());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Call the method - this mimics interpreter method dispatch
|
|
|
|
|
let result = self.call_box_method(box_nyash, method, arg_values)?;
|
|
|
|
|
|
|
|
|
|
// Store result if destination is specified
|
2025-08-13 09:45:22 +00:00
|
|
|
if let Some(dst_id) = dst {
|
2025-08-14 03:39:08 +00:00
|
|
|
let vm_result = VMValue::from_nyash_box(result);
|
|
|
|
|
self.values.insert(*dst_id, vm_result);
|
2025-08-13 09:45:22 +00:00
|
|
|
}
|
|
|
|
|
Ok(ControlFlow::Continue)
|
|
|
|
|
},
|
|
|
|
|
|
2025-08-14 03:46:06 +00:00
|
|
|
MirInstruction::NewBox { dst, box_type, args: _ } => {
|
|
|
|
|
// Implement basic box creation for common types
|
|
|
|
|
let result = match box_type.as_str() {
|
|
|
|
|
"StringBox" => {
|
|
|
|
|
// Create empty StringBox - in real implementation would use args
|
|
|
|
|
let string_box = Box::new(StringBox::new(""));
|
|
|
|
|
VMValue::from_nyash_box(string_box)
|
|
|
|
|
},
|
|
|
|
|
"ArrayBox" => {
|
|
|
|
|
// Create empty ArrayBox - in real implementation would use args
|
|
|
|
|
let array_box = Box::new(crate::boxes::array::ArrayBox::new());
|
|
|
|
|
VMValue::from_nyash_box(array_box)
|
|
|
|
|
},
|
|
|
|
|
"IntegerBox" => {
|
|
|
|
|
// Create IntegerBox with default value
|
|
|
|
|
let int_box = Box::new(IntegerBox::new(0));
|
|
|
|
|
VMValue::from_nyash_box(int_box)
|
|
|
|
|
},
|
|
|
|
|
"BoolBox" => {
|
|
|
|
|
// Create BoolBox with default value
|
|
|
|
|
let bool_box = Box::new(BoolBox::new(false));
|
|
|
|
|
VMValue::from_nyash_box(bool_box)
|
|
|
|
|
},
|
|
|
|
|
_ => {
|
|
|
|
|
// For unknown types, create a placeholder string
|
|
|
|
|
VMValue::String(format!("NewBox[{}]", box_type))
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
self.values.insert(*dst, result);
|
2025-08-13 09:45:22 +00:00
|
|
|
Ok(ControlFlow::Continue)
|
|
|
|
|
},
|
|
|
|
|
|
2025-08-17 14:06:47 +09:00
|
|
|
// Phase 5: Removed instructions - TypeCheck, Cast, ArrayGet, ArraySet, Copy, Debug, Nop
|
|
|
|
|
#[allow(deprecated)]
|
|
|
|
|
MirInstruction::TypeCheck { .. } |
|
|
|
|
|
MirInstruction::Cast { .. } |
|
|
|
|
|
MirInstruction::ArrayGet { .. } |
|
|
|
|
|
MirInstruction::ArraySet { .. } |
|
|
|
|
|
MirInstruction::Copy { .. } |
|
|
|
|
|
MirInstruction::Debug { .. } |
|
2025-08-13 06:23:28 +00:00
|
|
|
MirInstruction::Nop => {
|
2025-08-17 14:06:47 +09:00
|
|
|
Err(VMError::InvalidInstruction(
|
|
|
|
|
"Phase 5: Deprecated instruction - use 26-instruction set replacements".to_string()
|
|
|
|
|
))
|
2025-08-13 06:23:28 +00:00
|
|
|
},
|
|
|
|
|
|
2025-08-17 14:06:47 +09:00
|
|
|
// Phase 5: Removed instructions - Throw, Catch
|
|
|
|
|
#[allow(deprecated)]
|
|
|
|
|
MirInstruction::Throw { .. } |
|
|
|
|
|
MirInstruction::Catch { .. } => {
|
|
|
|
|
Err(VMError::InvalidInstruction(
|
|
|
|
|
"Phase 5: Exception handling via intrinsics - use Call with @throw/@catch".to_string()
|
|
|
|
|
))
|
2025-08-13 06:23:28 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
MirInstruction::Safepoint => {
|
|
|
|
|
// Safepoint is a no-op for now
|
|
|
|
|
// In a real implementation, this could trigger GC, debugging, etc.
|
|
|
|
|
Ok(ControlFlow::Continue)
|
|
|
|
|
},
|
|
|
|
|
|
2025-08-17 14:06:47 +09:00
|
|
|
// Phase 5: Removed instruction - RefNew
|
|
|
|
|
#[allow(deprecated)]
|
|
|
|
|
MirInstruction::RefNew { .. } => {
|
|
|
|
|
Err(VMError::InvalidInstruction(
|
|
|
|
|
"Phase 5: RefNew deprecated - RefGet is sufficient".to_string()
|
|
|
|
|
))
|
2025-08-13 09:45:22 +00:00
|
|
|
},
|
|
|
|
|
|
2025-08-17 12:27:12 +09:00
|
|
|
// Phase 3: RefGet/RefSet removed - now handled by BoxFieldLoad/BoxFieldStore
|
2025-08-13 09:45:22 +00:00
|
|
|
|
|
|
|
|
MirInstruction::WeakNew { dst, box_val } => {
|
|
|
|
|
// For now, a weak reference is just a copy of the value
|
|
|
|
|
// In a real implementation, this would create a proper weak reference
|
|
|
|
|
let box_value = self.get_value(*box_val)?;
|
|
|
|
|
self.values.insert(*dst, box_value);
|
|
|
|
|
Ok(ControlFlow::Continue)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
MirInstruction::WeakLoad { dst, weak_ref } => {
|
|
|
|
|
// For now, loading from weak ref is the same as getting the value
|
|
|
|
|
// In a real implementation, this would check if the weak ref is still valid
|
|
|
|
|
let weak_value = self.get_value(*weak_ref)?;
|
|
|
|
|
self.values.insert(*dst, weak_value);
|
|
|
|
|
Ok(ControlFlow::Continue)
|
|
|
|
|
},
|
|
|
|
|
|
2025-08-17 14:06:47 +09:00
|
|
|
// Phase 5: Removed instructions - BarrierRead, BarrierWrite
|
|
|
|
|
#[allow(deprecated)]
|
|
|
|
|
MirInstruction::BarrierRead { .. } |
|
|
|
|
|
MirInstruction::BarrierWrite { .. } => {
|
|
|
|
|
Err(VMError::InvalidInstruction(
|
|
|
|
|
"Phase 5: Memory barriers deprecated - use AtomicFence".to_string()
|
|
|
|
|
))
|
2025-08-13 11:46:01 +00:00
|
|
|
},
|
|
|
|
|
|
2025-08-17 14:06:47 +09:00
|
|
|
// Phase 5: Removed instructions - FutureNew, FutureSet, Await
|
|
|
|
|
#[allow(deprecated)]
|
|
|
|
|
MirInstruction::FutureNew { .. } |
|
|
|
|
|
MirInstruction::FutureSet { .. } |
|
|
|
|
|
MirInstruction::Await { .. } => {
|
|
|
|
|
Err(VMError::InvalidInstruction(
|
|
|
|
|
"Phase 5: Future operations deprecated - use NewBox + BoxCall".to_string()
|
|
|
|
|
))
|
2025-08-13 11:46:01 +00:00
|
|
|
},
|
2025-08-14 08:56:39 +00:00
|
|
|
|
|
|
|
|
// Phase 9.7: External Function Calls
|
|
|
|
|
MirInstruction::ExternCall { dst, iface_name, method_name, args, effects: _ } => {
|
|
|
|
|
// For VM backend, we implement a stub that logs the call
|
|
|
|
|
// Real implementation would route to native host functions
|
|
|
|
|
let arg_values: Result<Vec<_>, _> = args.iter().map(|id| self.get_value(*id)).collect();
|
|
|
|
|
let arg_values = arg_values?;
|
|
|
|
|
|
|
|
|
|
println!("ExternCall: {}.{}({:?})", iface_name, method_name, arg_values);
|
|
|
|
|
|
|
|
|
|
// For console.log, print the message
|
|
|
|
|
if iface_name == "env.console" && method_name == "log" {
|
|
|
|
|
for arg in &arg_values {
|
|
|
|
|
if let VMValue::String(s) = arg {
|
|
|
|
|
println!("Console: {}", s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For canvas operations, just log them for now
|
|
|
|
|
if iface_name == "env.canvas" {
|
|
|
|
|
println!("Canvas operation: {}", method_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Store void result if destination is provided
|
|
|
|
|
if let Some(dst) = dst {
|
|
|
|
|
self.values.insert(*dst, VMValue::Void);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(ControlFlow::Continue)
|
|
|
|
|
},
|
2025-08-17 12:27:12 +09:00
|
|
|
|
|
|
|
|
// Phase 8.5: MIR 26-instruction reduction (NEW)
|
|
|
|
|
MirInstruction::BoxFieldLoad { dst, box_val, field } => {
|
|
|
|
|
// Load field from box (Everything is Box principle)
|
|
|
|
|
let box_value = self.get_value(*box_val)?;
|
|
|
|
|
|
|
|
|
|
// For now, simulate field access - in full implementation,
|
|
|
|
|
// this would access actual Box structure fields
|
|
|
|
|
let field_value = match field.as_str() {
|
|
|
|
|
"value" => box_value.clone(), // Default field
|
|
|
|
|
"type" => VMValue::String(format!("{}Field", box_val)),
|
|
|
|
|
_ => VMValue::String(format!("field_{}", field)),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
self.values.insert(*dst, field_value);
|
|
|
|
|
Ok(ControlFlow::Continue)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
MirInstruction::BoxFieldStore { box_val, field: _, value } => {
|
|
|
|
|
// Store field in box (Everything is Box principle)
|
|
|
|
|
let _box_value = self.get_value(*box_val)?;
|
|
|
|
|
let _store_value = self.get_value(*value)?;
|
|
|
|
|
|
|
|
|
|
// For now, this is a no-op - in full implementation,
|
|
|
|
|
// this would modify actual Box structure fields
|
|
|
|
|
// println!("Storing {} in {}.{}", store_value, box_val, field);
|
|
|
|
|
|
|
|
|
|
Ok(ControlFlow::Continue)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
MirInstruction::WeakCheck { dst, weak_ref } => {
|
|
|
|
|
// Check if weak reference is still alive
|
|
|
|
|
let _weak_value = self.get_value(*weak_ref)?;
|
|
|
|
|
|
|
|
|
|
// For now, always return true - in full implementation,
|
|
|
|
|
// this would check actual weak reference validity
|
|
|
|
|
self.values.insert(*dst, VMValue::Bool(true));
|
|
|
|
|
Ok(ControlFlow::Continue)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
MirInstruction::Send { data, target } => {
|
|
|
|
|
// Send data via Bus system
|
|
|
|
|
let _data_value = self.get_value(*data)?;
|
|
|
|
|
let _target_value = self.get_value(*target)?;
|
|
|
|
|
|
|
|
|
|
// For now, this is a no-op - in full implementation,
|
|
|
|
|
// this would use the Bus communication system
|
|
|
|
|
// println!("Sending {} to {}", data_value, target_value);
|
|
|
|
|
|
|
|
|
|
Ok(ControlFlow::Continue)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
MirInstruction::Recv { dst, source } => {
|
|
|
|
|
// Receive data from Bus system
|
|
|
|
|
let _source_value = self.get_value(*source)?;
|
|
|
|
|
|
|
|
|
|
// For now, return a placeholder - in full implementation,
|
|
|
|
|
// this would receive from actual Bus communication
|
|
|
|
|
self.values.insert(*dst, VMValue::String("received_data".to_string()));
|
|
|
|
|
Ok(ControlFlow::Continue)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
MirInstruction::TailCall { func, args, effects: _ } => {
|
|
|
|
|
// Tail call optimization - call function and return immediately
|
|
|
|
|
let _func_value = self.get_value(*func)?;
|
|
|
|
|
let _arg_values: Result<Vec<_>, _> = args.iter().map(|arg| self.get_value(*arg)).collect();
|
|
|
|
|
|
|
|
|
|
// For now, this is simplified - in full implementation,
|
|
|
|
|
// this would optimize the call stack
|
|
|
|
|
// println!("Tail calling function with {} args", args.len());
|
|
|
|
|
|
|
|
|
|
Ok(ControlFlow::Continue)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
MirInstruction::Adopt { parent, child } => {
|
|
|
|
|
// Adopt ownership (parent takes child)
|
|
|
|
|
let _parent_value = self.get_value(*parent)?;
|
|
|
|
|
let _child_value = self.get_value(*child)?;
|
|
|
|
|
|
|
|
|
|
// For now, this is a no-op - in full implementation,
|
|
|
|
|
// this would modify ownership relationships
|
|
|
|
|
// println!("Parent {} adopts child {}", parent, child);
|
|
|
|
|
|
|
|
|
|
Ok(ControlFlow::Continue)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
MirInstruction::Release { reference } => {
|
|
|
|
|
// Release strong ownership
|
|
|
|
|
let _ref_value = self.get_value(*reference)?;
|
|
|
|
|
|
|
|
|
|
// For now, this is a no-op - in full implementation,
|
|
|
|
|
// this would release strong ownership and potentially weak-ify
|
|
|
|
|
// println!("Releasing ownership of {}", reference);
|
|
|
|
|
|
|
|
|
|
Ok(ControlFlow::Continue)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
MirInstruction::MemCopy { dst, src, size } => {
|
|
|
|
|
// Memory copy optimization
|
|
|
|
|
let src_value = self.get_value(*src)?;
|
|
|
|
|
let _size_value = self.get_value(*size)?;
|
|
|
|
|
|
|
|
|
|
// For now, just copy the source value
|
|
|
|
|
self.values.insert(*dst, src_value);
|
|
|
|
|
Ok(ControlFlow::Continue)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
MirInstruction::AtomicFence { ordering: _ } => {
|
|
|
|
|
// Atomic memory fence
|
|
|
|
|
|
|
|
|
|
// For now, this is a no-op - in full implementation,
|
|
|
|
|
// this would ensure proper memory ordering for parallel execution
|
|
|
|
|
// println!("Memory fence with ordering: {:?}", ordering);
|
|
|
|
|
|
|
|
|
|
Ok(ControlFlow::Continue)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Phase 3: Removed instructions that are no longer generated by frontend
|
|
|
|
|
MirInstruction::UnaryOp { .. } |
|
|
|
|
|
MirInstruction::Print { .. } |
|
|
|
|
|
MirInstruction::Load { .. } |
|
|
|
|
|
MirInstruction::Store { .. } |
|
|
|
|
|
MirInstruction::RefGet { .. } |
|
|
|
|
|
MirInstruction::RefSet { .. } => {
|
|
|
|
|
Err(VMError::InvalidInstruction(
|
|
|
|
|
"Old instruction format no longer supported - use new intrinsic/BoxField format".to_string()
|
|
|
|
|
))
|
|
|
|
|
},
|
2025-08-13 05:59:10 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get a value from storage
|
|
|
|
|
fn get_value(&self, value_id: ValueId) -> Result<VMValue, VMError> {
|
|
|
|
|
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<VMValue, VMError> {
|
|
|
|
|
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<VMValue, VMError> {
|
|
|
|
|
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<bool, VMError> {
|
|
|
|
|
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))),
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-14 03:39:08 +00:00
|
|
|
|
|
|
|
|
/// Call a method on a Box - simplified version of interpreter method dispatch
|
|
|
|
|
fn call_box_method(&self, box_value: Box<dyn NyashBox>, method: &str, _args: Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, VMError> {
|
|
|
|
|
// For now, implement basic methods for common box types
|
|
|
|
|
// This is a simplified version - real implementation would need full method dispatch
|
|
|
|
|
|
|
|
|
|
// StringBox methods
|
|
|
|
|
if let Some(string_box) = box_value.as_any().downcast_ref::<StringBox>() {
|
|
|
|
|
match method {
|
|
|
|
|
"length" | "len" => {
|
|
|
|
|
return Ok(Box::new(IntegerBox::new(string_box.value.len() as i64)));
|
|
|
|
|
},
|
|
|
|
|
"toString" => {
|
|
|
|
|
return Ok(Box::new(StringBox::new(string_box.value.clone())));
|
|
|
|
|
},
|
2025-08-14 03:46:06 +00:00
|
|
|
"substring" => {
|
|
|
|
|
// substring(start, end) - simplified implementation
|
|
|
|
|
if _args.len() >= 2 {
|
|
|
|
|
if let (Some(start_box), Some(end_box)) = (_args.get(0), _args.get(1)) {
|
|
|
|
|
if let (Some(start_int), Some(end_int)) = (
|
|
|
|
|
start_box.as_any().downcast_ref::<IntegerBox>(),
|
|
|
|
|
end_box.as_any().downcast_ref::<IntegerBox>()
|
|
|
|
|
) {
|
|
|
|
|
let start = start_int.value.max(0) as usize;
|
|
|
|
|
let end = end_int.value.max(0) as usize;
|
|
|
|
|
let len = string_box.value.len();
|
|
|
|
|
|
|
|
|
|
if start <= len {
|
|
|
|
|
let end_idx = end.min(len);
|
|
|
|
|
if start <= end_idx {
|
|
|
|
|
let substr = &string_box.value[start..end_idx];
|
|
|
|
|
return Ok(Box::new(StringBox::new(substr)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Ok(Box::new(StringBox::new(""))); // Return empty string on error
|
|
|
|
|
},
|
|
|
|
|
"concat" => {
|
|
|
|
|
// concat(other) - concatenate with another string
|
|
|
|
|
if let Some(other_box) = _args.get(0) {
|
|
|
|
|
let other_str = other_box.to_string_box().value;
|
|
|
|
|
let result = string_box.value.clone() + &other_str;
|
|
|
|
|
return Ok(Box::new(StringBox::new(result)));
|
|
|
|
|
}
|
|
|
|
|
return Ok(Box::new(StringBox::new(string_box.value.clone())));
|
|
|
|
|
},
|
2025-08-14 03:39:08 +00:00
|
|
|
_ => return Ok(Box::new(VoidBox::new())), // Unsupported method
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IntegerBox methods
|
|
|
|
|
if let Some(integer_box) = box_value.as_any().downcast_ref::<IntegerBox>() {
|
|
|
|
|
match method {
|
|
|
|
|
"toString" => {
|
|
|
|
|
return Ok(Box::new(StringBox::new(integer_box.value.to_string())));
|
|
|
|
|
},
|
|
|
|
|
"abs" => {
|
|
|
|
|
return Ok(Box::new(IntegerBox::new(integer_box.value.abs())));
|
|
|
|
|
},
|
|
|
|
|
_ => return Ok(Box::new(VoidBox::new())), // Unsupported method
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BoolBox methods
|
|
|
|
|
if let Some(bool_box) = box_value.as_any().downcast_ref::<BoolBox>() {
|
|
|
|
|
match method {
|
|
|
|
|
"toString" => {
|
|
|
|
|
return Ok(Box::new(StringBox::new(bool_box.value.to_string())));
|
|
|
|
|
},
|
|
|
|
|
_ => return Ok(Box::new(VoidBox::new())), // Unsupported method
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-14 03:46:06 +00:00
|
|
|
// ArrayBox methods - needed for kilo editor
|
|
|
|
|
if let Some(array_box) = box_value.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
|
|
|
|
match method {
|
|
|
|
|
"length" | "len" => {
|
2025-08-14 23:59:11 +00:00
|
|
|
let items = array_box.items.read().unwrap();
|
2025-08-14 03:46:06 +00:00
|
|
|
return Ok(Box::new(IntegerBox::new(items.len() as i64)));
|
|
|
|
|
},
|
|
|
|
|
"get" => {
|
|
|
|
|
// get(index) - get element at index
|
|
|
|
|
if let Some(index_box) = _args.get(0) {
|
|
|
|
|
if let Some(index_int) = index_box.as_any().downcast_ref::<IntegerBox>() {
|
2025-08-14 23:59:11 +00:00
|
|
|
let items = array_box.items.read().unwrap();
|
2025-08-14 03:46:06 +00:00
|
|
|
let index = index_int.value as usize;
|
|
|
|
|
if index < items.len() {
|
|
|
|
|
return Ok(items[index].clone_box());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Ok(Box::new(VoidBox::new())); // Return void for out of bounds
|
|
|
|
|
},
|
|
|
|
|
"set" => {
|
|
|
|
|
// set(index, value) - simplified implementation
|
|
|
|
|
// Note: This is a read-only operation in the VM for now
|
|
|
|
|
// In a real implementation, we'd need mutable access
|
|
|
|
|
return Ok(Box::new(VoidBox::new()));
|
|
|
|
|
},
|
|
|
|
|
"push" => {
|
|
|
|
|
// push(value) - simplified implementation
|
|
|
|
|
// Note: This is a read-only operation in the VM for now
|
|
|
|
|
return Ok(Box::new(VoidBox::new()));
|
|
|
|
|
},
|
|
|
|
|
"insert" => {
|
|
|
|
|
// insert(index, value) - simplified implementation
|
|
|
|
|
// Note: This is a read-only operation in the VM for now
|
|
|
|
|
return Ok(Box::new(VoidBox::new()));
|
|
|
|
|
},
|
|
|
|
|
_ => return Ok(Box::new(VoidBox::new())), // Unsupported method
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-14 03:39:08 +00:00
|
|
|
// Default: return void for any unrecognized box type or method
|
|
|
|
|
Ok(Box::new(VoidBox::new()))
|
|
|
|
|
}
|
2025-08-17 12:27:12 +09:00
|
|
|
|
|
|
|
|
/// Execute intrinsic function call (Phase 2 addition)
|
|
|
|
|
fn execute_intrinsic(&mut self, intrinsic_name: &str, args: &[ValueId]) -> Result<VMValue, VMError> {
|
|
|
|
|
match intrinsic_name {
|
|
|
|
|
"@print" => {
|
|
|
|
|
// Print intrinsic - output the first argument
|
|
|
|
|
if let Some(arg_id) = args.first() {
|
|
|
|
|
let value = self.get_value(*arg_id)?;
|
|
|
|
|
match value {
|
|
|
|
|
VMValue::String(s) => println!("{}", s),
|
|
|
|
|
VMValue::Integer(i) => println!("{}", i),
|
|
|
|
|
VMValue::Float(f) => println!("{}", f),
|
|
|
|
|
VMValue::Bool(b) => println!("{}", b),
|
|
|
|
|
VMValue::Void => println!("void"),
|
|
|
|
|
VMValue::Future(_) => println!("Future"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(VMValue::Void) // Print returns void
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
"@unary_neg" => {
|
|
|
|
|
// Unary negation intrinsic
|
|
|
|
|
if let Some(arg_id) = args.first() {
|
|
|
|
|
let value = self.get_value(*arg_id)?;
|
|
|
|
|
match value {
|
|
|
|
|
VMValue::Integer(i) => Ok(VMValue::Integer(-i)),
|
|
|
|
|
VMValue::Float(f) => Ok(VMValue::Float(-f)),
|
|
|
|
|
_ => Err(VMError::TypeError(format!("Cannot negate {:?}", value))),
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
Err(VMError::TypeError("@unary_neg requires 1 argument".to_string()))
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
"@unary_not" => {
|
|
|
|
|
// Unary logical NOT intrinsic
|
|
|
|
|
if let Some(arg_id) = args.first() {
|
|
|
|
|
let value = self.get_value(*arg_id)?;
|
|
|
|
|
match value {
|
|
|
|
|
VMValue::Bool(b) => Ok(VMValue::Bool(!b)),
|
|
|
|
|
VMValue::Integer(i) => Ok(VMValue::Bool(i == 0)), // 0 is false, non-zero is true
|
|
|
|
|
_ => Err(VMError::TypeError(format!("Cannot apply NOT to {:?}", value))),
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
Err(VMError::TypeError("@unary_not requires 1 argument".to_string()))
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
"@unary_bitnot" => {
|
|
|
|
|
// Unary bitwise NOT intrinsic
|
|
|
|
|
if let Some(arg_id) = args.first() {
|
|
|
|
|
let value = self.get_value(*arg_id)?;
|
|
|
|
|
match value {
|
|
|
|
|
VMValue::Integer(i) => Ok(VMValue::Integer(!i)),
|
|
|
|
|
_ => Err(VMError::TypeError(format!("Cannot apply bitwise NOT to {:?}", value))),
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
Err(VMError::TypeError("@unary_bitnot requires 1 argument".to_string()))
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
"@throw" => {
|
|
|
|
|
// Throw intrinsic - for now just print the exception
|
|
|
|
|
if let Some(arg_id) = args.first() {
|
|
|
|
|
let value = self.get_value(*arg_id)?;
|
|
|
|
|
println!("Exception thrown: {:?}", value);
|
|
|
|
|
}
|
|
|
|
|
Err(VMError::InvalidInstruction("Exception thrown".to_string()))
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
"@set_exception_handler" => {
|
|
|
|
|
// Exception handler setup - for now just return success
|
|
|
|
|
Ok(VMValue::Void)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
_ => {
|
|
|
|
|
Err(VMError::InvalidInstruction(format!("Unknown intrinsic: {}", intrinsic_name)))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-13 05:59:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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);
|
|
|
|
|
}
|
|
|
|
|
}
|