2024-10-30 09:16:34 +00:00
|
|
|
use std::{
|
|
|
|
cmp::Ordering,
|
|
|
|
io::{self, stdout, Write},
|
|
|
|
mem::replace,
|
|
|
|
};
|
2024-10-12 07:06:44 +00:00
|
|
|
|
2024-09-12 04:39:31 +00:00
|
|
|
use crate::{
|
2024-10-20 06:30:22 +00:00
|
|
|
parse, value::Primitive, AnnotatedError, Chunk, ChunkError, DustError, FunctionType,
|
2024-10-30 07:08:25 +00:00
|
|
|
Identifier, Instruction, NativeFunction, Operation, Span, Type, Value, ValueError,
|
2024-09-12 04:39:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
|
|
|
let chunk = parse(source)?;
|
2024-10-19 03:34:48 +00:00
|
|
|
let vm = Vm::new(chunk);
|
2024-09-12 04:39:31 +00:00
|
|
|
|
|
|
|
vm.run()
|
|
|
|
.map_err(|error| DustError::Runtime { error, source })
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
|
|
pub struct Vm {
|
|
|
|
ip: usize,
|
2024-10-12 07:06:44 +00:00
|
|
|
chunk: Chunk,
|
|
|
|
stack: Vec<Register>,
|
2024-10-30 03:11:55 +00:00
|
|
|
last_assigned_register: Option<u8>,
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Vm {
|
|
|
|
const STACK_LIMIT: usize = u16::MAX as usize;
|
|
|
|
|
|
|
|
pub fn new(chunk: Chunk) -> Self {
|
|
|
|
Self {
|
|
|
|
ip: 0,
|
2024-10-12 07:06:44 +00:00
|
|
|
chunk,
|
|
|
|
stack: Vec::new(),
|
2024-10-30 03:11:55 +00:00
|
|
|
last_assigned_register: None,
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-19 03:34:48 +00:00
|
|
|
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(), position)?
|
2024-09-18 01:10:44 +00:00
|
|
|
} else {
|
2024-09-24 20:49:17 +00:00
|
|
|
vm.get(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(), position)?
|
2024-09-18 01:10:44 +00:00
|
|
|
} else {
|
2024-09-24 20:49:17 +00:00
|
|
|
vm.get(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
|
|
|
|
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-10-20 14:20:09 +00:00
|
|
|
instruction.disassembly_info(Some(&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-10-19 07:06:14 +00:00
|
|
|
Operation::Move => todo!(),
|
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();
|
|
|
|
let skip = instruction.c_as_boolean();
|
2024-09-18 17:42:32 +00:00
|
|
|
let value = Value::boolean(boolean);
|
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
self.set(to_register, value, position)?;
|
2024-09-18 17:42:32 +00:00
|
|
|
|
2024-09-23 08:01:36 +00:00
|
|
|
if skip {
|
2024-09-18 17:42:32 +00:00
|
|
|
self.ip += 1;
|
|
|
|
}
|
|
|
|
}
|
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-10-12 07:06:44 +00:00
|
|
|
self.set_constant(to_register, from_constant, position)?;
|
2024-09-25 13:55:10 +00:00
|
|
|
|
|
|
|
if jump {
|
|
|
|
self.ip += 1;
|
|
|
|
}
|
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();
|
|
|
|
let first_register = instruction.b();
|
2024-09-25 02:58:14 +00:00
|
|
|
let last_register = instruction.c();
|
2024-10-13 20:46:45 +00:00
|
|
|
|
|
|
|
let is_empty = to_register == first_register && first_register == last_register;
|
|
|
|
let item_type = if is_empty {
|
|
|
|
Type::Any
|
|
|
|
} else {
|
|
|
|
self.get(first_register, position)?.r#type()
|
|
|
|
};
|
2024-10-12 10:16:12 +00:00
|
|
|
let value = Value::list(first_register, last_register, item_type);
|
2024-09-17 23:35:33 +00:00
|
|
|
|
2024-10-12 08:50:30 +00:00
|
|
|
self.set(to_register, value, position)?;
|
2024-09-17 23:35:33 +00:00
|
|
|
}
|
2024-10-20 06:30:22 +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(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();
|
2024-09-12 09:08:55 +00:00
|
|
|
|
2024-09-13 06:28:18 +00:00
|
|
|
self.chunk.define_local(to_local, from_register, position)?;
|
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-09-25 02:58:14 +00:00
|
|
|
let local = self.chunk.get_local(local_index, position)?;
|
2024-09-12 18:16:26 +00:00
|
|
|
|
2024-10-13 08:21:07 +00:00
|
|
|
self.set_pointer(to_register, local.register_index, position)?;
|
2024-09-12 09:08:55 +00:00
|
|
|
}
|
2024-09-15 01:05:03 +00:00
|
|
|
Operation::SetLocal => {
|
2024-10-12 07:06:44 +00:00
|
|
|
let register = instruction.a();
|
2024-09-24 20:49:17 +00:00
|
|
|
let local_index = instruction.b();
|
2024-09-25 02:58:14 +00:00
|
|
|
let local = self.chunk.get_local(local_index, position)?;
|
2024-10-05 04:11:03 +00:00
|
|
|
|
|
|
|
if !local.is_mutable {
|
|
|
|
return Err(VmError::CannotMutateImmutableLocal {
|
2024-09-25 02:58:14 +00:00
|
|
|
identifier: local.identifier.clone(),
|
|
|
|
position,
|
|
|
|
});
|
2024-10-05 04:11:03 +00:00
|
|
|
}
|
2024-09-25 02:58:14 +00:00
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
self.chunk.define_local(local_index, register, position)?;
|
2024-09-15 01:05:03 +00:00
|
|
|
}
|
2024-09-12 04:39:31 +00:00
|
|
|
Operation::Add => {
|
2024-10-19 03:34:48 +00:00
|
|
|
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
let sum = left
|
2024-09-23 12:57:49 +00:00
|
|
|
.add(right)
|
2024-09-12 04:39:31 +00:00
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
self.set(instruction.a(), sum, position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
|
|
|
Operation::Subtract => {
|
2024-10-19 03:34:48 +00:00
|
|
|
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
let difference = left
|
2024-09-23 12:57:49 +00:00
|
|
|
.subtract(right)
|
2024-09-12 04:39:31 +00:00
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
self.set(instruction.a(), difference, position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
|
|
|
Operation::Multiply => {
|
2024-10-19 03:34:48 +00:00
|
|
|
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
let product = left
|
2024-09-23 12:57:49 +00:00
|
|
|
.multiply(right)
|
2024-09-12 04:39:31 +00:00
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
self.set(instruction.a(), product, position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
|
|
|
Operation::Divide => {
|
2024-10-19 03:34:48 +00:00
|
|
|
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
let quotient = left
|
2024-09-23 12:57:49 +00:00
|
|
|
.divide(right)
|
2024-09-12 04:39:31 +00:00
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
self.set(instruction.a(), quotient, position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
2024-09-17 21:51:39 +00:00
|
|
|
Operation::Modulo => {
|
2024-10-19 03:34:48 +00:00
|
|
|
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
2024-09-17 21:51:39 +00:00
|
|
|
let remainder = left
|
2024-09-23 12:57:49 +00:00
|
|
|
.modulo(right)
|
2024-09-17 21:51:39 +00:00
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
self.set(instruction.a(), 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-09-23 23:16:15 +00:00
|
|
|
let value = self.get(register, position)?;
|
2024-10-12 08:50:30 +00:00
|
|
|
let boolean = if let Value::Primitive(Primitive::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-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!(
|
|
|
|
self.chunk.get_instruction(self.ip, position)?.0.operation(),
|
|
|
|
Operation::Jump
|
|
|
|
);
|
2024-09-24 14:16:19 +00:00
|
|
|
|
2024-10-19 03:34:48 +00:00
|
|
|
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
2024-10-12 08:50:30 +00:00
|
|
|
let equal_result = left
|
2024-09-23 12:57:49 +00:00
|
|
|
.equal(right)
|
2024-10-12 08:50:30 +00:00
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
|
|
|
let boolean =
|
|
|
|
if let Value::Primitive(Primitive::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-18 20:43:34 +00:00
|
|
|
|
2024-09-24 14:16:19 +00:00
|
|
|
if boolean == compare_to {
|
|
|
|
self.ip += 1;
|
|
|
|
} else {
|
2024-10-12 07:06:44 +00:00
|
|
|
let (jump, _) = *self.chunk.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;
|
2024-09-18 20:43:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Operation::Less => {
|
2024-10-12 07:06:44 +00:00
|
|
|
debug_assert_eq!(
|
|
|
|
self.chunk.get_instruction(self.ip, position)?.0.operation(),
|
|
|
|
Operation::Jump
|
|
|
|
);
|
2024-09-24 14:16:19 +00:00
|
|
|
|
2024-10-19 03:34:48 +00:00
|
|
|
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
2024-10-12 08:50:30 +00:00
|
|
|
let less_result = left
|
2024-09-24 15:40:12 +00:00
|
|
|
.less_than(right)
|
2024-10-12 08:50:30 +00:00
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
|
|
|
let boolean = if let Value::Primitive(Primitive::Boolean(boolean)) = less_result
|
|
|
|
{
|
|
|
|
boolean
|
|
|
|
} else {
|
|
|
|
return Err(VmError::ExpectedBoolean {
|
|
|
|
found: less_result.clone(),
|
2024-09-24 14:16:19 +00:00
|
|
|
position,
|
2024-10-12 08:50:30 +00:00
|
|
|
});
|
|
|
|
};
|
2024-09-24 20:49:17 +00:00
|
|
|
let compare_to = instruction.a_as_boolean();
|
2024-09-18 20:43:34 +00:00
|
|
|
|
2024-09-24 14:16:19 +00:00
|
|
|
if boolean == compare_to {
|
|
|
|
self.ip += 1;
|
|
|
|
} else {
|
2024-10-12 07:06:44 +00:00
|
|
|
let jump = self.chunk.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;
|
2024-09-18 20:43:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Operation::LessEqual => {
|
2024-10-12 07:06:44 +00:00
|
|
|
debug_assert_eq!(
|
|
|
|
self.chunk.get_instruction(self.ip, position)?.0.operation(),
|
|
|
|
Operation::Jump
|
|
|
|
);
|
2024-09-24 15:40:12 +00:00
|
|
|
|
2024-10-19 03:34:48 +00:00
|
|
|
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
2024-10-12 08:50:30 +00:00
|
|
|
let less_or_equal_result = left
|
2024-09-24 15:40:12 +00:00
|
|
|
.less_than_or_equal(right)
|
2024-10-12 08:50:30 +00:00
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
|
|
|
let boolean = if let Value::Primitive(Primitive::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-10-12 08:50:30 +00:00
|
|
|
});
|
|
|
|
};
|
2024-09-24 20:49:17 +00:00
|
|
|
let compare_to = instruction.a_as_boolean();
|
2024-09-18 20:43:34 +00:00
|
|
|
|
2024-09-24 14:16:19 +00:00
|
|
|
if boolean == compare_to {
|
|
|
|
self.ip += 1;
|
|
|
|
} else {
|
2024-10-12 07:06:44 +00:00
|
|
|
let jump = self.chunk.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;
|
2024-09-18 20:43:34 +00:00
|
|
|
}
|
|
|
|
}
|
2024-09-12 04:39:31 +00:00
|
|
|
Operation::Negate => {
|
2024-09-24 20:49:17 +00:00
|
|
|
let value = if instruction.b_is_constant() {
|
|
|
|
self.chunk.get_constant(instruction.b(), position)?
|
2024-09-18 01:10:44 +00:00
|
|
|
} else {
|
2024-09-24 20:49:17 +00:00
|
|
|
self.get(instruction.b(), position)?
|
2024-09-18 01:10:44 +00:00
|
|
|
};
|
2024-09-12 04:39:31 +00:00
|
|
|
let negated = value
|
|
|
|
.negate()
|
|
|
|
.map_err(|error| VmError::Value { error, position })?;
|
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
self.set(instruction.a(), negated, position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
2024-09-17 21:51:39 +00:00
|
|
|
Operation::Not => {
|
2024-09-24 20:49:17 +00:00
|
|
|
let value = if instruction.b_is_constant() {
|
|
|
|
self.chunk.get_constant(instruction.b(), position)?
|
2024-09-18 01:10:44 +00:00
|
|
|
} else {
|
2024-09-24 20:49:17 +00:00
|
|
|
self.get(instruction.b(), position)?
|
2024-09-18 01:10:44 +00:00
|
|
|
};
|
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-10-12 07:06:44 +00:00
|
|
|
self.set(instruction.a(), not, position)?;
|
2024-09-17 21:51:39 +00:00
|
|
|
}
|
2024-09-18 20:43:34 +00:00
|
|
|
Operation::Jump => {
|
2024-10-20 14:20:09 +00:00
|
|
|
let jump_to = instruction.b();
|
2024-09-18 20:43:34 +00:00
|
|
|
|
2024-10-20 14:20:09 +00:00
|
|
|
self.ip = jump_to as usize;
|
2024-09-18 20:43:34 +00:00
|
|
|
}
|
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(function_register, position)?.clone();
|
2024-10-13 06:33:58 +00:00
|
|
|
let function = if let Value::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
|
|
|
};
|
2024-10-13 00:19:21 +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
|
|
|
|
{
|
2024-10-20 04:06:22 +00:00
|
|
|
let argument = match self.get(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
|
|
|
|
2024-10-13 11:14:12 +00:00
|
|
|
function_vm.set(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 {
|
2024-10-19 21:24:22 +00:00
|
|
|
self.set(to_register, value, position)?;
|
2024-10-12 14:55:34 +00:00
|
|
|
}
|
|
|
|
}
|
2024-10-30 07:08:25 +00:00
|
|
|
Operation::CallNative => {
|
|
|
|
let to_register = instruction.a();
|
|
|
|
let native_function = NativeFunction::from(instruction.b());
|
|
|
|
let argument_count = instruction.c();
|
|
|
|
let return_value = match native_function {
|
|
|
|
NativeFunction::Panic => {
|
|
|
|
let message = if argument_count == 0 {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
let mut message = String::new();
|
|
|
|
|
|
|
|
for argument_index in 0..argument_count {
|
|
|
|
if argument_index != 0 {
|
|
|
|
message.push(' ');
|
|
|
|
}
|
|
|
|
|
|
|
|
let argument = self.get(argument_index, position)?;
|
|
|
|
|
|
|
|
message.push_str(&argument.to_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(message)
|
|
|
|
};
|
|
|
|
|
|
|
|
return Err(VmError::Panic { message, position });
|
|
|
|
}
|
|
|
|
NativeFunction::ToString => {
|
|
|
|
let mut string = String::new();
|
|
|
|
|
|
|
|
for argument_index in 0..argument_count {
|
|
|
|
let argument = self.get(argument_index, position)?;
|
|
|
|
|
|
|
|
string.push_str(&argument.to_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(Value::Primitive(Primitive::String(string)))
|
|
|
|
}
|
2024-10-30 09:16:34 +00:00
|
|
|
NativeFunction::Write => {
|
2024-10-30 09:31:46 +00:00
|
|
|
let to_register = instruction.a();
|
2024-10-30 09:16:34 +00:00
|
|
|
let mut stdout = stdout();
|
2024-10-30 09:31:46 +00:00
|
|
|
let map_err = |io_error: io::Error| VmError::Io {
|
|
|
|
error: io_error.kind(),
|
|
|
|
position,
|
|
|
|
};
|
2024-10-30 09:16:34 +00:00
|
|
|
|
2024-10-30 09:31:46 +00:00
|
|
|
let first_argument = to_register.saturating_sub(argument_count);
|
|
|
|
let last_argument = to_register.saturating_sub(1);
|
|
|
|
|
|
|
|
for argument_index in first_argument..=last_argument {
|
|
|
|
if argument_index != first_argument {
|
|
|
|
stdout.write(b" ").map_err(map_err)?;
|
2024-10-30 09:16:34 +00:00
|
|
|
}
|
|
|
|
|
2024-10-30 09:31:46 +00:00
|
|
|
let argument_string =
|
|
|
|
self.get(argument_index, position)?.to_string();
|
2024-10-30 09:16:34 +00:00
|
|
|
|
2024-10-30 09:31:46 +00:00
|
|
|
stdout
|
|
|
|
.write_all(argument_string.as_bytes())
|
|
|
|
.map_err(map_err)?;
|
2024-10-30 09:16:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
NativeFunction::WriteLine => {
|
|
|
|
let mut stdout = stdout();
|
2024-10-30 09:31:46 +00:00
|
|
|
let map_err = |io_error: io::Error| VmError::Io {
|
|
|
|
error: io_error.kind(),
|
|
|
|
position,
|
|
|
|
};
|
2024-10-30 09:16:34 +00:00
|
|
|
|
2024-10-30 09:31:46 +00:00
|
|
|
let first_argument = to_register.saturating_sub(argument_count);
|
|
|
|
let last_argument = to_register.saturating_sub(1);
|
|
|
|
|
|
|
|
for argument_index in first_argument..=last_argument {
|
2024-10-30 09:16:34 +00:00
|
|
|
if argument_index != 0 {
|
2024-10-30 09:31:46 +00:00
|
|
|
stdout.write(b" ").map_err(map_err)?;
|
2024-10-30 09:16:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let argument_string =
|
|
|
|
self.get(argument_index, position)?.to_string();
|
|
|
|
|
2024-10-30 09:31:46 +00:00
|
|
|
stdout
|
|
|
|
.write_all(argument_string.as_bytes())
|
|
|
|
.map_err(map_err)?;
|
2024-10-30 09:16:34 +00:00
|
|
|
}
|
|
|
|
|
2024-10-30 09:31:46 +00:00
|
|
|
stdout.write(b"\n").map_err(map_err)?;
|
2024-10-30 09:16:34 +00:00
|
|
|
|
|
|
|
None
|
|
|
|
}
|
2024-10-30 07:08:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(value) = return_value {
|
|
|
|
self.set(to_register, value, position)?;
|
|
|
|
}
|
|
|
|
}
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(register) = self.last_assigned_register {
|
|
|
|
let value = self.empty(register, position)?;
|
2024-10-12 07:06:44 +00:00
|
|
|
|
2024-10-30 03:11:55 +00:00
|
|
|
return Ok(Some(value));
|
2024-10-05 02:33:47 +00:00
|
|
|
} else {
|
2024-10-30 03:11:55 +00:00
|
|
|
return Err(VmError::StackUnderflow { position });
|
|
|
|
}
|
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-10-13 08:21:07 +00:00
|
|
|
fn set(&mut self, to_register: u8, value: Value, position: Span) -> Result<(), VmError> {
|
2024-10-12 07:06:44 +00:00
|
|
|
let length = self.stack.len();
|
2024-10-30 03:11:55 +00:00
|
|
|
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
|
|
|
|
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-10-19 07:06:14 +00:00
|
|
|
log::trace!("Change R{to_register} to {value}");
|
2024-09-12 13:11:49 +00:00
|
|
|
|
2024-10-13 08:21:07 +00:00
|
|
|
self.stack[to_register] = Register::Value(value);
|
2024-10-12 07:06:44 +00:00
|
|
|
|
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-09-12 04:39:31 +00:00
|
|
|
|
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(())
|
|
|
|
}
|
2024-10-21 02:18:58 +00:00
|
|
|
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();
|
2024-10-30 03:11:55 +00:00
|
|
|
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-12 07:06:44 +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(())
|
|
|
|
}
|
2024-10-21 02:18:58 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
fn set_constant(
|
|
|
|
&mut self,
|
2024-10-21 02:18:58 +00:00
|
|
|
to_register: u8,
|
2024-10-12 07:06:44 +00:00
|
|
|
constant_index: u8,
|
|
|
|
position: Span,
|
|
|
|
) -> Result<(), VmError> {
|
|
|
|
let length = self.stack.len();
|
2024-10-30 03:11:55 +00:00
|
|
|
self.last_assigned_register = Some(to_register);
|
2024-10-21 02:18:58 +00:00
|
|
|
let to_register = to_register as usize;
|
2024-09-23 12:57:49 +00:00
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
if length == Self::STACK_LIMIT {
|
|
|
|
return Err(VmError::StackOverflow { position });
|
|
|
|
}
|
|
|
|
|
2024-10-21 02:18:58 +00:00
|
|
|
match to_register.cmp(&length) {
|
|
|
|
Ordering::Less => {
|
|
|
|
log::trace!("Change R{to_register} to C{constant_index}");
|
2024-10-12 07:06:44 +00:00
|
|
|
|
2024-10-21 02:18:58 +00:00
|
|
|
self.stack[to_register] = Register::Constant(constant_index);
|
2024-09-23 12:57:49 +00:00
|
|
|
|
2024-10-21 02:18:58 +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");
|
2024-10-12 07:06:44 +00:00
|
|
|
|
2024-10-21 02:18:58 +00:00
|
|
|
self.stack.push(Register::Empty);
|
|
|
|
}
|
2024-10-12 07:06:44 +00:00
|
|
|
|
2024-10-21 02:18:58 +00:00
|
|
|
log::trace!("Set R{to_register} to C{constant_index}");
|
2024-10-12 07:06:44 +00:00
|
|
|
|
2024-10-21 02:18:58 +00:00
|
|
|
self.stack.push(Register::Constant(constant_index));
|
2024-10-12 07:06:44 +00:00
|
|
|
|
2024-10-21 02:18:58 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
2024-09-23 12:57:49 +00:00
|
|
|
}
|
|
|
|
|
2024-10-12 08:50:30 +00:00
|
|
|
pub fn get(&self, index: u8, position: Span) -> Result<&Value, VmError> {
|
2024-09-15 01:05:03 +00:00
|
|
|
let index = index as usize;
|
2024-10-12 07:06:44 +00:00
|
|
|
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
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
match register {
|
|
|
|
Register::Value(value) => Ok(value),
|
|
|
|
Register::Pointer(register_index) => {
|
|
|
|
let value = self.get(*register_index, position)?;
|
2024-09-14 19:06:13 +00:00
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
Ok(value)
|
|
|
|
}
|
|
|
|
Register::Constant(constant_index) => {
|
|
|
|
let value = self.chunk.get_constant(*constant_index, position)?;
|
2024-09-14 19:06:13 +00:00
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
Ok(value)
|
|
|
|
}
|
|
|
|
Register::Empty => Err(VmError::EmptyRegister { index, position }),
|
2024-09-13 01:14:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-19 07:06:14 +00:00
|
|
|
fn empty(mut self, index: u8, position: Span) -> Result<Value, VmError> {
|
2024-10-12 14:55:34 +00:00
|
|
|
let index = index as usize;
|
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
let register = replace(&mut self.stack[index], Register::Empty);
|
|
|
|
|
|
|
|
match register {
|
|
|
|
Register::Value(value) => Ok(value),
|
|
|
|
Register::Pointer(register_index) => {
|
2024-10-12 14:55:34 +00:00
|
|
|
let value = self.empty(register_index, position)?;
|
2024-10-12 07:06:44 +00:00
|
|
|
|
|
|
|
Ok(value)
|
|
|
|
}
|
|
|
|
Register::Constant(constant_index) => {
|
2024-10-19 07:06:14 +00:00
|
|
|
let value = self.chunk.take_constants().remove(constant_index as usize);
|
2024-09-12 04:39:31 +00:00
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
Ok(value)
|
|
|
|
}
|
|
|
|
Register::Empty => Err(VmError::EmptyRegister { index, position }),
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> {
|
2024-09-12 13:11:49 +00:00
|
|
|
let current = self.chunk.get_instruction(self.ip, position)?;
|
2024-09-12 04:39:31 +00:00
|
|
|
|
|
|
|
self.ip += 1;
|
|
|
|
|
|
|
|
Ok(current)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-12 07:06:44 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
|
|
enum Register {
|
|
|
|
Empty,
|
|
|
|
Value(Value),
|
|
|
|
Pointer(u8),
|
|
|
|
Constant(u8),
|
|
|
|
}
|
|
|
|
|
2024-09-12 04:39:31 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
|
|
pub enum VmError {
|
2024-10-05 04:11:03 +00:00
|
|
|
CannotMutateImmutableLocal {
|
|
|
|
identifier: Identifier,
|
|
|
|
position: Span,
|
|
|
|
},
|
2024-09-12 04:39:31 +00:00
|
|
|
EmptyRegister {
|
|
|
|
index: usize,
|
|
|
|
position: Span,
|
|
|
|
},
|
2024-09-19 15:41:18 +00:00
|
|
|
ExpectedBoolean {
|
|
|
|
found: Value,
|
|
|
|
position: Span,
|
|
|
|
},
|
2024-10-13 06:33:58 +00:00
|
|
|
ExpectedFunction {
|
|
|
|
found: Value,
|
|
|
|
position: Span,
|
|
|
|
},
|
2024-10-30 07:08:25 +00:00
|
|
|
Panic {
|
|
|
|
message: Option<String>,
|
|
|
|
position: Span,
|
|
|
|
},
|
2024-09-12 04:39:31 +00:00
|
|
|
RegisterIndexOutOfBounds {
|
2024-10-13 08:21:07 +00:00
|
|
|
index: usize,
|
2024-09-12 04:39:31 +00:00
|
|
|
position: Span,
|
|
|
|
},
|
|
|
|
InvalidInstruction {
|
|
|
|
instruction: Instruction,
|
|
|
|
position: Span,
|
|
|
|
},
|
|
|
|
StackOverflow {
|
|
|
|
position: Span,
|
|
|
|
},
|
|
|
|
StackUnderflow {
|
|
|
|
position: Span,
|
|
|
|
},
|
|
|
|
UndefinedVariable {
|
|
|
|
identifier: Identifier,
|
|
|
|
position: Span,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Wrappers for foreign errors
|
|
|
|
Chunk(ChunkError),
|
2024-10-30 09:16:34 +00:00
|
|
|
Io {
|
|
|
|
error: io::ErrorKind,
|
|
|
|
position: Span,
|
|
|
|
},
|
2024-09-12 04:39:31 +00:00
|
|
|
Value {
|
|
|
|
error: ValueError,
|
|
|
|
position: Span,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<ChunkError> for VmError {
|
2024-09-18 15:27:41 +00:00
|
|
|
fn from(error: ChunkError) -> Self {
|
|
|
|
Self::Chunk(error)
|
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-10-05 04:11:03 +00:00
|
|
|
Self::CannotMutateImmutableLocal { .. } => "Cannot mutate immutable variable",
|
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-10-30 07:08:25 +00:00
|
|
|
Self::Panic { .. } => "Explicit Panic",
|
2024-09-12 04:39:31 +00:00
|
|
|
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
|
|
|
Self::InvalidInstruction { .. } => "Invalid instruction",
|
|
|
|
Self::StackOverflow { .. } => "Stack overflow",
|
|
|
|
Self::StackUnderflow { .. } => "Stack underflow",
|
|
|
|
Self::UndefinedVariable { .. } => "Undefined variable",
|
2024-09-18 11:58:31 +00:00
|
|
|
Self::Chunk(error) => error.description(),
|
2024-10-30 09:16:34 +00:00
|
|
|
Self::Io { .. } => "I/O error",
|
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 {
|
|
|
|
Self::EmptyRegister { index, .. } => Some(format!("Register {index} is empty")),
|
2024-10-13 06:33:58 +00:00
|
|
|
Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")),
|
2024-10-30 07:08:25 +00:00
|
|
|
Self::Panic { message, .. } => message.clone(),
|
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-09-12 04:39:31 +00:00
|
|
|
Self::UndefinedVariable { identifier, .. } => {
|
|
|
|
Some(format!("{identifier} is not in scope"))
|
|
|
|
}
|
|
|
|
Self::Chunk(error) => error.details(),
|
2024-10-30 09:16:34 +00:00
|
|
|
Self::Io { error, .. } => Some(error.to_string()),
|
2024-09-12 04:39:31 +00:00
|
|
|
Self::Value { error, .. } => Some(error.to_string()),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn position(&self) -> Span {
|
|
|
|
match self {
|
2024-10-05 04:11:03 +00:00
|
|
|
Self::CannotMutateImmutableLocal { 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-10-30 07:08:25 +00:00
|
|
|
Self::Panic { position, .. } => *position,
|
2024-10-13 08:21:07 +00:00
|
|
|
Self::RegisterIndexOutOfBounds { position, .. } => *position,
|
2024-09-12 04:39:31 +00:00
|
|
|
Self::InvalidInstruction { position, .. } => *position,
|
|
|
|
Self::StackUnderflow { position } => *position,
|
|
|
|
Self::StackOverflow { position } => *position,
|
|
|
|
Self::UndefinedVariable { position, .. } => *position,
|
|
|
|
Self::Chunk(error) => error.position(),
|
2024-10-30 09:16:34 +00:00
|
|
|
Self::Io { position, .. } => *position,
|
2024-09-12 04:39:31 +00:00
|
|
|
Self::Value { position, .. } => *position,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|