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

803 lines
29 KiB
Rust
Raw Normal View History

2024-11-06 00:38:26 +00:00
//! Virtual machine and errors
2024-10-30 13:32:46 +00:00
use std::{cmp::Ordering, mem::replace};
use crate::{
parse, value::ConcreteValue, AnnotatedError, Chunk, DustError, FunctionType, Instruction,
Local, NativeFunction, NativeFunctionError, Operation, Span, Type, Value, ValueError,
};
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
let chunk = parse(source)?;
let vm = Vm::new(chunk);
vm.run()
.map_err(|error| DustError::Runtime { error, source })
}
2024-11-06 00:38:26 +00:00
/// Dust virtual machine.
///
/// See the [module-level documentation](index.html) for more information.
#[derive(Debug, Eq, PartialEq)]
pub struct Vm {
ip: usize,
chunk: Chunk,
stack: Vec<Register>,
last_assigned_register: Option<u8>,
}
impl Vm {
const STACK_LIMIT: usize = u16::MAX as usize;
pub fn new(chunk: Chunk) -> Self {
Self {
ip: 0,
chunk,
stack: Vec::new(),
last_assigned_register: None,
}
}
pub fn run(mut self) -> Result<Option<Value>, VmError> {
2024-09-24 03:38:49 +00:00
// DRY helper to get constant or register values for binary operations
2024-09-23 23:16:15 +00:00
fn get_arguments(
2024-09-19 03:02:28 +00:00
vm: &mut Vm,
instruction: Instruction,
position: Span,
2024-09-23 23:16:15 +00:00
) -> Result<(&Value, &Value), VmError> {
2024-09-24 20:49:17 +00:00
let left = if instruction.b_is_constant() {
vm.chunk.get_constant(instruction.b()).ok_or_else(|| {
VmError::ConstantIndexOutOfBounds {
index: instruction.b() as usize,
position,
}
})?
2024-09-18 01:10:44 +00:00
} else {
vm.get_register(instruction.b(), position)?
2024-09-18 01:10:44 +00:00
};
2024-09-24 20:49:17 +00:00
let right = if instruction.c_is_constant() {
vm.chunk.get_constant(instruction.c()).ok_or_else(|| {
VmError::ConstantIndexOutOfBounds {
index: instruction.c() as usize,
position,
}
})?
2024-09-18 01:10:44 +00:00
} else {
vm.get_register(instruction.c(), position)?
2024-09-18 01:10:44 +00:00
};
Ok((left, right))
2024-09-19 03:02:28 +00:00
}
2024-09-18 01:10:44 +00:00
while let Ok((instruction, position)) = self.read(Span(0, 0)).copied() {
log::info!(
"{} | {} | {} | {}",
self.ip - 1,
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();
self.set_pointer(to_register, from_register, position)?;
}
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 {
return Err(VmError::StackUnderflow { 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();
let value = Value::boolean(boolean);
self.set_register(to_register, value, position)?;
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_constant(to_register, from_constant, position)?;
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();
let item_type = (start_register..to_register)
.find_map(|register_index| {
if let Ok(value) = self.get_register(register_index, position) {
Some(value.r#type())
} else {
None
}
})
.unwrap_or(Type::Any);
let value = Value::abstract_list(start_register, to_register, item_type);
2024-09-17 23:35:33 +00:00
self.set_register(to_register, value, position)?;
2024-09-17 23:35:33 +00:00
}
Operation::LoadSelf => {
let to_register = instruction.a();
let value = Value::function(
self.chunk.clone(),
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: None,
},
);
self.set_register(to_register, value, position)?;
}
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, position)?;
}
Operation::GetLocal => {
let to_register = instruction.a();
2024-09-24 20:49:17 +00:00
let local_index = instruction.b();
let local = self.get_local(local_index, position)?;
2024-10-13 08:21:07 +00:00
self.set_pointer(to_register, local.register_index, position)?;
}
2024-09-15 01:05:03 +00:00
Operation::SetLocal => {
let register = instruction.a();
2024-09-24 20:49:17 +00:00
let local_index = instruction.b();
self.define_local(local_index, register, position)?;
2024-09-15 01:05:03 +00:00
}
Operation::Add => {
let (left, right) = get_arguments(&mut self, instruction, position)?;
let sum = left
2024-09-23 12:57:49 +00:00
.add(right)
.map_err(|error| VmError::Value { error, position })?;
self.set_register(instruction.a(), sum, position)?;
}
Operation::Subtract => {
let (left, right) = get_arguments(&mut self, instruction, position)?;
let difference = left
2024-09-23 12:57:49 +00:00
.subtract(right)
.map_err(|error| VmError::Value { error, position })?;
self.set_register(instruction.a(), difference, position)?;
}
Operation::Multiply => {
let (left, right) = get_arguments(&mut self, instruction, position)?;
let product = left
2024-09-23 12:57:49 +00:00
.multiply(right)
.map_err(|error| VmError::Value { error, position })?;
self.set_register(instruction.a(), product, position)?;
}
Operation::Divide => {
let (left, right) = get_arguments(&mut self, instruction, position)?;
let quotient = left
2024-09-23 12:57:49 +00:00
.divide(right)
.map_err(|error| VmError::Value { error, position })?;
self.set_register(instruction.a(), quotient, position)?;
}
Operation::Modulo => {
let (left, right) = get_arguments(&mut self, instruction, position)?;
let remainder = left
2024-09-23 12:57:49 +00:00
.modulo(right)
.map_err(|error| VmError::Value { error, position })?;
self.set_register(instruction.a(), remainder, position)?;
}
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();
let value = self.get_register(register, position)?;
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
*boolean
} else {
return Err(VmError::ExpectedBoolean {
found: value.clone(),
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!(
self.get_instruction(self.ip, position)?.0.operation(),
Operation::Jump
);
2024-09-24 14:16:19 +00:00
let (left, right) = get_arguments(&mut self, instruction, position)?;
let equal_result = left
2024-09-23 12:57:49 +00:00
.equal(right)
.map_err(|error| VmError::Value { error, position })?;
let boolean =
if let Value::Concrete(ConcreteValue::Boolean(boolean)) = equal_result {
boolean
} else {
return Err(VmError::ExpectedBoolean {
found: equal_result.clone(),
position,
});
};
2024-09-24 20:49:17 +00:00
let compare_to = instruction.a_as_boolean();
2024-09-24 14:16:19 +00:00
if boolean == compare_to {
self.ip += 1;
} else {
let (jump, _) = self.get_instruction(self.ip, position)?;
2024-09-24 20:49:17 +00:00
let jump_distance = jump.a();
2024-10-06 01:30:48 +00:00
let is_positive = jump.b_as_boolean();
2024-09-24 14:16:19 +00:00
let new_ip = if is_positive {
self.ip + jump_distance as usize
} else {
self.ip - jump_distance as usize
};
self.ip = new_ip;
}
}
Operation::Less => {
debug_assert_eq!(
self.get_instruction(self.ip, position)?.0.operation(),
Operation::Jump
);
2024-09-24 14:16:19 +00:00
let (left, right) = get_arguments(&mut self, instruction, position)?;
let less_result = left
2024-09-24 15:40:12 +00:00
.less_than(right)
.map_err(|error| VmError::Value { error, position })?;
let boolean =
if let Value::Concrete(ConcreteValue::Boolean(boolean)) = less_result {
boolean
} else {
return Err(VmError::ExpectedBoolean {
found: less_result.clone(),
position,
});
};
2024-09-24 20:49:17 +00:00
let compare_to = instruction.a_as_boolean();
2024-09-24 14:16:19 +00:00
if boolean == compare_to {
self.ip += 1;
} else {
let jump = self.get_instruction(self.ip, position)?.0;
2024-09-24 20:49:17 +00:00
let jump_distance = jump.a();
2024-10-06 01:30:48 +00:00
let is_positive = jump.b_as_boolean();
2024-09-24 14:16:19 +00:00
let new_ip = if is_positive {
self.ip + jump_distance as usize
} else {
self.ip - jump_distance as usize
};
self.ip = new_ip;
}
}
Operation::LessEqual => {
debug_assert_eq!(
self.get_instruction(self.ip, position)?.0.operation(),
Operation::Jump
);
2024-09-24 15:40:12 +00:00
let (left, right) = get_arguments(&mut self, instruction, position)?;
let less_or_equal_result = left
2024-09-24 15:40:12 +00:00
.less_than_or_equal(right)
.map_err(|error| VmError::Value { error, position })?;
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) =
less_or_equal_result
{
boolean
} else {
return Err(VmError::ExpectedBoolean {
found: less_or_equal_result.clone(),
2024-09-24 14:16:19 +00:00
position,
});
};
2024-09-24 20:49:17 +00:00
let compare_to = instruction.a_as_boolean();
2024-09-24 14:16:19 +00:00
if boolean == compare_to {
self.ip += 1;
} else {
let jump = self.get_instruction(self.ip, position)?.0;
2024-09-24 20:49:17 +00:00
let jump_distance = jump.a();
2024-10-06 01:30:48 +00:00
let is_positive = jump.b_as_boolean();
2024-09-24 14:16:19 +00:00
let new_ip = if is_positive {
self.ip + jump_distance as usize
} else {
self.ip - jump_distance as usize
};
self.ip = new_ip;
}
}
Operation::Negate => {
2024-09-24 20:49:17 +00:00
let value = if instruction.b_is_constant() {
self.chunk.get_constant(instruction.b()).ok_or_else(|| {
VmError::ConstantIndexOutOfBounds {
index: instruction.b() as usize,
position,
}
})?
2024-09-18 01:10:44 +00:00
} else {
self.get_register(instruction.b(), position)?
2024-09-18 01:10:44 +00:00
};
let negated = value
.negate()
.map_err(|error| VmError::Value { error, position })?;
self.set_register(instruction.a(), negated, position)?;
}
Operation::Not => {
2024-09-24 20:49:17 +00:00
let value = if instruction.b_is_constant() {
self.chunk.get_constant(instruction.b()).ok_or_else(|| {
VmError::ConstantIndexOutOfBounds {
index: instruction.b() as usize,
position,
}
})?
2024-09-18 01:10:44 +00:00
} else {
self.get_register(instruction.b(), position)?
2024-09-18 01:10:44 +00:00
};
2024-09-18 11:58:31 +00:00
let not = value
.not()
.map_err(|error| VmError::Value { error, position })?;
self.set_register(instruction.a(), not, position)?;
}
Operation::Jump => {
2024-10-30 18:48:30 +00:00
let jump_distance = instruction.b();
let is_positive = instruction.c_as_boolean();
let new_ip = if is_positive {
self.ip + jump_distance as usize
} else {
2024-11-01 00:33:46 +00:00
self.ip - jump_distance as usize - 1
2024-10-30 18:48:30 +00:00
};
self.ip = new_ip;
}
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();
let value = self.get_register(function_register, position)?.clone();
let function = if let Value::Concrete(ConcreteValue::Function(function)) = value
{
2024-10-12 14:55:34 +00:00
function
} else {
2024-10-13 06:33:58 +00:00
return Err(VmError::ExpectedFunction {
found: value,
position,
});
2024-10-12 14:55:34 +00:00
};
let mut function_vm = Vm::new(function.take_chunk());
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
{
let argument = match self.get_register(argument_index, position) {
Ok(value) => value.clone(),
Err(VmError::EmptyRegister { .. }) => continue,
Err(error) => return Err(error),
};
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, argument, position)?;
2024-10-12 14:55:34 +00:00
}
let return_value = function_vm.run()?;
if let Some(value) = return_value {
self.set_register(to_register, 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-10-30 12:02:22 +00:00
let return_value = native_function.call(instruction, &self, position)?;
2024-10-30 07:08:25 +00:00
if let Some(value) = return_value {
2024-10-30 12:02:22 +00:00
let to_register = instruction.a();
self.set_register(to_register, value, position)?;
2024-10-30 07:08:25 +00:00
}
}
Operation::Return => {
let should_return_value = instruction.b_as_boolean();
if !should_return_value {
return Ok(None);
}
if let Some(register) = self.last_assigned_register {
let value = self
.empty_register(register, position)?
.to_concrete(&mut self, position)?;
return Ok(Some(value));
} else {
return Err(VmError::StackUnderflow { position });
}
}
}
}
Ok(None)
}
fn set_register(
&mut self,
to_register: u8,
value: Value,
position: Span,
) -> Result<(), VmError> {
let length = self.stack.len();
self.last_assigned_register = Some(to_register);
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 {
return Err(VmError::StackOverflow { position });
}
2024-10-13 08:21:07 +00:00
match to_register.cmp(&length) {
Ordering::Less => {
2024-10-19 07:06:14 +00:00
log::trace!("Change R{to_register} to {value}");
2024-10-13 08:21:07 +00:00
self.stack[to_register] = Register::Value(value);
2024-10-13 08:21:07 +00:00
Ok(())
}
Ordering::Equal => {
2024-10-19 07:06:14 +00:00
log::trace!("Set R{to_register} to {value}");
2024-10-13 08:21:07 +00:00
self.stack.push(Register::Value(value));
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 empty");
self.stack.push(Register::Empty);
}
log::trace!("Set R{to_register} to {value}");
self.stack.push(Register::Value(value));
Ok(())
}
2024-10-13 08:21:07 +00:00
}
}
2024-09-19 15:41:18 +00:00
2024-10-13 08:21:07 +00:00
fn set_pointer(
&mut self,
to_register: u8,
from_register: u8,
position: Span,
) -> Result<(), VmError> {
let length = self.stack.len();
self.last_assigned_register = Some(to_register);
2024-10-13 08:21:07 +00:00
let to_register = to_register as usize;
if length == Self::STACK_LIMIT {
return Err(VmError::StackOverflow { position });
2024-09-19 15:41:18 +00:00
}
2024-10-13 08:21:07 +00:00
match to_register.cmp(&length) {
Ordering::Less => {
2024-10-19 07:06:14 +00:00
log::trace!("Change R{to_register} to R{from_register}");
2024-10-13 08:21:07 +00:00
self.stack[to_register] = Register::Pointer(from_register);
Ok(())
}
Ordering::Equal => {
2024-10-19 07:06:14 +00:00
log::trace!("Set R{to_register} to R{from_register}");
2024-10-13 08:21:07 +00:00
self.stack.push(Register::Pointer(from_register));
Ok(())
}
Ordering::Greater => {
let difference = to_register - length;
for index in 0..difference {
log::trace!("Set R{index} to empty");
self.stack.push(Register::Empty);
}
log::trace!("Set R{to_register} to R{from_register}");
self.stack.push(Register::Pointer(from_register));
Ok(())
}
2024-10-13 08:21:07 +00:00
}
2024-09-19 15:41:18 +00:00
}
fn set_constant(
&mut self,
to_register: u8,
constant_index: u8,
position: Span,
) -> Result<(), VmError> {
let length = self.stack.len();
self.last_assigned_register = Some(to_register);
let to_register = to_register as usize;
2024-09-23 12:57:49 +00:00
if length == Self::STACK_LIMIT {
return Err(VmError::StackOverflow { position });
}
match to_register.cmp(&length) {
Ordering::Less => {
log::trace!("Change R{to_register} to C{constant_index}");
self.stack[to_register] = Register::Constant(constant_index);
2024-09-23 12:57:49 +00:00
Ok(())
}
Ordering::Equal => {
log::trace!("Set R{to_register} to C{constant_index}");
self.stack.push(Register::Constant(constant_index));
Ok(())
}
Ordering::Greater => {
let difference = to_register - length;
for index in 0..difference {
log::trace!("Set R{index} to empty");
self.stack.push(Register::Empty);
}
log::trace!("Set R{to_register} to C{constant_index}");
self.stack.push(Register::Constant(constant_index));
Ok(())
}
}
2024-09-23 12:57:49 +00:00
}
pub fn get_register(&self, index: u8, position: Span) -> Result<&Value, VmError> {
2024-09-15 01:05:03 +00:00
let index = index as usize;
let register = self
.stack
.get(index)
2024-10-13 08:21:07 +00:00
.ok_or_else(|| VmError::RegisterIndexOutOfBounds { index, position })?;
2024-09-15 01:05:03 +00:00
match register {
Register::Value(value) => Ok(value),
Register::Pointer(register_index) => self.get_register(*register_index, position),
Register::Constant(constant_index) => self
.chunk
.get_constant(*constant_index)
.ok_or_else(|| VmError::ConstantIndexOutOfBounds {
index: *constant_index as usize,
position,
}),
Register::Empty => Err(VmError::EmptyRegister { index, position }),
}
}
pub fn empty_register(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
2024-10-12 14:55:34 +00:00
let index = index as usize;
if index >= self.stack.len() {
2024-10-13 08:21:07 +00:00
return Err(VmError::RegisterIndexOutOfBounds { index, position });
2024-09-18 11:58:31 +00:00
}
let register = replace(&mut self.stack[index], Register::Empty);
match register {
Register::Value(value) => Ok(value),
Register::Pointer(register_index) => {
let value = self.empty_register(register_index, position)?;
Ok(value)
}
Register::Constant(constant_index) => {
let constants = &mut self.chunk.constants_mut();
constants.push(Value::integer(0));
let value = constants.swap_remove(constant_index as usize);
self.chunk.is_poisoned = true;
Ok(value)
}
Register::Empty => Err(VmError::EmptyRegister { index, position }),
}
}
fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> {
if self.chunk.is_poisoned {
return Err(VmError::PoisonedChunk { position });
}
if self.ip >= self.chunk.len() {
self.ip = self.chunk.len() - 1;
}
let current = self.chunk.instructions().get(self.ip).ok_or_else(|| {
VmError::InstructionIndexOutOfBounds {
index: self.ip,
position,
}
})?;
self.ip += 1;
Ok(current)
}
fn define_local(
&mut self,
local_index: u8,
register_index: u8,
position: Span,
) -> Result<(), VmError> {
let local = self
.chunk
.locals_mut()
.get_mut(local_index as usize)
.ok_or_else(|| VmError::LocalIndexOutOfBounds {
index: local_index as usize,
position,
})?;
log::debug!("Define local L{}", local_index);
local.register_index = register_index;
Ok(())
}
fn get_local(&self, local_index: u8, position: Span) -> Result<&Local, VmError> {
let local_index = local_index as usize;
self.chunk
.locals()
.get(local_index)
.ok_or_else(|| VmError::LocalIndexOutOfBounds {
index: local_index,
position,
})
}
fn get_instruction(
&self,
index: usize,
position: Span,
) -> Result<&(Instruction, Span), VmError> {
self.chunk
.instructions()
.get(index)
.ok_or_else(|| VmError::InstructionIndexOutOfBounds { index, position })
}
}
#[derive(Debug, Eq, PartialEq)]
enum Register {
Empty,
Value(Value),
Pointer(u8),
Constant(u8),
}
#[derive(Debug, Clone, PartialEq)]
pub enum VmError {
// Stack errors
StackOverflow { position: Span },
StackUnderflow { position: Span },
// Register errors
EmptyRegister { index: usize, position: Span },
RegisterIndexOutOfBounds { index: usize, position: Span },
// Chunk errors
ConstantIndexOutOfBounds { index: usize, position: Span },
InstructionIndexOutOfBounds { index: usize, position: Span },
LocalIndexOutOfBounds { index: usize, position: Span },
PoisonedChunk { position: Span },
// Execution errors
ExpectedBoolean { found: Value, position: Span },
ExpectedFunction { found: Value, position: Span },
// Wrappers for foreign errors
2024-10-30 13:32:46 +00:00
NativeFunction(NativeFunctionError),
Value { error: ValueError, position: Span },
}
impl AnnotatedError for VmError {
fn title() -> &'static str {
"Runtime Error"
}
fn description(&self) -> &'static str {
match self {
Self::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
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::InstructionIndexOutOfBounds { .. } => "Instruction index out of bounds",
Self::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
Self::NativeFunction(error) => error.description(),
Self::PoisonedChunk { .. } => "Poisoned chunk",
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
Self::StackOverflow { .. } => "Stack overflow",
Self::StackUnderflow { .. } => "Stack underflow",
Self::Value { .. } => "Value error",
}
}
fn details(&self) -> Option<String> {
match self {
Self::ConstantIndexOutOfBounds { index, .. } => {
Some(format!("Constant C{index} does not exist"))
}
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-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
}
Self::InstructionIndexOutOfBounds { index, .. } => {
Some(format!("Instruction {index} does not exist"))
}
Self::LocalIndexOutOfBounds { index, .. } => {
Some(format!("Local L{index} does not exist"))
}
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 {
Self::ConstantIndexOutOfBounds { 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::InstructionIndexOutOfBounds { position, .. } => *position,
Self::LocalIndexOutOfBounds { position, .. } => *position,
2024-10-30 13:32:46 +00:00
Self::NativeFunction(error) => error.position(),
Self::PoisonedChunk { position } => *position,
Self::RegisterIndexOutOfBounds { position, .. } => *position,
Self::StackOverflow { position } => *position,
Self::StackUnderflow { position } => *position,
Self::Value { position, .. } => *position,
}
}
}