2024-11-06 00:38:26 +00:00
|
|
|
//! Virtual machine and errors
|
2024-11-07 07:10:02 +00:00
|
|
|
use std::{
|
|
|
|
cmp::Ordering,
|
2024-11-09 18:49:02 +00:00
|
|
|
collections::HashMap,
|
2024-11-07 07:10:02 +00:00
|
|
|
fmt::{self, Display, Formatter},
|
2024-11-11 00:28:21 +00:00
|
|
|
mem::replace,
|
|
|
|
rc::Weak,
|
2024-11-07 07:10:02 +00:00
|
|
|
};
|
2024-10-12 07:06:44 +00:00
|
|
|
|
2024-09-12 04:39:31 +00:00
|
|
|
use crate::{
|
2024-11-11 00:28:21 +00:00
|
|
|
compile, value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionBorrowed,
|
2024-11-10 02:40:33 +00:00
|
|
|
Instruction, NativeFunction, NativeFunctionError, Operation, Span, Type, Value, ValueError,
|
2024-09-12 04:39:31 +00:00
|
|
|
};
|
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> {
|
2024-11-09 18:49:02 +00:00
|
|
|
let chunk = compile(source)?;
|
|
|
|
let mut vm = Vm::new(&chunk, None);
|
2024-09-12 04:39:31 +00:00
|
|
|
|
|
|
|
vm.run()
|
|
|
|
.map_err(|error| DustError::Runtime { error, source })
|
|
|
|
}
|
|
|
|
|
2024-11-07 07:10:02 +00:00
|
|
|
pub fn run_and_display_output(source: &str) {
|
|
|
|
match run(source) {
|
|
|
|
Ok(Some(value)) => println!("{}", value),
|
|
|
|
Ok(None) => {}
|
|
|
|
Err(error) => eprintln!("{}", error.report()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-06 00:38:26 +00:00
|
|
|
/// Dust virtual machine.
|
|
|
|
///
|
|
|
|
/// See the [module-level documentation](index.html) for more information.
|
2024-09-12 04:39:31 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
2024-11-11 00:28:21 +00:00
|
|
|
pub struct Vm<'chunk, 'parent, 'stack> {
|
2024-09-12 04:39:31 +00:00
|
|
|
ip: usize,
|
2024-11-09 18:49:02 +00:00
|
|
|
chunk: &'chunk Chunk,
|
2024-11-11 00:28:21 +00:00
|
|
|
stack: Vec<Register<'stack>>,
|
2024-11-09 18:49:02 +00:00
|
|
|
local_definitions: HashMap<u8, u8>,
|
2024-10-30 03:11:55 +00:00
|
|
|
last_assigned_register: Option<u8>,
|
2024-11-11 00:28:21 +00:00
|
|
|
parent: Option<&'parent Vm<'chunk, 'stack, 'parent>>,
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
2024-09-12 04:39:31 +00:00
|
|
|
const STACK_LIMIT: usize = u16::MAX as usize;
|
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
pub fn new(chunk: &'chunk Chunk, parent: Option<&'parent Vm<'chunk, 'parent, 'stack>>) -> Self {
|
2024-09-12 04:39:31 +00:00
|
|
|
Self {
|
|
|
|
ip: 0,
|
2024-10-12 07:06:44 +00:00
|
|
|
chunk,
|
|
|
|
stack: Vec::new(),
|
2024-11-09 18:49:02 +00:00
|
|
|
local_definitions: HashMap::new(),
|
2024-10-30 03:11:55 +00:00
|
|
|
last_assigned_register: None,
|
2024-11-07 07:10:02 +00:00
|
|
|
parent,
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
pub fn run(&'stack mut self) -> Result<Option<ConcreteValue>, VmError> {
|
2024-09-12 04:39:31 +00:00
|
|
|
while let Ok((instruction, position)) = self.read(Span(0, 0)).copied() {
|
2024-10-12 07:06:44 +00:00
|
|
|
log::info!(
|
|
|
|
"{} | {} | {} | {}",
|
|
|
|
self.ip - 1,
|
|
|
|
position,
|
|
|
|
instruction.operation(),
|
2024-11-07 08:05:32 +00:00
|
|
|
instruction.disassembly_info(self.chunk)
|
2024-10-12 07:06:44 +00:00
|
|
|
);
|
2024-09-12 04:39:31 +00:00
|
|
|
|
2024-09-15 05:24:04 +00:00
|
|
|
match instruction.operation() {
|
2024-11-01 06:51:18 +00:00
|
|
|
Operation::Move => {
|
|
|
|
let to_register = instruction.a();
|
|
|
|
let from_register = instruction.b();
|
2024-11-06 20:08:51 +00:00
|
|
|
let from_register_has_value = self
|
|
|
|
.stack
|
|
|
|
.get(from_register as usize)
|
|
|
|
.is_some_and(|register| !matches!(register, Register::Empty));
|
2024-11-01 06:51:18 +00:00
|
|
|
|
2024-11-06 20:08:51 +00:00
|
|
|
if from_register_has_value {
|
2024-11-07 07:10:02 +00:00
|
|
|
self.set_register(
|
|
|
|
to_register,
|
|
|
|
Register::StackPointer(from_register),
|
|
|
|
position,
|
|
|
|
)?;
|
2024-11-06 20:08:51 +00:00
|
|
|
}
|
2024-11-01 06:51:18 +00:00
|
|
|
}
|
2024-09-18 03:06:58 +00:00
|
|
|
Operation::Close => {
|
2024-10-12 07:06:44 +00:00
|
|
|
let from_register = instruction.b();
|
|
|
|
let to_register = instruction.c();
|
|
|
|
|
|
|
|
if self.stack.len() < to_register as usize {
|
|
|
|
return Err(VmError::StackUnderflow { position });
|
|
|
|
}
|
2024-09-18 03:06:58 +00:00
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
for register_index in from_register..to_register {
|
|
|
|
self.stack[register_index as usize] = Register::Empty;
|
2024-09-18 03:06:58 +00:00
|
|
|
}
|
|
|
|
}
|
2024-09-18 17:42:32 +00:00
|
|
|
Operation::LoadBoolean => {
|
2024-09-24 20:49:17 +00:00
|
|
|
let to_register = instruction.a();
|
|
|
|
let boolean = instruction.b_as_boolean();
|
2024-10-30 18:48:30 +00:00
|
|
|
let jump = instruction.c_as_boolean();
|
2024-11-11 00:28:21 +00:00
|
|
|
let boolean = ConcreteValue::boolean(boolean);
|
2024-09-18 17:42:32 +00:00
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
self.set_register(to_register, Register::Value(boolean), position)?;
|
2024-09-18 17:42:32 +00:00
|
|
|
|
2024-10-30 18:48:30 +00:00
|
|
|
if jump {
|
2024-11-01 13:55:15 +00:00
|
|
|
self.ip += 1;
|
2024-09-18 17:42:32 +00:00
|
|
|
}
|
|
|
|
}
|
2024-09-12 04:39:31 +00:00
|
|
|
Operation::LoadConstant => {
|
2024-09-24 20:49:17 +00:00
|
|
|
let to_register = instruction.a();
|
|
|
|
let from_constant = instruction.b();
|
2024-09-25 13:55:10 +00:00
|
|
|
let jump = instruction.c_as_boolean();
|
2024-09-12 04:39:31 +00:00
|
|
|
|
2024-11-07 07:10:02 +00:00
|
|
|
self.set_register(
|
|
|
|
to_register,
|
|
|
|
Register::ConstantPointer(from_constant),
|
|
|
|
position,
|
|
|
|
)?;
|
2024-09-25 13:55:10 +00:00
|
|
|
|
|
|
|
if jump {
|
2024-11-01 13:55:15 +00:00
|
|
|
self.ip += 1
|
2024-09-25 13:55:10 +00:00
|
|
|
}
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
2024-09-17 23:35:33 +00:00
|
|
|
Operation::LoadList => {
|
2024-09-24 20:49:17 +00:00
|
|
|
let to_register = instruction.a();
|
2024-11-06 08:36:46 +00:00
|
|
|
let start_register = instruction.b();
|
2024-11-11 00:28:21 +00:00
|
|
|
let mut list = Vec::new();
|
|
|
|
|
|
|
|
for register_index in start_register..to_register {
|
|
|
|
let value = self.open_register(register_index, position)?;
|
|
|
|
|
|
|
|
list.push(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.set_register(to_register, Register::List(list), position)?;
|
2024-09-17 23:35:33 +00:00
|
|
|
}
|
2024-10-20 06:30:22 +00:00
|
|
|
Operation::LoadSelf => {
|
|
|
|
let to_register = instruction.a();
|
2024-11-11 00:28:21 +00:00
|
|
|
let function = Value::FunctionBorrowed(FunctionBorrowed::new(self.chunk));
|
2024-10-20 06:30:22 +00:00
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
// self.set_register(to_register, Register::Value(function), position)?;
|
|
|
|
|
|
|
|
todo!()
|
2024-10-20 06:30:22 +00:00
|
|
|
}
|
2024-09-18 15:27:41 +00:00
|
|
|
Operation::DefineLocal => {
|
2024-09-24 20:49:17 +00:00
|
|
|
let from_register = instruction.a();
|
|
|
|
let to_local = instruction.b();
|
2024-09-12 09:08:55 +00:00
|
|
|
|
2024-11-09 18:49:02 +00:00
|
|
|
self.define_local(to_local, from_register)?;
|
2024-09-12 09:08:55 +00:00
|
|
|
}
|
2024-09-12 17:03:24 +00:00
|
|
|
Operation::GetLocal => {
|
2024-10-12 07:06:44 +00:00
|
|
|
let to_register = instruction.a();
|
2024-09-24 20:49:17 +00:00
|
|
|
let local_index = instruction.b();
|
2024-11-09 18:49:02 +00:00
|
|
|
let local_register = self.local_definitions.get(&local_index).copied().ok_or(
|
|
|
|
VmError::UndefinedLocal {
|
|
|
|
local_index,
|
|
|
|
position,
|
|
|
|
},
|
|
|
|
)?;
|
2024-09-12 18:16:26 +00:00
|
|
|
|
2024-11-07 07:10:02 +00:00
|
|
|
self.set_register(
|
|
|
|
to_register,
|
2024-11-09 18:49:02 +00:00
|
|
|
Register::StackPointer(local_register),
|
2024-11-07 07:10:02 +00:00
|
|
|
position,
|
|
|
|
)?;
|
2024-09-12 09:08:55 +00:00
|
|
|
}
|
2024-09-15 01:05:03 +00:00
|
|
|
Operation::SetLocal => {
|
2024-11-09 18:49:02 +00:00
|
|
|
let from_register = instruction.a();
|
|
|
|
let to_local = instruction.b();
|
|
|
|
let local_register = self.local_definitions.get(&to_local).copied().ok_or(
|
|
|
|
VmError::UndefinedLocal {
|
|
|
|
local_index: to_local,
|
|
|
|
position,
|
|
|
|
},
|
|
|
|
)?;
|
2024-09-25 02:58:14 +00:00
|
|
|
|
2024-11-09 18:49:02 +00:00
|
|
|
self.set_register(
|
|
|
|
local_register,
|
|
|
|
Register::StackPointer(from_register),
|
|
|
|
position,
|
|
|
|
)?;
|
2024-09-15 01:05:03 +00:00
|
|
|
}
|
2024-09-12 04:39:31 +00:00
|
|
|
Operation::Add => {
|
2024-11-07 07:10:02 +00:00
|
|
|
let to_register = instruction.a();
|
2024-11-11 00:28:21 +00:00
|
|
|
let (left, right) = self.get_arguments(instruction, position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
let sum = left
|
2024-11-11 00:28:21 +00:00
|
|
|
.add(&right)
|
2024-09-12 04:39:31 +00:00
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
|
|
|
|
2024-11-07 07:10:02 +00:00
|
|
|
self.set_register(to_register, Register::Value(sum), position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
|
|
|
Operation::Subtract => {
|
2024-11-07 07:10:02 +00:00
|
|
|
let to_register = instruction.a();
|
2024-11-11 00:28:21 +00:00
|
|
|
let (left, right) = self.get_arguments(instruction, position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
let difference = left
|
2024-11-11 00:28:21 +00:00
|
|
|
.subtract(&right)
|
2024-09-12 04:39:31 +00:00
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
|
|
|
|
2024-11-07 07:10:02 +00:00
|
|
|
self.set_register(to_register, Register::Value(difference), position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
|
|
|
Operation::Multiply => {
|
2024-11-07 07:10:02 +00:00
|
|
|
let to_register = instruction.a();
|
2024-11-11 00:28:21 +00:00
|
|
|
let (left, right) = self.get_arguments(instruction, position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
let product = left
|
2024-11-11 00:28:21 +00:00
|
|
|
.multiply(&right)
|
2024-09-12 04:39:31 +00:00
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
|
|
|
|
2024-11-07 07:10:02 +00:00
|
|
|
self.set_register(to_register, Register::Value(product), position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
|
|
|
Operation::Divide => {
|
2024-11-07 07:10:02 +00:00
|
|
|
let to_register = instruction.a();
|
2024-11-11 00:28:21 +00:00
|
|
|
let (left, right) = self.get_arguments(instruction, position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
let quotient = left
|
2024-11-11 00:28:21 +00:00
|
|
|
.divide(&right)
|
2024-09-12 04:39:31 +00:00
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
|
|
|
|
2024-11-07 07:10:02 +00:00
|
|
|
self.set_register(to_register, Register::Value(quotient), position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
2024-09-17 21:51:39 +00:00
|
|
|
Operation::Modulo => {
|
2024-11-07 07:10:02 +00:00
|
|
|
let to_register = instruction.a();
|
2024-11-11 00:28:21 +00:00
|
|
|
let (left, right) = self.get_arguments(instruction, position)?;
|
2024-09-17 21:51:39 +00:00
|
|
|
let remainder = left
|
2024-11-11 00:28:21 +00:00
|
|
|
.modulo(&right)
|
2024-09-17 21:51:39 +00:00
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
|
|
|
|
2024-11-07 07:10:02 +00:00
|
|
|
self.set_register(to_register, Register::Value(remainder), position)?;
|
2024-09-17 21:51:39 +00:00
|
|
|
}
|
2024-09-19 15:41:18 +00:00
|
|
|
Operation::Test => {
|
2024-09-24 20:49:17 +00:00
|
|
|
let register = instruction.a();
|
|
|
|
let test_value = instruction.c_as_boolean();
|
2024-11-07 08:05:32 +00:00
|
|
|
let value = self.open_register(register, position)?;
|
2024-11-11 00:28:21 +00:00
|
|
|
let boolean = if let Some(boolean) = value.as_boolean() {
|
|
|
|
boolean
|
2024-10-12 08:50:30 +00:00
|
|
|
} else {
|
|
|
|
return Err(VmError::ExpectedBoolean {
|
2024-11-11 00:28:21 +00:00
|
|
|
found: value.into_concrete(),
|
2024-10-12 08:50:30 +00:00
|
|
|
position,
|
|
|
|
});
|
|
|
|
};
|
2024-09-19 15:41:18 +00:00
|
|
|
|
2024-09-24 20:49:17 +00:00
|
|
|
if boolean != test_value {
|
2024-09-19 15:41:18 +00:00
|
|
|
self.ip += 1;
|
|
|
|
}
|
2024-09-17 21:51:39 +00:00
|
|
|
}
|
2024-10-19 07:06:14 +00:00
|
|
|
Operation::TestSet => todo!(),
|
2024-09-18 20:43:34 +00:00
|
|
|
Operation::Equal => {
|
2024-10-12 07:06:44 +00:00
|
|
|
debug_assert_eq!(
|
2024-11-04 20:38:58 +00:00
|
|
|
self.get_instruction(self.ip, position)?.0.operation(),
|
2024-10-12 07:06:44 +00:00
|
|
|
Operation::Jump
|
|
|
|
);
|
2024-09-24 14:16:19 +00:00
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
let compare_to = instruction.a_as_boolean();
|
|
|
|
let (left, right) = self.get_arguments(instruction, position)?;
|
2024-10-12 08:50:30 +00:00
|
|
|
let equal_result = left
|
2024-11-11 00:28:21 +00:00
|
|
|
.equal(&right)
|
2024-10-12 08:50:30 +00:00
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
2024-11-11 00:28:21 +00:00
|
|
|
let is_equal = if let ConcreteValue::Boolean(boolean) = equal_result {
|
|
|
|
boolean
|
|
|
|
} else {
|
|
|
|
return Err(VmError::ExpectedBoolean {
|
|
|
|
found: equal_result.clone(),
|
|
|
|
position,
|
|
|
|
});
|
|
|
|
};
|
2024-09-18 20:43:34 +00:00
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
if is_equal == compare_to {
|
2024-09-24 14:16:19 +00:00
|
|
|
self.ip += 1;
|
|
|
|
} else {
|
2024-11-11 00:28:21 +00:00
|
|
|
let jump = self.get_instruction(self.ip, position)?.0;
|
2024-09-24 14:16:19 +00:00
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
self.jump(jump);
|
2024-09-18 20:43:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Operation::Less => {
|
2024-10-12 07:06:44 +00:00
|
|
|
debug_assert_eq!(
|
2024-11-04 20:38:58 +00:00
|
|
|
self.get_instruction(self.ip, position)?.0.operation(),
|
2024-10-12 07:06:44 +00:00
|
|
|
Operation::Jump
|
|
|
|
);
|
2024-09-24 14:16:19 +00:00
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
let compare_to = instruction.a_as_boolean();
|
|
|
|
let (left, right) = self.get_arguments(instruction, position)?;
|
2024-10-12 08:50:30 +00:00
|
|
|
let less_result = left
|
2024-11-11 00:28:21 +00:00
|
|
|
.less_than(&right)
|
2024-10-12 08:50:30 +00:00
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
2024-11-11 00:28:21 +00:00
|
|
|
let is_less_than = if let ConcreteValue::Boolean(boolean) = less_result {
|
|
|
|
boolean
|
|
|
|
} else {
|
|
|
|
return Err(VmError::ExpectedBoolean {
|
|
|
|
found: less_result.clone(),
|
|
|
|
position,
|
|
|
|
});
|
|
|
|
};
|
2024-09-18 20:43:34 +00:00
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
if is_less_than == compare_to {
|
2024-09-24 14:16:19 +00:00
|
|
|
self.ip += 1;
|
|
|
|
} else {
|
2024-11-04 20:38:58 +00:00
|
|
|
let jump = self.get_instruction(self.ip, position)?.0;
|
2024-09-24 14:16:19 +00:00
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
self.jump(jump);
|
2024-09-18 20:43:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Operation::LessEqual => {
|
2024-10-12 07:06:44 +00:00
|
|
|
debug_assert_eq!(
|
2024-11-04 20:38:58 +00:00
|
|
|
self.get_instruction(self.ip, position)?.0.operation(),
|
2024-10-12 07:06:44 +00:00
|
|
|
Operation::Jump
|
|
|
|
);
|
2024-09-24 15:40:12 +00:00
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
let compare_to = instruction.a_as_boolean();
|
|
|
|
let (left, right) = self.get_arguments(instruction, position)?;
|
2024-10-12 08:50:30 +00:00
|
|
|
let less_or_equal_result = left
|
2024-11-11 00:28:21 +00:00
|
|
|
.less_than_or_equal(&right)
|
2024-10-12 08:50:30 +00:00
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
2024-11-11 00:28:21 +00:00
|
|
|
let is_less_than_or_equal =
|
|
|
|
if let ConcreteValue::Boolean(boolean) = less_or_equal_result {
|
|
|
|
boolean
|
|
|
|
} else {
|
|
|
|
return Err(VmError::ExpectedBoolean {
|
|
|
|
found: less_or_equal_result.clone(),
|
|
|
|
position,
|
|
|
|
});
|
|
|
|
};
|
2024-09-18 20:43:34 +00:00
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
if is_less_than_or_equal == compare_to {
|
2024-09-24 14:16:19 +00:00
|
|
|
self.ip += 1;
|
|
|
|
} else {
|
2024-11-04 20:38:58 +00:00
|
|
|
let jump = self.get_instruction(self.ip, position)?.0;
|
2024-09-24 14:16:19 +00:00
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
self.jump(jump);
|
2024-09-18 20:43:34 +00:00
|
|
|
}
|
|
|
|
}
|
2024-09-12 04:39:31 +00:00
|
|
|
Operation::Negate => {
|
2024-11-11 00:28:21 +00:00
|
|
|
let value =
|
|
|
|
self.get_argument(instruction.b(), instruction.b_is_constant(), position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
let negated = value
|
|
|
|
.negate()
|
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
|
|
|
|
2024-11-07 07:10:02 +00:00
|
|
|
self.set_register(instruction.a(), Register::Value(negated), position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
2024-09-17 21:51:39 +00:00
|
|
|
Operation::Not => {
|
2024-11-11 00:28:21 +00:00
|
|
|
let value =
|
|
|
|
self.get_argument(instruction.b(), instruction.b_is_constant(), position)?;
|
2024-09-18 11:58:31 +00:00
|
|
|
let not = value
|
2024-09-17 21:51:39 +00:00
|
|
|
.not()
|
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
|
|
|
|
2024-11-07 07:10:02 +00:00
|
|
|
self.set_register(instruction.a(), Register::Value(not), position)?;
|
2024-09-17 21:51:39 +00:00
|
|
|
}
|
2024-11-11 00:28:21 +00:00
|
|
|
Operation::Jump => self.jump(instruction),
|
2024-10-12 14:55:34 +00:00
|
|
|
Operation::Call => {
|
2024-10-13 11:14:12 +00:00
|
|
|
let to_register = instruction.a();
|
2024-10-19 21:24:22 +00:00
|
|
|
let function_register = instruction.b();
|
|
|
|
let argument_count = instruction.c();
|
2024-11-09 18:49:02 +00:00
|
|
|
let value = self.open_register(function_register, position)?;
|
2024-11-11 00:28:21 +00:00
|
|
|
let function = if let Some(function) = value.as_function() {
|
2024-11-09 18:49:02 +00:00
|
|
|
function
|
|
|
|
} else {
|
|
|
|
return Err(VmError::ExpectedFunction {
|
2024-11-11 00:28:21 +00:00
|
|
|
found: value.into_concrete(),
|
2024-11-09 18:49:02 +00:00
|
|
|
position,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
let mut function_vm = Vm::new(function.chunk(), Some(self));
|
2024-11-11 00:28:21 +00:00
|
|
|
|
2024-10-19 21:24:22 +00:00
|
|
|
let first_argument_index = function_register + 1;
|
2024-10-12 14:55:34 +00:00
|
|
|
|
2024-10-19 21:24:22 +00:00
|
|
|
for argument_index in
|
|
|
|
first_argument_index..first_argument_index + argument_count
|
|
|
|
{
|
2024-10-13 11:14:12 +00:00
|
|
|
let top_of_stack = function_vm.stack.len() as u8;
|
2024-10-12 14:55:34 +00:00
|
|
|
|
2024-11-07 07:10:02 +00:00
|
|
|
function_vm.set_register(
|
|
|
|
top_of_stack,
|
|
|
|
Register::ParentStackPointer(argument_index),
|
|
|
|
position,
|
|
|
|
)?
|
2024-10-12 14:55:34 +00:00
|
|
|
}
|
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
let return_value = function_vm.run()?;
|
2024-10-12 14:55:34 +00:00
|
|
|
|
|
|
|
if let Some(value) = return_value {
|
2024-11-07 07:10:02 +00:00
|
|
|
self.set_register(to_register, Register::Value(value), position)?;
|
2024-10-12 14:55:34 +00:00
|
|
|
}
|
|
|
|
}
|
2024-10-30 07:08:25 +00:00
|
|
|
Operation::CallNative => {
|
|
|
|
let native_function = NativeFunction::from(instruction.b());
|
2024-11-11 00:28:21 +00:00
|
|
|
let return_value = native_function.call(self, instruction, position)?;
|
2024-10-30 07:08:25 +00:00
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
if let Some(concrete_value) = return_value {
|
2024-10-30 12:02:22 +00:00
|
|
|
let to_register = instruction.a();
|
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
self.set_register(to_register, Register::Value(concrete_value), position)?;
|
2024-10-30 07:08:25 +00:00
|
|
|
}
|
|
|
|
}
|
2024-09-12 04:39:31 +00:00
|
|
|
Operation::Return => {
|
2024-10-12 07:06:44 +00:00
|
|
|
let should_return_value = instruction.b_as_boolean();
|
|
|
|
|
2024-10-30 03:11:55 +00:00
|
|
|
if !should_return_value {
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
return if let Some(register_index) = self.last_assigned_register {
|
|
|
|
self.open_register(register_index, position)
|
|
|
|
.map(|value| Some(value.into_concrete()))
|
2024-10-05 02:33:47 +00:00
|
|
|
} else {
|
2024-11-11 00:28:21 +00:00
|
|
|
Err(VmError::StackUnderflow { position })
|
2024-11-07 07:10:02 +00:00
|
|
|
};
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-05 02:21:17 +00:00
|
|
|
Ok(None)
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
pub(crate) fn open_register(
|
|
|
|
&'stack self,
|
|
|
|
register_index: u8,
|
|
|
|
position: Span,
|
|
|
|
) -> Result<&'stack ConcreteValue, VmError> {
|
|
|
|
let register_index = register_index as usize;
|
|
|
|
let register =
|
|
|
|
self.stack
|
|
|
|
.get(register_index)
|
|
|
|
.ok_or_else(|| VmError::RegisterIndexOutOfBounds {
|
|
|
|
index: register_index,
|
|
|
|
position,
|
|
|
|
})?;
|
|
|
|
|
|
|
|
log::trace!("Open R{register_index} to {register}");
|
|
|
|
|
|
|
|
match register {
|
|
|
|
Register::Value(value) => Ok(value),
|
|
|
|
Register::List(list) => Ok(ConcreteValue::List(
|
|
|
|
list.into_iter()
|
|
|
|
.map(|concrete_value| (*concrete_value).clone())
|
|
|
|
.collect(),
|
|
|
|
)),
|
|
|
|
Register::StackPointer(register_index) => self.open_register(*register_index, position),
|
|
|
|
Register::ConstantPointer(constant_index) => {
|
|
|
|
self.get_constant(*constant_index, position)
|
|
|
|
}
|
|
|
|
Register::ParentStackPointer(register_index) => {
|
|
|
|
let parent = self.parent.ok_or(VmError::ExpectedParent { position })?;
|
|
|
|
|
|
|
|
parent.open_register(*register_index, position)
|
|
|
|
}
|
|
|
|
Register::ParentConstantPointer(constant_index) => {
|
|
|
|
let parent = self
|
|
|
|
.parent
|
|
|
|
.as_ref()
|
|
|
|
.ok_or(VmError::ExpectedParent { position })?;
|
|
|
|
let constant = parent.get_constant(*constant_index, position)?;
|
|
|
|
|
|
|
|
Ok(constant)
|
|
|
|
}
|
|
|
|
Register::Empty => Err(VmError::EmptyRegister {
|
|
|
|
index: register_index,
|
|
|
|
position,
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_concrete_from_register(
|
|
|
|
&'stack self,
|
|
|
|
register_index: u8,
|
|
|
|
position: Span,
|
|
|
|
) -> Result<ConcreteValue, VmError> {
|
|
|
|
let register_index = register_index as usize;
|
|
|
|
let register =
|
|
|
|
self.stack
|
|
|
|
.get(register_index)
|
|
|
|
.ok_or_else(|| VmError::RegisterIndexOutOfBounds {
|
|
|
|
index: register_index,
|
|
|
|
position,
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let value = match register {
|
|
|
|
Register::Value(concrete_value) => concrete_value.clone(),
|
|
|
|
Register::List(list) => {
|
|
|
|
let items = list.into_iter().map(|value| (*value).clone()).collect();
|
|
|
|
|
|
|
|
ConcreteValue::List(items)
|
|
|
|
}
|
|
|
|
Register::StackPointer(register_index) => {
|
|
|
|
self.get_concrete_from_register(*register_index, position)?
|
|
|
|
}
|
|
|
|
Register::ConstantPointer(constant_pointer) => {
|
|
|
|
self.get_constant(*constant_pointer, position)?.clone()
|
|
|
|
}
|
|
|
|
Register::ParentStackPointer(register_index) => {
|
|
|
|
let parent = self.parent.ok_or(VmError::ExpectedParent { position })?;
|
|
|
|
|
|
|
|
parent.get_concrete_from_register(*register_index, position)?
|
|
|
|
}
|
|
|
|
Register::ParentConstantPointer(constant_index) => {
|
|
|
|
let parent = self
|
|
|
|
.parent
|
|
|
|
.as_ref()
|
|
|
|
.ok_or(VmError::ExpectedParent { position })?;
|
|
|
|
|
|
|
|
parent.get_constant(*constant_index, position)?.clone()
|
|
|
|
}
|
|
|
|
Register::Empty => {
|
|
|
|
return Err(VmError::EmptyRegister {
|
|
|
|
index: register_index,
|
|
|
|
position,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(value)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// DRY helper for handling JUMP instructions
|
|
|
|
fn jump(&mut self, jump: Instruction) {
|
|
|
|
let jump_distance = jump.b();
|
|
|
|
let is_positive = jump.c_as_boolean();
|
|
|
|
let new_ip = if is_positive {
|
|
|
|
self.ip + jump_distance as usize
|
|
|
|
} else {
|
|
|
|
self.ip - jump_distance as usize - 1
|
|
|
|
};
|
|
|
|
self.ip = new_ip;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// DRY helper to get a constant or register values
|
|
|
|
fn get_argument(
|
|
|
|
&'stack self,
|
|
|
|
index: u8,
|
|
|
|
is_constant: bool,
|
|
|
|
position: Span,
|
|
|
|
) -> Result<Value<'stack>, VmError> {
|
|
|
|
let argument = if is_constant {
|
|
|
|
self.get_constant(index, position)?.as_reference_value()
|
|
|
|
} else {
|
|
|
|
self.open_register(index, position)?
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(argument)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// DRY helper to get two arguments for binary operations
|
|
|
|
fn get_arguments(
|
|
|
|
&'stack self,
|
|
|
|
instruction: Instruction,
|
|
|
|
position: Span,
|
|
|
|
) -> Result<(Value<'stack>, Value<'stack>), VmError> {
|
|
|
|
let left = self.get_argument(instruction.b(), instruction.b_is_constant(), position)?;
|
|
|
|
let right = self.get_argument(instruction.c(), instruction.c_is_constant(), position)?;
|
|
|
|
|
|
|
|
Ok((left, right))
|
|
|
|
}
|
|
|
|
|
2024-11-04 20:38:58 +00:00
|
|
|
fn set_register(
|
|
|
|
&mut self,
|
|
|
|
to_register: u8,
|
2024-11-11 00:28:21 +00:00
|
|
|
register: Register<'stack>,
|
2024-11-04 20:38:58 +00:00
|
|
|
position: Span,
|
|
|
|
) -> Result<(), VmError> {
|
2024-10-30 03:11:55 +00:00
|
|
|
self.last_assigned_register = Some(to_register);
|
2024-11-06 20:08:51 +00:00
|
|
|
|
|
|
|
let length = self.stack.len();
|
2024-10-13 08:21:07 +00:00
|
|
|
let to_register = to_register as usize;
|
2024-09-15 01:05:03 +00:00
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
if length == Self::STACK_LIMIT {
|
|
|
|
return Err(VmError::StackOverflow { position });
|
|
|
|
}
|
2024-09-12 04:39:31 +00:00
|
|
|
|
2024-10-13 08:21:07 +00:00
|
|
|
match to_register.cmp(&length) {
|
|
|
|
Ordering::Less => {
|
2024-11-07 07:10:02 +00:00
|
|
|
log::trace!("Change R{to_register} to {register}");
|
2024-09-12 13:11:49 +00:00
|
|
|
|
2024-11-07 07:10:02 +00:00
|
|
|
self.stack[to_register] = register;
|
2024-10-12 07:06:44 +00:00
|
|
|
|
2024-10-13 08:21:07 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
Ordering::Equal => {
|
2024-11-07 07:10:02 +00:00
|
|
|
log::trace!("Set R{to_register} to {register}");
|
2024-09-12 04:39:31 +00:00
|
|
|
|
2024-11-07 07:10:02 +00:00
|
|
|
self.stack.push(register);
|
2024-09-19 15:41:18 +00:00
|
|
|
|
2024-10-13 08:21:07 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2024-10-21 02:18:58 +00:00
|
|
|
Ordering::Greater => {
|
|
|
|
let difference = to_register - length;
|
|
|
|
|
|
|
|
for index in 0..difference {
|
2024-11-07 08:05:32 +00:00
|
|
|
log::trace!("Set R{index} to {register}");
|
2024-10-21 02:18:58 +00:00
|
|
|
|
|
|
|
self.stack.push(Register::Empty);
|
|
|
|
}
|
|
|
|
|
2024-11-07 07:10:02 +00:00
|
|
|
log::trace!("Set R{to_register} to {register}");
|
2024-10-21 02:18:58 +00:00
|
|
|
|
2024-11-07 07:10:02 +00:00
|
|
|
self.stack.push(register);
|
2024-10-21 02:18:58 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2024-10-13 08:21:07 +00:00
|
|
|
}
|
|
|
|
}
|
2024-09-19 15:41:18 +00:00
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
fn get_constant(&self, index: u8, position: Span) -> Result<&'chunk ConcreteValue, VmError> {
|
2024-11-06 18:50:49 +00:00
|
|
|
self.chunk
|
|
|
|
.get_constant(index)
|
|
|
|
.map_err(|error| VmError::Chunk { error, position })
|
|
|
|
}
|
|
|
|
|
2024-09-12 04:39:31 +00:00
|
|
|
fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> {
|
2024-11-11 00:28:21 +00:00
|
|
|
let ip = self.ip;
|
2024-11-04 20:38:58 +00:00
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
self.ip += 1;
|
2024-11-04 20:38:58 +00:00
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
self.get_instruction(ip, position)
|
2024-11-04 20:38:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_instruction(
|
|
|
|
&self,
|
|
|
|
index: usize,
|
|
|
|
position: Span,
|
|
|
|
) -> Result<&(Instruction, Span), VmError> {
|
|
|
|
self.chunk
|
2024-11-06 18:50:49 +00:00
|
|
|
.get_instruction(index)
|
|
|
|
.map_err(|error| VmError::Chunk { error, position })
|
2024-11-04 20:38:58 +00:00
|
|
|
}
|
2024-11-11 00:28:21 +00:00
|
|
|
|
|
|
|
fn define_local(&mut self, local_index: u8, register_index: u8) -> Result<(), VmError> {
|
|
|
|
log::debug!("Define local L{}", local_index);
|
|
|
|
|
|
|
|
self.local_definitions.insert(local_index, register_index);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
2024-11-11 00:28:21 +00:00
|
|
|
enum Register<'stack> {
|
2024-10-12 07:06:44 +00:00
|
|
|
Empty,
|
2024-11-11 00:28:21 +00:00
|
|
|
Value(ConcreteValue),
|
|
|
|
List(Vec<&'stack ConcreteValue>),
|
2024-11-07 07:10:02 +00:00
|
|
|
StackPointer(u8),
|
|
|
|
ConstantPointer(u8),
|
|
|
|
ParentStackPointer(u8),
|
|
|
|
ParentConstantPointer(u8),
|
|
|
|
}
|
|
|
|
|
2024-11-11 00:28:21 +00:00
|
|
|
impl<'stack> Display for Register<'stack> {
|
2024-11-07 07:10:02 +00:00
|
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
Self::Empty => write!(f, "empty"),
|
|
|
|
Self::Value(value) => write!(f, "{}", value),
|
2024-11-11 00:28:21 +00:00
|
|
|
Self::List(values) => {
|
|
|
|
write!(f, "[")?;
|
|
|
|
|
|
|
|
for (index, value) in values.iter().enumerate() {
|
|
|
|
if index > 0 {
|
|
|
|
write!(f, ", ")?;
|
|
|
|
}
|
|
|
|
|
|
|
|
write!(f, "{}", value)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
write!(f, "]")
|
|
|
|
}
|
2024-11-07 07:10:02 +00:00
|
|
|
Self::StackPointer(index) => write!(f, "R{}", index),
|
|
|
|
Self::ConstantPointer(index) => write!(f, "C{}", index),
|
|
|
|
Self::ParentStackPointer(index) => write!(f, "PR{}", index),
|
|
|
|
Self::ParentConstantPointer(index) => write!(f, "PC{}", index),
|
|
|
|
}
|
|
|
|
}
|
2024-10-12 07:06:44 +00:00
|
|
|
}
|
|
|
|
|
2024-11-06 18:50:49 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
2024-09-12 04:39:31 +00:00
|
|
|
pub enum VmError {
|
2024-11-06 08:36:46 +00:00
|
|
|
// Stack errors
|
2024-11-11 00:28:21 +00:00
|
|
|
StackOverflow {
|
|
|
|
position: Span,
|
|
|
|
},
|
|
|
|
StackUnderflow {
|
|
|
|
position: Span,
|
|
|
|
},
|
2024-11-06 08:36:46 +00:00
|
|
|
|
|
|
|
// Register errors
|
2024-11-11 00:28:21 +00:00
|
|
|
EmptyRegister {
|
|
|
|
index: usize,
|
|
|
|
position: Span,
|
|
|
|
},
|
|
|
|
RegisterIndexOutOfBounds {
|
|
|
|
index: usize,
|
|
|
|
position: Span,
|
|
|
|
},
|
2024-11-06 08:36:46 +00:00
|
|
|
|
2024-11-09 18:49:02 +00:00
|
|
|
// Local errors
|
2024-11-11 00:28:21 +00:00
|
|
|
UndefinedLocal {
|
|
|
|
local_index: u8,
|
|
|
|
position: Span,
|
|
|
|
},
|
2024-11-09 18:49:02 +00:00
|
|
|
|
2024-11-06 08:36:46 +00:00
|
|
|
// Execution errors
|
2024-11-11 00:28:21 +00:00
|
|
|
ExpectedBoolean {
|
|
|
|
found: ConcreteValue,
|
|
|
|
position: Span,
|
|
|
|
},
|
|
|
|
ExpectedFunction {
|
|
|
|
found: ConcreteValue,
|
|
|
|
position: Span,
|
|
|
|
},
|
|
|
|
ExpectedParent {
|
|
|
|
position: Span,
|
|
|
|
},
|
2024-09-12 04:39:31 +00:00
|
|
|
|
|
|
|
// Wrappers for foreign errors
|
2024-11-11 00:28:21 +00:00
|
|
|
Chunk {
|
|
|
|
error: ChunkError,
|
|
|
|
position: Span,
|
|
|
|
},
|
2024-10-30 13:32:46 +00:00
|
|
|
NativeFunction(NativeFunctionError),
|
2024-11-11 00:28:21 +00:00
|
|
|
Value {
|
|
|
|
error: ValueError,
|
|
|
|
position: Span,
|
|
|
|
},
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl AnnotatedError for VmError {
|
|
|
|
fn title() -> &'static str {
|
|
|
|
"Runtime Error"
|
|
|
|
}
|
|
|
|
|
|
|
|
fn description(&self) -> &'static str {
|
|
|
|
match self {
|
2024-11-06 18:50:49 +00:00
|
|
|
Self::Chunk { .. } => "Chunk error",
|
2024-09-12 04:39:31 +00:00
|
|
|
Self::EmptyRegister { .. } => "Empty register",
|
2024-09-19 15:41:18 +00:00
|
|
|
Self::ExpectedBoolean { .. } => "Expected boolean",
|
2024-10-13 06:33:58 +00:00
|
|
|
Self::ExpectedFunction { .. } => "Expected function",
|
2024-11-07 07:10:02 +00:00
|
|
|
Self::ExpectedParent { .. } => "Expected parent",
|
2024-11-06 08:36:46 +00:00
|
|
|
Self::NativeFunction(error) => error.description(),
|
|
|
|
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
2024-09-12 04:39:31 +00:00
|
|
|
Self::StackOverflow { .. } => "Stack overflow",
|
|
|
|
Self::StackUnderflow { .. } => "Stack underflow",
|
2024-11-09 18:49:02 +00:00
|
|
|
Self::UndefinedLocal { .. } => "Undefined local",
|
2024-10-12 07:06:44 +00:00
|
|
|
Self::Value { .. } => "Value error",
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn details(&self) -> Option<String> {
|
|
|
|
match self {
|
2024-11-06 18:50:49 +00:00
|
|
|
Self::Chunk { error, .. } => Some(error.to_string()),
|
2024-11-06 08:36:46 +00:00
|
|
|
Self::EmptyRegister { index, .. } => Some(format!("Register R{index} is empty")),
|
2024-10-13 06:33:58 +00:00
|
|
|
Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")),
|
2024-11-11 00:28:21 +00:00
|
|
|
|
2024-10-13 08:21:07 +00:00
|
|
|
Self::RegisterIndexOutOfBounds { index, .. } => {
|
2024-10-13 20:46:45 +00:00
|
|
|
Some(format!("Register {index} does not exist"))
|
2024-10-13 08:21:07 +00:00
|
|
|
}
|
2024-10-30 13:32:46 +00:00
|
|
|
Self::NativeFunction(error) => error.details(),
|
2024-09-12 04:39:31 +00:00
|
|
|
Self::Value { error, .. } => Some(error.to_string()),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn position(&self) -> Span {
|
|
|
|
match self {
|
2024-11-06 18:50:49 +00:00
|
|
|
Self::Chunk { position, .. } => *position,
|
2024-09-12 04:39:31 +00:00
|
|
|
Self::EmptyRegister { position, .. } => *position,
|
2024-09-19 15:41:18 +00:00
|
|
|
Self::ExpectedBoolean { position, .. } => *position,
|
2024-10-13 06:33:58 +00:00
|
|
|
Self::ExpectedFunction { position, .. } => *position,
|
2024-11-07 07:10:02 +00:00
|
|
|
Self::ExpectedParent { position } => *position,
|
2024-10-30 13:32:46 +00:00
|
|
|
Self::NativeFunction(error) => error.position(),
|
2024-11-06 08:36:46 +00:00
|
|
|
Self::RegisterIndexOutOfBounds { position, .. } => *position,
|
|
|
|
Self::StackOverflow { position } => *position,
|
|
|
|
Self::StackUnderflow { position } => *position,
|
2024-11-09 18:49:02 +00:00
|
|
|
Self::UndefinedLocal { position, .. } => *position,
|
2024-09-12 04:39:31 +00:00
|
|
|
Self::Value { position, .. } => *position,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|