1
0
dust/dust-lang/src/vm.rs

733 lines
26 KiB
Rust
Raw Normal View History

2024-11-06 00:38:26 +00:00
//! Virtual machine and errors
use std::{
cmp::Ordering,
collections::HashMap,
fmt::{self, Display, Formatter},
};
use crate::{
2024-11-16 00:18:00 +00:00
compile, value::Value, AnnotatedError, Chunk, ChunkError, DustError, Instruction,
NativeFunction, NativeFunctionError, Operation, Span, Type, ValueError,
};
2024-11-16 00:18:00 +00:00
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
let chunk = compile(source)?;
2024-11-16 00:18:00 +00:00
let has_return_value = *chunk.r#type().return_type != Type::None;
let mut vm = Vm::new(&chunk, None);
vm.run()
2024-11-16 00:18:00 +00:00
.map_err(|error| DustError::Runtime { error, source })?;
if has_return_value {
vm.take_top_of_stack_as_value()
.map(Some)
.map_err(|error| DustError::Runtime { error, source })
} else {
Ok(None)
}
}
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-11-16 00:18:00 +00:00
#[derive(Debug, PartialEq)]
pub struct Vm<'a> {
chunk: &'a Chunk,
stack: Vec<Register>,
parent: Option<&'a Vm<'a>>,
ip: usize,
local_definitions: HashMap<u8, u8>,
last_assigned_register: Option<u8>,
2024-11-16 00:18:00 +00:00
current_position: Span,
}
2024-11-16 00:18:00 +00:00
impl<'a> Vm<'a> {
const STACK_LIMIT: usize = u16::MAX as usize;
2024-11-16 00:18:00 +00:00
pub fn new(chunk: &'a Chunk, parent: Option<&'a Vm<'a>>) -> Self {
Self {
chunk,
stack: Vec::new(),
2024-11-16 00:18:00 +00:00
parent,
ip: 0,
local_definitions: HashMap::new(),
last_assigned_register: None,
2024-11-16 00:18:00 +00:00
current_position: Span(0, 0),
}
}
2024-11-16 00:18:00 +00:00
pub fn current_position(&self) -> Span {
self.current_position
}
pub fn run(&mut self) -> Result<(), VmError> {
while let Ok(instruction) = self.read() {
log::info!(
"{} | {} | {} | {}",
self.ip - 1,
2024-11-16 00:18:00 +00:00
self.current_position,
instruction.operation(),
instruction.disassembly_info(self.chunk)
);
2024-09-15 05:24:04 +00:00
match instruction.operation() {
Operation::Move => {
let to_register = instruction.a();
let from_register = instruction.b();
let from_register_has_value = self
.stack
.get(from_register as usize)
.is_some_and(|register| !matches!(register, Register::Empty));
2024-11-16 00:18:00 +00:00
let register = Register::Pointer(Pointer::Stack(from_register));
if from_register_has_value {
2024-11-16 00:18:00 +00:00
self.set_register(to_register, register)?;
}
}
2024-09-18 03:06:58 +00:00
Operation::Close => {
let from_register = instruction.b();
let to_register = instruction.c();
if self.stack.len() < to_register as usize {
2024-11-16 00:18:00 +00:00
return Err(VmError::StackUnderflow {
position: self.current_position,
});
}
2024-09-18 03:06:58 +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
}
}
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-16 00:18:00 +00:00
let boolean = Value::boolean(boolean);
2024-11-16 00:18:00 +00:00
self.set_register(to_register, Register::Value(boolean))?;
2024-10-30 18:48:30 +00:00
if jump {
self.ip += 1;
}
}
Operation::LoadConstant => {
2024-09-24 20:49:17 +00:00
let to_register = instruction.a();
let from_constant = instruction.b();
let jump = instruction.c_as_boolean();
self.set_register(
to_register,
2024-11-16 00:18:00 +00:00
Register::Pointer(Pointer::Constant(from_constant)),
)?;
if jump {
self.ip += 1
}
}
2024-09-17 23:35:33 +00:00
Operation::LoadList => {
2024-09-24 20:49:17 +00:00
let to_register = instruction.a();
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 {
2024-11-16 00:18:00 +00:00
let value = self.open_register(register_index)?;
2024-11-11 00:28:21 +00:00
list.push(value);
}
2024-11-16 00:18:00 +00:00
// self.set_register(to_register, Register::List(list))?;
todo!()
2024-09-17 23:35:33 +00:00
}
Operation::LoadSelf => {
let to_register = instruction.a();
2024-11-16 00:18:00 +00:00
// self.set_register(to_register, Register::Value(function))?;
2024-11-11 00:28:21 +00:00
todo!()
}
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();
self.define_local(to_local, from_register)?;
}
Operation::GetLocal => {
let to_register = instruction.a();
2024-09-24 20:49:17 +00:00
let local_index = instruction.b();
let local_register = self.local_definitions.get(&local_index).copied().ok_or(
VmError::UndefinedLocal {
local_index,
2024-11-16 00:18:00 +00:00
position: self.current_position,
},
)?;
2024-11-16 00:18:00 +00:00
let register = Register::Pointer(Pointer::Stack(local_register));
2024-11-16 00:18:00 +00:00
self.set_register(to_register, register)?;
}
2024-09-15 01:05:03 +00:00
Operation::SetLocal => {
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,
2024-11-16 00:18:00 +00:00
position: self.current_position,
},
)?;
2024-11-16 00:18:00 +00:00
let register = Register::Pointer(Pointer::Stack(from_register));
2024-11-16 00:18:00 +00:00
self.set_register(local_register, register)?;
2024-09-15 01:05:03 +00:00
}
Operation::Add => {
let to_register = instruction.a();
2024-11-16 00:18:00 +00:00
let (left, right) = self.get_arguments(instruction)?;
let sum = left.add(right).map_err(|error| VmError::Value {
error,
position: self.current_position,
})?;
2024-11-16 00:18:00 +00:00
self.set_register(to_register, Register::Value(sum))?;
}
Operation::Subtract => {
let to_register = instruction.a();
2024-11-16 00:18:00 +00:00
let (left, right) = self.get_arguments(instruction)?;
let difference = left.subtract(right).map_err(|error| VmError::Value {
error,
position: self.current_position,
})?;
2024-11-16 00:18:00 +00:00
self.set_register(to_register, Register::Value(difference))?;
}
Operation::Multiply => {
let to_register = instruction.a();
2024-11-16 00:18:00 +00:00
let (left, right) = self.get_arguments(instruction)?;
let product = left.multiply(right).map_err(|error| VmError::Value {
error,
position: self.current_position,
})?;
2024-11-16 00:18:00 +00:00
self.set_register(to_register, Register::Value(product))?;
}
Operation::Divide => {
let to_register = instruction.a();
2024-11-16 00:18:00 +00:00
let (left, right) = self.get_arguments(instruction)?;
let quotient = left.divide(right).map_err(|error| VmError::Value {
error,
position: self.current_position,
})?;
2024-11-16 00:18:00 +00:00
self.set_register(to_register, Register::Value(quotient))?;
}
Operation::Modulo => {
let to_register = instruction.a();
2024-11-16 00:18:00 +00:00
let (left, right) = self.get_arguments(instruction)?;
let remainder = left.modulo(right).map_err(|error| VmError::Value {
error,
position: self.current_position,
})?;
2024-11-16 00:18:00 +00:00
self.set_register(to_register, Register::Value(remainder))?;
}
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-16 00:18:00 +00:00
let value = self.open_register(register)?;
let boolean = if let Value::Boolean(boolean) = value {
*boolean
} else {
return Err(VmError::ExpectedBoolean {
2024-11-16 00:18:00 +00:00
found: value.clone(),
position: self.current_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-10-19 07:06:14 +00:00
Operation::TestSet => todo!(),
Operation::Equal => {
debug_assert_eq!(
2024-11-16 00:18:00 +00:00
self.get_instruction(self.ip)?.0.operation(),
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();
2024-11-16 00:18:00 +00:00
let (left, right) = self.get_arguments(instruction)?;
let equal_result = left.equal(right).map_err(|error| VmError::Value {
error,
position: self.current_position,
})?;
let is_equal = if let Value::Boolean(boolean) = equal_result {
2024-11-11 00:28:21 +00:00
boolean
} else {
return Err(VmError::ExpectedBoolean {
found: equal_result.clone(),
2024-11-16 00:18:00 +00:00
position: self.current_position,
2024-11-11 00:28:21 +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-16 00:18:00 +00:00
let jump = self.get_instruction(self.ip)?.0;
2024-09-24 14:16:19 +00:00
2024-11-11 00:28:21 +00:00
self.jump(jump);
}
}
Operation::Less => {
debug_assert_eq!(
2024-11-16 00:18:00 +00:00
self.get_instruction(self.ip)?.0.operation(),
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();
2024-11-16 00:18:00 +00:00
let (left, right) = self.get_arguments(instruction)?;
let less_result = left.less_than(right).map_err(|error| VmError::Value {
error,
position: self.current_position,
})?;
let is_less_than = if let Value::Boolean(boolean) = less_result {
2024-11-11 00:28:21 +00:00
boolean
} else {
return Err(VmError::ExpectedBoolean {
found: less_result.clone(),
2024-11-16 00:18:00 +00:00
position: self.current_position,
2024-11-11 00:28:21 +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-16 00:18:00 +00:00
let jump = self.get_instruction(self.ip)?.0;
2024-09-24 14:16:19 +00:00
2024-11-11 00:28:21 +00:00
self.jump(jump);
}
}
Operation::LessEqual => {
debug_assert_eq!(
2024-11-16 00:18:00 +00:00
self.get_instruction(self.ip)?.0.operation(),
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();
2024-11-16 00:18:00 +00:00
let (left, right) = self.get_arguments(instruction)?;
let less_or_equal_result =
left.less_than_or_equal(right)
.map_err(|error| VmError::Value {
error,
position: self.current_position,
})?;
2024-11-11 00:28:21 +00:00
let is_less_than_or_equal =
2024-11-16 00:18:00 +00:00
if let Value::Boolean(boolean) = less_or_equal_result {
2024-11-11 00:28:21 +00:00
boolean
} else {
return Err(VmError::ExpectedBoolean {
found: less_or_equal_result.clone(),
2024-11-16 00:18:00 +00:00
position: self.current_position,
2024-11-11 00:28:21 +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-16 00:18:00 +00:00
let jump = self.get_instruction(self.ip)?.0;
2024-09-24 14:16:19 +00:00
2024-11-11 00:28:21 +00:00
self.jump(jump);
}
}
Operation::Negate => {
2024-11-16 00:18:00 +00:00
let value = self.get_argument(instruction.b(), instruction.b_is_constant())?;
let negated = value.negate().map_err(|error| VmError::Value {
error,
position: self.current_position,
})?;
2024-11-16 00:18:00 +00:00
self.set_register(instruction.a(), Register::Value(negated))?;
}
Operation::Not => {
2024-11-16 00:18:00 +00:00
let value = self.get_argument(instruction.b(), instruction.b_is_constant())?;
let not = value.not().map_err(|error| VmError::Value {
error,
position: self.current_position,
})?;
2024-11-16 00:18:00 +00:00
self.set_register(instruction.a(), Register::Value(not))?;
}
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-16 00:18:00 +00:00
let value = self.open_register(function_register)?;
let chunk = if let Value::Function(chunk) = value {
chunk
} else {
return Err(VmError::ExpectedFunction {
2024-11-16 00:18:00 +00:00
found: value.clone(),
position: self.current_position,
});
};
2024-11-16 00:18:00 +00:00
let has_return_value = *chunk.r#type().return_type != Type::None;
let mut function_vm = Vm::new(chunk, Some(self));
2024-10-19 21:24:22 +00:00
let first_argument_index = function_register + 1;
2024-11-16 00:18:00 +00:00
let last_argument_index = first_argument_index + argument_count;
2024-10-12 14:55:34 +00:00
2024-11-16 00:18:00 +00:00
for argument_index in first_argument_index..last_argument_index {
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
function_vm.set_register(
top_of_stack,
2024-11-16 00:18:00 +00:00
Register::Pointer(Pointer::ParentStack(argument_index)),
)?
2024-10-12 14:55:34 +00:00
}
2024-11-16 00:18:00 +00:00
function_vm.run()?;
if has_return_value {
let top_of_stack = function_vm.stack.len() as u8 - 1;
2024-10-12 14:55:34 +00:00
2024-11-16 00:18:00 +00:00
self.set_register(
to_register,
Register::Pointer(Pointer::ParentStack(top_of_stack)),
)?;
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-16 00:18:00 +00:00
let return_value = native_function.call(self, instruction)?;
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-16 00:18:00 +00:00
self.set_register(to_register, Register::Value(concrete_value))?;
2024-10-30 07:08:25 +00:00
}
}
Operation::Return => {
let should_return_value = instruction.b_as_boolean();
if !should_return_value {
2024-11-16 00:18:00 +00:00
return Ok(());
}
2024-11-11 00:28:21 +00:00
return if let Some(register_index) = self.last_assigned_register {
2024-11-16 00:18:00 +00:00
let top_of_stack = self.stack.len() as u8 - 1;
if register_index != top_of_stack {
self.stack
.push(Register::Pointer(Pointer::Stack(register_index)));
}
Ok(())
} else {
2024-11-16 00:18:00 +00:00
Err(VmError::StackUnderflow {
position: self.current_position,
})
};
}
}
}
2024-11-16 00:18:00 +00:00
Ok(())
}
2024-11-16 00:18:00 +00:00
fn resolve_pointer(&self, pointer: Pointer) -> Result<&Value, VmError> {
match pointer {
Pointer::Stack(register_index) => self.open_register(register_index),
Pointer::Constant(constant_index) => self.get_constant(constant_index),
Pointer::ParentStack(register_index) => {
let parent = self
.parent
.as_ref()
.ok_or_else(|| VmError::ExpectedParent {
position: self.current_position,
})?;
2024-11-11 00:28:21 +00:00
2024-11-16 00:18:00 +00:00
parent.open_register(register_index)
2024-11-11 00:28:21 +00:00
}
2024-11-16 00:18:00 +00:00
Pointer::ParentConstant(constant_index) => {
2024-11-11 00:28:21 +00:00
let parent = self
.parent
.as_ref()
2024-11-16 00:18:00 +00:00
.ok_or_else(|| VmError::ExpectedParent {
position: self.current_position,
})?;
2024-11-11 00:28:21 +00:00
2024-11-16 00:18:00 +00:00
parent.get_constant(constant_index)
2024-11-11 00:28:21 +00:00
}
}
}
2024-11-16 00:18:00 +00:00
pub(crate) fn open_register(&self, register_index: u8) -> Result<&Value, VmError> {
2024-11-11 00:28:21 +00:00
let register_index = register_index as usize;
let register =
self.stack
.get(register_index)
.ok_or_else(|| VmError::RegisterIndexOutOfBounds {
index: register_index,
2024-11-16 00:18:00 +00:00
position: self.current_position,
2024-11-11 00:28:21 +00:00
})?;
2024-11-16 00:18:00 +00:00
log::trace!("Open R{register_index} to {register}");
2024-11-11 00:28:21 +00:00
2024-11-16 00:18:00 +00:00
match register {
Register::Value(value) => Ok(value),
Register::Pointer(pointer) => self.resolve_pointer(*pointer),
Register::Empty => Err(VmError::EmptyRegister {
index: register_index,
position: self.current_position,
}),
}
}
2024-11-11 00:28:21 +00:00
2024-11-16 00:18:00 +00:00
fn take_top_of_stack_as_value(&mut self) -> Result<Value, VmError> {
let top_of_stack = self.stack.pop().ok_or(VmError::StackUnderflow {
position: self.current_position,
})?;
2024-11-11 00:28:21 +00:00
2024-11-16 00:18:00 +00:00
match top_of_stack {
Register::Value(value) => Ok(value),
_ => Err(VmError::ExpectedValue {
found: top_of_stack,
position: self.current_position,
}),
}
2024-11-11 00:28:21 +00:00
}
/// 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
2024-11-16 00:18:00 +00:00
fn get_argument(&self, index: u8, is_constant: bool) -> Result<&Value, VmError> {
2024-11-11 00:28:21 +00:00
let argument = if is_constant {
2024-11-16 00:18:00 +00:00
self.get_constant(index)?
2024-11-11 00:28:21 +00:00
} else {
2024-11-16 00:18:00 +00:00
self.open_register(index)?
2024-11-11 00:28:21 +00:00
};
Ok(argument)
}
/// DRY helper to get two arguments for binary operations
2024-11-16 00:18:00 +00:00
fn get_arguments(&self, instruction: Instruction) -> Result<(&Value, &Value), VmError> {
let left = self.get_argument(instruction.b(), instruction.b_is_constant())?;
let right = self.get_argument(instruction.c(), instruction.c_is_constant())?;
2024-11-11 00:28:21 +00:00
Ok((left, right))
}
2024-11-16 00:18:00 +00:00
fn set_register(&mut self, to_register: u8, register: Register) -> Result<(), VmError> {
self.last_assigned_register = Some(to_register);
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
if length == Self::STACK_LIMIT {
2024-11-16 00:18:00 +00:00
return Err(VmError::StackOverflow {
position: self.current_position,
});
}
2024-10-13 08:21:07 +00:00
match to_register.cmp(&length) {
Ordering::Less => {
log::trace!("Change R{to_register} to {register}");
self.stack[to_register] = register;
2024-10-13 08:21:07 +00:00
Ok(())
}
Ordering::Equal => {
log::trace!("Set R{to_register} to {register}");
self.stack.push(register);
2024-09-19 15:41:18 +00:00
2024-10-13 08:21:07 +00:00
Ok(())
}
Ordering::Greater => {
let difference = to_register - length;
for index in 0..difference {
log::trace!("Set R{index} to {register}");
self.stack.push(Register::Empty);
}
log::trace!("Set R{to_register} to {register}");
self.stack.push(register);
Ok(())
}
2024-10-13 08:21:07 +00:00
}
}
2024-09-19 15:41:18 +00:00
2024-11-16 00:18:00 +00:00
fn get_constant(&self, index: u8) -> Result<&Value, VmError> {
2024-11-06 18:50:49 +00:00
self.chunk
.get_constant(index)
2024-11-16 00:18:00 +00:00
.map_err(|error| VmError::Chunk {
error,
position: self.current_position,
})
2024-11-06 18:50:49 +00:00
}
2024-11-16 00:18:00 +00:00
fn read(&mut self) -> Result<Instruction, VmError> {
let (instruction, position) = *self.get_instruction(self.ip)?;
2024-11-11 00:28:21 +00:00
self.ip += 1;
2024-11-16 00:18:00 +00:00
self.current_position = position;
2024-11-16 00:18:00 +00:00
Ok(instruction)
}
2024-11-16 00:18:00 +00:00
fn get_instruction(&self, index: usize) -> Result<&(Instruction, Span), VmError> {
self.chunk
2024-11-06 18:50:49 +00:00
.get_instruction(index)
2024-11-16 00:18:00 +00:00
.map_err(|error| VmError::Chunk {
error,
position: self.current_position,
})
}
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-11-16 00:18:00 +00:00
#[derive(Clone, Debug, PartialEq)]
enum Register {
Empty,
2024-11-16 00:18:00 +00:00
Value(Value),
Pointer(Pointer),
}
2024-11-16 00:18:00 +00:00
impl Display for Register {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Empty => write!(f, "empty"),
Self::Value(value) => write!(f, "{}", value),
2024-11-16 00:18:00 +00:00
Self::Pointer(pointer) => write!(f, "{}", pointer),
}
}
}
2024-11-11 00:28:21 +00:00
2024-11-16 00:18:00 +00:00
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Pointer {
Stack(u8),
Constant(u8),
ParentStack(u8),
ParentConstant(u8),
}
2024-11-11 00:28:21 +00:00
2024-11-16 00:18:00 +00:00
impl Display for Pointer {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Stack(index) => write!(f, "R{}", index),
Self::Constant(index) => write!(f, "C{}", index),
Self::ParentStack(index) => write!(f, "PR{}", index),
Self::ParentConstant(index) => write!(f, "PC{}", index),
}
}
}
2024-11-06 18:50:49 +00:00
#[derive(Clone, Debug, PartialEq)]
pub enum VmError {
// Stack errors
2024-11-16 00:18:00 +00:00
StackOverflow { position: Span },
StackUnderflow { position: Span },
// Register errors
2024-11-16 00:18:00 +00:00
EmptyRegister { index: usize, position: Span },
ExpectedValue { found: Register, position: Span },
RegisterIndexOutOfBounds { index: usize, position: Span },
// Local errors
2024-11-16 00:18:00 +00:00
UndefinedLocal { local_index: u8, position: Span },
// Execution errors
2024-11-16 00:18:00 +00:00
ExpectedBoolean { found: Value, position: Span },
ExpectedFunction { found: Value, position: Span },
ExpectedParent { position: Span },
// Wrappers for foreign errors
2024-11-16 00:18:00 +00:00
Chunk { error: ChunkError, position: Span },
2024-10-30 13:32:46 +00:00
NativeFunction(NativeFunctionError),
2024-11-16 00:18:00 +00:00
Value { error: ValueError, position: Span },
}
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",
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",
Self::ExpectedParent { .. } => "Expected parent",
2024-11-16 00:18:00 +00:00
Self::ExpectedValue { .. } => "Expected value",
Self::NativeFunction(error) => error.description(),
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
Self::StackOverflow { .. } => "Stack overflow",
Self::StackUnderflow { .. } => "Stack underflow",
Self::UndefinedLocal { .. } => "Undefined local",
Self::Value { .. } => "Value error",
}
}
fn details(&self) -> Option<String> {
match self {
2024-11-06 18:50:49 +00:00
Self::Chunk { error, .. } => Some(error.to_string()),
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, .. } => {
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(),
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,
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,
Self::ExpectedParent { position } => *position,
2024-11-16 00:18:00 +00:00
Self::ExpectedValue { position, .. } => *position,
2024-10-30 13:32:46 +00:00
Self::NativeFunction(error) => error.position(),
Self::RegisterIndexOutOfBounds { position, .. } => *position,
Self::StackOverflow { position } => *position,
Self::StackUnderflow { position } => *position,
Self::UndefinedLocal { position, .. } => *position,
Self::Value { position, .. } => *position,
}
}
}