1
0

Clean up chunk

This commit is contained in:
Jeff 2024-11-29 15:48:50 -05:00
parent dcb590fe07
commit 12ae935f50
7 changed files with 232 additions and 321 deletions

View File

@ -4,12 +4,11 @@
//! list of locals that can be executed by the Dust virtual machine. Chunks have a name when they //! list of locals that can be executed by the Dust virtual machine. Chunks have a name when they
//! belong to a named function. //! belong to a named function.
use std::fmt::{self, Debug, Display, Formatter, Write}; use std::fmt::{self, Debug, Display, Write};
use std::hash::{Hash, Hasher};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ConcreteValue, Disassembler, FunctionType, Instruction, Operation, Scope, Span, Type}; use crate::{ConcreteValue, Disassembler, FunctionType, Instruction, Scope, Span, Type};
/// In-memory representation of a Dust program or function. /// In-memory representation of a Dust program or function.
/// ///
@ -59,18 +58,10 @@ impl Chunk {
self.name.as_ref() self.name.as_ref()
} }
pub fn set_name(&mut self, name: String) {
self.name = Some(name);
}
pub fn r#type(&self) -> &FunctionType { pub fn r#type(&self) -> &FunctionType {
&self.r#type &self.r#type
} }
pub fn set_type(&mut self, r#type: FunctionType) {
self.r#type = r#type;
}
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.instructions.len() self.instructions.len()
} }
@ -83,113 +74,14 @@ impl Chunk {
&self.constants &self.constants
} }
pub fn constants_mut(&mut self) -> &mut Vec<ConcreteValue> {
&mut self.constants
}
pub fn get_constant(&self, index: u16) -> Result<&ConcreteValue, ChunkError> {
self.constants
.get(index as usize)
.ok_or(ChunkError::ConstantIndexOutOfBounds {
index: index as usize,
})
}
pub fn push_or_get_constant(&mut self, value: ConcreteValue) -> u16 {
if let Some(index) = self
.constants
.iter()
.position(|constant| *constant == value)
{
index as u16
} else {
let index = self.constants.len() as u16;
self.constants.push(value);
index
}
}
pub fn instructions(&self) -> &Vec<(Instruction, Type, Span)> { pub fn instructions(&self) -> &Vec<(Instruction, Type, Span)> {
&self.instructions &self.instructions
} }
pub fn instructions_mut(&mut self) -> &mut Vec<(Instruction, Type, Span)> {
&mut self.instructions
}
pub fn get_instruction(&self, index: usize) -> Result<&(Instruction, Type, Span), ChunkError> {
self.instructions
.get(index)
.ok_or(ChunkError::InstructionIndexOutOfBounds { index })
}
pub fn get_last_operations<const COUNT: usize>(&self) -> Option<[Operation; COUNT]> {
let mut n_operations = [Operation::Return; COUNT];
for (nth, operation) in n_operations.iter_mut().rev().zip(
self.instructions
.iter()
.rev()
.map(|(instruction, _, _)| instruction.operation()),
) {
*nth = operation;
}
Some(n_operations)
}
pub fn locals(&self) -> &Vec<Local> { pub fn locals(&self) -> &Vec<Local> {
&self.locals &self.locals
} }
pub fn locals_mut(&mut self) -> &mut Vec<Local> {
&mut self.locals
}
pub fn get_local(&self, index: u16) -> Result<&Local, ChunkError> {
self.locals
.get(index as usize)
.ok_or(ChunkError::LocalIndexOutOfBounds {
index: index as usize,
})
}
pub fn get_local_mut(&mut self, index: u16) -> Result<&mut Local, ChunkError> {
self.locals
.get_mut(index as usize)
.ok_or(ChunkError::LocalIndexOutOfBounds {
index: index as usize,
})
}
pub fn get_identifier(&self, local_index: u16) -> Option<String> {
self.locals.get(local_index as usize).and_then(|local| {
self.constants
.get(local.identifier_index as usize)
.map(|value| value.to_string())
})
}
pub fn get_constant_type(&self, constant_index: u16) -> Result<Type, ChunkError> {
self.constants
.get(constant_index as usize)
.map(|value| value.r#type())
.ok_or(ChunkError::ConstantIndexOutOfBounds {
index: constant_index as usize,
})
}
pub fn get_local_type(&self, local_index: u16) -> Result<&Type, ChunkError> {
self.locals
.get(local_index as usize)
.map(|local| &local.r#type)
.ok_or(ChunkError::LocalIndexOutOfBounds {
index: local_index as usize,
})
}
pub fn disassembler(&self) -> Disassembler { pub fn disassembler(&self) -> Disassembler {
Disassembler::new(self) Disassembler::new(self)
} }
@ -252,37 +144,3 @@ impl Local {
} }
} }
} }
impl Hash for Local {
fn hash<H: Hasher>(&self, state: &mut H) {
self.identifier_index.hash(state);
}
}
/// Errors that can occur when using a [`Chunk`].
#[derive(Clone, Debug, PartialEq)]
pub enum ChunkError {
ConstantIndexOutOfBounds { index: usize },
FunctionIndexOutOfBounds { index: usize },
InstructionIndexOutOfBounds { index: usize },
LocalIndexOutOfBounds { index: usize },
}
impl Display for ChunkError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
ChunkError::ConstantIndexOutOfBounds { index } => {
write!(f, "Constant index {} out of bounds", index)
}
ChunkError::FunctionIndexOutOfBounds { index } => {
write!(f, "Function index {} out of bounds", index)
}
ChunkError::InstructionIndexOutOfBounds { index } => {
write!(f, "Instruction index {} out of bounds", index)
}
ChunkError::LocalIndexOutOfBounds { index } => {
write!(f, "Local index {} out of bounds", index)
}
}
}
}

View File

@ -19,9 +19,9 @@ use crate::{
}, },
optimize_control_flow, optimize_set_local, optimize_control_flow, optimize_set_local,
value::ConcreteValue, value::ConcreteValue,
AnnotatedError, Argument, Chunk, ChunkError, Destination, DustError, FunctionType, Instruction, AnnotatedError, Argument, Chunk, Destination, DustError, FunctionType, Instruction, LexError,
LexError, Lexer, Local, NativeFunction, Operation, Scope, Span, Token, TokenKind, TokenOwned, Lexer, Local, NativeFunction, Operation, Scope, Span, Token, TokenKind, TokenOwned, Type,
Type, TypeConflict, TypeConflict,
}; };
/// Compiles the input and returns a chunk. /// Compiles the input and returns a chunk.
@ -44,15 +44,19 @@ pub fn compile(source: &str) -> Result<Chunk, DustError> {
.parse_top_level() .parse_top_level()
.map_err(|error| DustError::Compile { error, source })?; .map_err(|error| DustError::Compile { error, source })?;
Ok(compiler.finish()) Ok(compiler.finish(None, None))
} }
/// Low-level tool for compiling the input a token at a time while assembling a chunk. /// Low-level tool for compiling the input a token at a time while assembling a chunk.
/// ///
/// See the [`compile`] function an example of how to create and use a Compiler. /// See the [`compile`] function an example of how to create and use a Compiler.
#[derive(Debug, Eq, PartialEq)] #[derive(Debug)]
pub struct Compiler<'src> { pub struct Compiler<'src> {
chunk: Chunk, self_name: Option<String>,
instructions: Vec<(Instruction, Type, Span)>,
constants: Vec<ConcreteValue>,
locals: Vec<Local>,
lexer: Lexer<'src>, lexer: Lexer<'src>,
current_token: Token<'src>, current_token: Token<'src>,
@ -69,7 +73,6 @@ pub struct Compiler<'src> {
impl<'src> Compiler<'src> { impl<'src> Compiler<'src> {
pub fn new(mut lexer: Lexer<'src>) -> Result<Self, CompileError> { pub fn new(mut lexer: Lexer<'src>) -> Result<Self, CompileError> {
let (current_token, current_position) = lexer.next_token()?; let (current_token, current_position) = lexer.next_token()?;
let chunk = Chunk::new(None);
log::info!( log::info!(
"Begin chunk with {} at {}", "Begin chunk with {} at {}",
@ -78,7 +81,10 @@ impl<'src> Compiler<'src> {
); );
Ok(Compiler { Ok(Compiler {
chunk, self_name: None,
instructions: Vec::new(),
constants: Vec::new(),
locals: Vec::new(),
lexer, lexer,
current_token, current_token,
current_position, current_position,
@ -91,20 +97,26 @@ impl<'src> Compiler<'src> {
}) })
} }
pub fn finish(mut self) -> Chunk { pub fn finish(
self,
type_parameters: Option<Vec<u16>>,
value_parameters: Option<Vec<(u16, Type)>>,
) -> Chunk {
log::info!("End chunk"); log::info!("End chunk");
if let Type::None = *self.chunk.r#type().return_type { let r#type = FunctionType {
self.chunk.set_type(FunctionType { type_parameters,
type_parameters: None, value_parameters,
value_parameters: None, return_type: Box::new(self.return_type.unwrap_or(Type::None)),
return_type: self };
.return_type
.map_or_else(|| Box::new(Type::None), Box::new),
});
}
self.chunk Chunk::with_data(
self.self_name,
r#type,
self.instructions,
self.constants,
self.locals,
)
} }
fn is_eof(&self) -> bool { fn is_eof(&self) -> bool {
@ -112,8 +124,7 @@ impl<'src> Compiler<'src> {
} }
fn next_register(&self) -> u16 { fn next_register(&self) -> u16 {
self.chunk self.instructions
.instructions()
.iter() .iter()
.rev() .rev()
.find_map(|(instruction, _, _)| { .find_map(|(instruction, _, _)| {
@ -146,25 +157,21 @@ impl<'src> Compiler<'src> {
} }
fn get_local(&self, index: u16) -> Result<&Local, CompileError> { fn get_local(&self, index: u16) -> Result<&Local, CompileError> {
self.chunk self.locals
.get_local(index) .get(index as usize)
.map_err(|error| CompileError::Chunk { .ok_or(CompileError::UndeclaredVariable {
error, identifier: format!("#{}", index),
position: self.current_position, position: self.current_position,
}) })
} }
fn get_local_index(&self, identifier_text: &str) -> Result<u16, CompileError> { fn get_local_index(&self, identifier_text: &str) -> Result<u16, CompileError> {
self.chunk self.locals
.locals()
.iter() .iter()
.enumerate() .enumerate()
.rev() .rev()
.find_map(|(index, local)| { .find_map(|(index, local)| {
let constant = self let constant = self.constants.get(local.identifier_index as usize)?;
.chunk
.constants()
.get(local.identifier_index as usize)?;
let identifier = if let ConcreteValue::String(identifier) = constant { let identifier = if let ConcreteValue::String(identifier) = constant {
identifier identifier
} else { } else {
@ -193,16 +200,39 @@ impl<'src> Compiler<'src> {
log::info!("Declaring local {identifier}"); log::info!("Declaring local {identifier}");
let identifier = ConcreteValue::string(identifier); let identifier = ConcreteValue::string(identifier);
let identifier_index = self.chunk.push_or_get_constant(identifier); let identifier_index = self.push_or_get_constant(identifier);
let local_index = self.chunk.locals().len() as u16; let local_index = self.locals.len() as u16;
self.chunk self.locals
.locals_mut()
.push(Local::new(identifier_index, r#type, is_mutable, scope)); .push(Local::new(identifier_index, r#type, is_mutable, scope));
(local_index, identifier_index) (local_index, identifier_index)
} }
fn get_identifier(&self, local_index: u16) -> Option<String> {
self.locals.get(local_index as usize).and_then(|local| {
self.constants
.get(local.identifier_index as usize)
.map(|value| value.to_string())
})
}
fn push_or_get_constant(&mut self, value: ConcreteValue) -> u16 {
if let Some(index) = self
.constants
.iter()
.position(|constant| constant == &value)
{
index as u16
} else {
let index = self.constants.len() as u16;
self.constants.push(value);
index
}
}
fn allow(&mut self, allowed: Token) -> Result<bool, CompileError> { fn allow(&mut self, allowed: Token) -> Result<bool, CompileError> {
if self.current_token == allowed { if self.current_token == allowed {
self.advance()?; self.advance()?;
@ -226,8 +256,7 @@ impl<'src> Compiler<'src> {
} }
fn pop_last_instruction(&mut self) -> Result<(Instruction, Type, Span), CompileError> { fn pop_last_instruction(&mut self) -> Result<(Instruction, Type, Span), CompileError> {
self.chunk self.instructions
.instructions_mut()
.pop() .pop()
.ok_or_else(|| CompileError::ExpectedExpression { .ok_or_else(|| CompileError::ExpectedExpression {
found: self.previous_token.to_owned(), found: self.previous_token.to_owned(),
@ -235,13 +264,27 @@ impl<'src> Compiler<'src> {
}) })
} }
fn get_last_operations<const COUNT: usize>(&self) -> Option<[Operation; COUNT]> {
let mut n_operations = [Operation::Return; COUNT];
for (nth, operation) in n_operations.iter_mut().rev().zip(
self.instructions
.iter()
.rev()
.map(|(instruction, _, _)| instruction.operation()),
) {
*nth = operation;
}
Some(n_operations)
}
fn get_last_jumpable_mut_between( fn get_last_jumpable_mut_between(
&mut self, &mut self,
minimum: usize, minimum: usize,
maximum: usize, maximum: usize,
) -> Option<&mut Instruction> { ) -> Option<&mut Instruction> {
self.chunk self.instructions
.instructions_mut()
.iter_mut() .iter_mut()
.rev() .rev()
.skip(minimum) .skip(minimum)
@ -256,32 +299,14 @@ impl<'src> Compiler<'src> {
} }
fn get_last_instruction_type(&self) -> Type { fn get_last_instruction_type(&self) -> Type {
self.chunk self.instructions
.instructions()
.last() .last()
.map(|(_, r#type, _)| r#type.clone()) .map(|(_, r#type, _)| r#type.clone())
.unwrap_or(Type::None) .unwrap_or(Type::None)
} }
pub fn get_argument_type(&self, argument: &Argument) -> Result<Type, CompileError> { fn get_register_type(&self, register_index: u16) -> Result<Type, CompileError> {
let r#type = match argument { for (instruction, r#type, _) in &self.instructions {
Argument::Register(register_index) => self.get_register_type(*register_index)?,
Argument::Constant(constant_index) => self
.chunk
.get_constant(*constant_index)
.map_err(|error| CompileError::Chunk {
error,
position: self.current_position,
})?
.r#type(),
Argument::Local(local_index) => self.get_local(*local_index)?.r#type.clone(),
};
Ok(r#type)
}
pub fn get_register_type(&self, register_index: u16) -> Result<Type, CompileError> {
for (instruction, r#type, _) in self.chunk.instructions() {
if instruction.a() == register_index { if instruction.a() == register_index {
if let Operation::LoadList = instruction.operation() { if let Operation::LoadList = instruction.operation() {
let LoadList { start_register, .. } = LoadList::from(instruction); let LoadList { start_register, .. } = LoadList::from(instruction);
@ -336,9 +361,7 @@ impl<'src> Compiler<'src> {
position.to_string() position.to_string()
); );
self.chunk self.instructions.push((instruction, r#type, position));
.instructions_mut()
.push((instruction, r#type, position));
} }
fn emit_constant( fn emit_constant(
@ -347,7 +370,7 @@ impl<'src> Compiler<'src> {
position: Span, position: Span,
) -> Result<(), CompileError> { ) -> Result<(), CompileError> {
let r#type = constant.r#type(); let r#type = constant.r#type();
let constant_index = self.chunk.push_or_get_constant(constant); let constant_index = self.push_or_get_constant(constant);
let destination = Destination::Register(self.next_register()); let destination = Destination::Register(self.next_register());
let instruction = Instruction::from(LoadConstant { let instruction = Instruction::from(LoadConstant {
destination, destination,
@ -520,7 +543,7 @@ impl<'src> Compiler<'src> {
let (argument, push_back) = self.handle_binary_argument(&previous_instruction)?; let (argument, push_back) = self.handle_binary_argument(&previous_instruction)?;
if push_back { if push_back {
self.chunk.instructions_mut().push(( self.instructions.push((
previous_instruction, previous_instruction,
previous_type.clone(), previous_type.clone(),
previous_position, previous_position,
@ -568,11 +591,11 @@ impl<'src> Compiler<'src> {
fn parse_math_binary(&mut self) -> Result<(), CompileError> { fn parse_math_binary(&mut self) -> Result<(), CompileError> {
let (left_instruction, left_type, left_position) = let (left_instruction, left_type, left_position) =
self.chunk.instructions_mut().pop().ok_or_else(|| { self.instructions
CompileError::ExpectedExpression { .pop()
.ok_or_else(|| CompileError::ExpectedExpression {
found: self.previous_token.to_owned(), found: self.previous_token.to_owned(),
position: self.previous_position, position: self.previous_position,
}
})?; })?;
let (left, push_back_left) = self.handle_binary_argument(&left_instruction)?; let (left, push_back_left) = self.handle_binary_argument(&left_instruction)?;
let left_is_mutable_local = if let Argument::Local(local_index) = left { let left_is_mutable_local = if let Argument::Local(local_index) = left {
@ -601,8 +624,7 @@ impl<'src> Compiler<'src> {
} }
if push_back_left { if push_back_left {
self.chunk self.instructions
.instructions_mut()
.push((left_instruction, left_type, left_position)); .push((left_instruction, left_type, left_position));
} }
@ -613,8 +635,7 @@ impl<'src> Compiler<'src> {
let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?; let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?;
if push_back_right { if push_back_right {
self.chunk self.instructions
.instructions_mut()
.push((right_instruction, right_type, right_position)); .push((right_instruction, right_type, right_position));
} }
@ -660,7 +681,7 @@ impl<'src> Compiler<'src> {
fn parse_comparison_binary(&mut self) -> Result<(), CompileError> { fn parse_comparison_binary(&mut self) -> Result<(), CompileError> {
if let Some([Operation::Equal | Operation::Less | Operation::LessEqual, _, _]) = if let Some([Operation::Equal | Operation::Less | Operation::LessEqual, _, _]) =
self.chunk.get_last_operations() self.get_last_operations()
{ {
return Err(CompileError::CannotChainComparison { return Err(CompileError::CannotChainComparison {
position: self.current_position, position: self.current_position,
@ -668,11 +689,11 @@ impl<'src> Compiler<'src> {
} }
let (left_instruction, left_type, left_position) = let (left_instruction, left_type, left_position) =
self.chunk.instructions_mut().pop().ok_or_else(|| { self.instructions
CompileError::ExpectedExpression { .pop()
.ok_or_else(|| CompileError::ExpectedExpression {
found: self.previous_token.to_owned(), found: self.previous_token.to_owned(),
position: self.previous_position, position: self.previous_position,
}
})?; })?;
let (left, push_back_left) = self.handle_binary_argument(&left_instruction)?; let (left, push_back_left) = self.handle_binary_argument(&left_instruction)?;
let operator = self.current_token; let operator = self.current_token;
@ -680,8 +701,7 @@ impl<'src> Compiler<'src> {
let rule = ParseRule::from(&operator); let rule = ParseRule::from(&operator);
if push_back_left { if push_back_left {
self.chunk self.instructions
.instructions_mut()
.push((left_instruction, left_type, left_position)); .push((left_instruction, left_type, left_position));
} }
@ -689,17 +709,16 @@ impl<'src> Compiler<'src> {
self.parse_sub_expression(&rule.precedence)?; self.parse_sub_expression(&rule.precedence)?;
let (right_instruction, right_type, right_position) = let (right_instruction, right_type, right_position) =
self.chunk.instructions_mut().pop().ok_or_else(|| { self.instructions
CompileError::ExpectedExpression { .pop()
.ok_or_else(|| CompileError::ExpectedExpression {
found: self.previous_token.to_owned(), found: self.previous_token.to_owned(),
position: self.previous_position, position: self.previous_position,
}
})?; })?;
let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?; let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?;
if push_back_right { if push_back_right {
self.chunk self.instructions
.instructions_mut()
.push((right_instruction, right_type, right_position)); .push((right_instruction, right_type, right_position));
} }
@ -762,11 +781,8 @@ impl<'src> Compiler<'src> {
let (argument, push_back) = self.handle_binary_argument(&left_instruction)?; let (argument, push_back) = self.handle_binary_argument(&left_instruction)?;
if push_back { if push_back {
self.chunk.instructions_mut().push(( self.instructions
left_instruction, .push((left_instruction, left_type.clone(), left_position));
left_type.clone(),
left_position,
));
} }
let operator = self.current_token; let operator = self.current_token;
@ -817,7 +833,7 @@ impl<'src> Compiler<'src> {
local_index local_index
} else if let Some(native_function) = NativeFunction::from_str(identifier) { } else if let Some(native_function) = NativeFunction::from_str(identifier) {
return self.parse_native_call(native_function); return self.parse_native_call(native_function);
} else if Some(identifier) == self.chunk.name().map(|string| string.as_str()) { } else if self.self_name.as_deref() == Some(identifier) {
let destination = Destination::Register(self.next_register()); let destination = Destination::Register(self.next_register());
let load_self = Instruction::from(LoadSelf { destination }); let load_self = Instruction::from(LoadSelf { destination });
@ -836,7 +852,7 @@ impl<'src> Compiler<'src> {
if !self.current_scope.contains(&local.scope) { if !self.current_scope.contains(&local.scope) {
return Err(CompileError::VariableOutOfScope { return Err(CompileError::VariableOutOfScope {
identifier: self.chunk.get_identifier(local_index).unwrap(), identifier: self.get_identifier(local_index).unwrap(),
position: start_position, position: start_position,
variable_scope: local.scope, variable_scope: local.scope,
access_scope: self.current_scope, access_scope: self.current_scope,
@ -846,7 +862,7 @@ impl<'src> Compiler<'src> {
if self.allow(Token::Equal)? { if self.allow(Token::Equal)? {
if !is_mutable { if !is_mutable {
return Err(CompileError::CannotMutateImmutableVariable { return Err(CompileError::CannotMutateImmutableVariable {
identifier: self.chunk.get_identifier(local_index).unwrap(), identifier: self.get_identifier(local_index).unwrap(),
position: start_position, position: start_position,
}); });
} }
@ -860,7 +876,7 @@ impl<'src> Compiler<'src> {
}); });
self.emit_instruction(set_local, Type::None, start_position); self.emit_instruction(set_local, Type::None, start_position);
optimize_set_local(&mut self.chunk); optimize_set_local(&mut self.instructions);
return Ok(()); return Ok(());
} }
@ -961,7 +977,7 @@ impl<'src> Compiler<'src> {
self.parse_expression()?; self.parse_expression()?;
if matches!( if matches!(
self.chunk.get_last_operations(), self.get_last_operations(),
Some([ Some([
Operation::Equal | Operation::Less | Operation::LessEqual, Operation::Equal | Operation::Less | Operation::LessEqual,
Operation::Jump, Operation::Jump,
@ -969,10 +985,10 @@ impl<'src> Compiler<'src> {
Operation::LoadBoolean, Operation::LoadBoolean,
]) ])
) { ) {
self.chunk.instructions_mut().pop(); self.instructions.pop();
self.chunk.instructions_mut().pop(); self.instructions.pop();
self.chunk.instructions_mut().pop(); self.instructions.pop();
} else if let Some((instruction, _, _)) = self.chunk.instructions().last() { } else if let Some((instruction, _, _)) = self.instructions.last() {
let test_register = instruction.a(); let test_register = instruction.a();
let test = Instruction::from(Test { let test = Instruction::from(Test {
argument: Argument::Register(test_register), argument: Argument::Register(test_register),
@ -982,7 +998,7 @@ impl<'src> Compiler<'src> {
self.emit_instruction(test, Type::None, self.current_position) self.emit_instruction(test, Type::None, self.current_position)
} }
let if_block_start = self.chunk.len(); let if_block_start = self.instructions.len();
let if_block_start_position = self.current_position; let if_block_start_position = self.current_position;
if let Token::LeftBrace = self.current_token { if let Token::LeftBrace = self.current_token {
@ -995,7 +1011,7 @@ impl<'src> Compiler<'src> {
}); });
} }
let if_block_end = self.chunk.len(); let if_block_end = self.instructions.len();
let mut if_block_distance = (if_block_end - if_block_start) as u16; let mut if_block_distance = (if_block_end - if_block_start) as u16;
let if_block_type = self.get_last_instruction_type(); let if_block_type = self.get_last_instruction_type();
let if_last_register = self.next_register().saturating_sub(1); let if_last_register = self.next_register().saturating_sub(1);
@ -1022,7 +1038,7 @@ impl<'src> Compiler<'src> {
false false
}; };
let else_block_end = self.chunk.len(); let else_block_end = self.instructions.len();
let else_block_distance = (else_block_end - if_block_end) as u16; let else_block_distance = (else_block_end - if_block_end) as u16;
let else_block_type = self.get_last_instruction_type(); let else_block_type = self.get_last_instruction_type();
@ -1047,8 +1063,7 @@ impl<'src> Compiler<'src> {
is_positive: true, is_positive: true,
}); });
self.chunk self.instructions
.instructions_mut()
.insert(if_block_end, (jump, Type::None, self.current_position)); .insert(if_block_end, (jump, Type::None, self.current_position));
} }
} }
@ -1059,8 +1074,7 @@ impl<'src> Compiler<'src> {
is_positive: true, is_positive: true,
}); });
self.chunk self.instructions
.instructions_mut()
.insert(if_block_end, (jump, Type::None, self.current_position)); .insert(if_block_end, (jump, Type::None, self.current_position));
} }
} }
@ -1070,12 +1084,11 @@ impl<'src> Compiler<'src> {
is_positive: true, is_positive: true,
}); });
self.chunk self.instructions
.instructions_mut()
.insert(if_block_start, (jump, Type::None, if_block_start_position)); .insert(if_block_start, (jump, Type::None, if_block_start_position));
if self.chunk.len() >= 4 { if self.instructions.len() >= 4 {
optimize_control_flow(&mut self.chunk); optimize_control_flow(&mut self.instructions);
} }
let else_last_register = self.next_register().saturating_sub(1); let else_last_register = self.next_register().saturating_sub(1);
@ -1094,12 +1107,12 @@ impl<'src> Compiler<'src> {
fn parse_while(&mut self) -> Result<(), CompileError> { fn parse_while(&mut self) -> Result<(), CompileError> {
self.advance()?; self.advance()?;
let expression_start = self.chunk.len() as u16; let expression_start = self.instructions.len() as u16;
self.parse_expression()?; self.parse_expression()?;
if matches!( if matches!(
self.chunk.get_last_operations(), self.get_last_operations(),
Some([ Some([
Operation::Equal | Operation::Less | Operation::LessEqual, Operation::Equal | Operation::Less | Operation::LessEqual,
Operation::Jump, Operation::Jump,
@ -1107,24 +1120,23 @@ impl<'src> Compiler<'src> {
Operation::LoadBoolean, Operation::LoadBoolean,
],) ],)
) { ) {
self.chunk.instructions_mut().pop(); self.instructions.pop();
self.chunk.instructions_mut().pop(); self.instructions.pop();
self.chunk.instructions_mut().pop(); self.instructions.pop();
} }
let block_start = self.chunk.len(); let block_start = self.instructions.len();
self.parse_block()?; self.parse_block()?;
let block_end = self.chunk.len() as u16; let block_end = self.instructions.len() as u16;
let jump_distance = block_end - block_start as u16 + 1; let jump_distance = block_end - block_start as u16 + 1;
let jump = Instruction::from(Jump { let jump = Instruction::from(Jump {
offset: jump_distance, offset: jump_distance,
is_positive: true, is_positive: true,
}); });
self.chunk self.instructions
.instructions_mut()
.insert(block_start, (jump, Type::None, self.current_position)); .insert(block_start, (jump, Type::None, self.current_position));
let jump_back_distance = block_end - expression_start + 1; let jump_back_distance = block_end - expression_start + 1;
@ -1203,7 +1215,7 @@ impl<'src> Compiler<'src> {
let expression_type = self.get_last_instruction_type(); let expression_type = self.get_last_instruction_type();
if expression_type == Type::None || self.chunk.is_empty() { if expression_type == Type::None || self.instructions.is_empty() {
return Err(CompileError::ExpectedExpression { return Err(CompileError::ExpectedExpression {
found: self.previous_token.to_owned(), found: self.previous_token.to_owned(),
position: self.current_position, position: self.current_position,
@ -1322,7 +1334,8 @@ impl<'src> Compiler<'src> {
let position = function_compiler.current_position; let position = function_compiler.current_position;
function_compiler.advance()?; function_compiler.advance()?;
function_compiler.chunk.set_name(text.to_string());
function_compiler.self_name = Some(text.to_string());
Some((text, position)) Some((text, position))
} else { } else {
@ -1388,13 +1401,8 @@ impl<'src> Compiler<'src> {
} else { } else {
Box::new(Type::None) Box::new(Type::None)
}; };
let function_type = FunctionType {
type_parameters: None,
value_parameters,
return_type,
};
function_compiler.chunk.set_type(function_type.clone()); function_compiler.return_type = Some((*return_type).clone());
function_compiler.expect(Token::LeftBrace)?; function_compiler.expect(Token::LeftBrace)?;
function_compiler.parse_top_level()?; function_compiler.parse_top_level()?;
@ -1404,10 +1412,16 @@ impl<'src> Compiler<'src> {
self.current_token = function_compiler.current_token; self.current_token = function_compiler.current_token;
self.current_position = function_compiler.current_position; self.current_position = function_compiler.current_position;
let function = ConcreteValue::Function(function_compiler.finish()); let function =
let constant_index = self.chunk.push_or_get_constant(function); ConcreteValue::Function(function_compiler.finish(None, value_parameters.clone()));
let constant_index = self.push_or_get_constant(function);
let function_end = self.current_position.1; let function_end = self.current_position.1;
let register = self.next_register(); let register = self.next_register();
let function_type = FunctionType {
type_parameters: None,
value_parameters,
return_type,
};
self.lexer.skip_to(function_end); self.lexer.skip_to(function_end);
@ -1454,8 +1468,7 @@ impl<'src> Compiler<'src> {
fn parse_call(&mut self) -> Result<(), CompileError> { fn parse_call(&mut self) -> Result<(), CompileError> {
let (last_instruction, _, _) = let (last_instruction, _, _) =
self.chunk self.instructions
.instructions()
.last() .last()
.ok_or_else(|| CompileError::ExpectedExpression { .ok_or_else(|| CompileError::ExpectedExpression {
found: self.previous_token.to_owned(), found: self.previous_token.to_owned(),
@ -1478,7 +1491,7 @@ impl<'src> Compiler<'src> {
let register_type = self.get_register_type(function.index())?; let register_type = self.get_register_type(function.index())?;
let function_return_type = match register_type { let function_return_type = match register_type {
Type::Function(function_type) => *function_type.return_type, Type::Function(function_type) => *function_type.return_type,
Type::SelfChunk => (*self.chunk.r#type().return_type).clone(), Type::SelfChunk => self.return_type.clone().unwrap_or(Type::None),
_ => { _ => {
return Err(CompileError::ExpectedFunction { return Err(CompileError::ExpectedFunction {
found: self.previous_token.to_owned(), found: self.previous_token.to_owned(),
@ -1977,11 +1990,21 @@ pub enum CompileError {
position: Span, position: Span,
}, },
// Wrappers around foreign errors // Chunk errors
Chunk { ConstantIndexOutOfBounds {
error: ChunkError, index: usize,
position: Span, position: Span,
}, },
InstructionIndexOutOfBounds {
index: usize,
position: Span,
},
LocalIndexOutOfBounds {
index: usize,
position: Span,
},
// Wrappers around foreign errors
Lex(LexError), Lex(LexError),
ParseFloatError { ParseFloatError {
error: ParseFloatError, error: ParseFloatError,
@ -2004,7 +2027,7 @@ impl AnnotatedError for CompileError {
Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable", Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable",
Self::CannotResolveRegisterType { .. } => "Cannot resolve register type", Self::CannotResolveRegisterType { .. } => "Cannot resolve register type",
Self::CannotResolveVariableType { .. } => "Cannot resolve type", Self::CannotResolveVariableType { .. } => "Cannot resolve type",
Self::Chunk { .. } => "Chunk error", Self::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
Self::ExpectedExpression { .. } => "Expected an expression", Self::ExpectedExpression { .. } => "Expected an expression",
Self::ExpectedFunction { .. } => "Expected a function", Self::ExpectedFunction { .. } => "Expected a function",
Self::ExpectedFunctionType { .. } => "Expected a function type", Self::ExpectedFunctionType { .. } => "Expected a function type",
@ -2013,9 +2036,11 @@ impl AnnotatedError for CompileError {
Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens", Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens",
Self::IfElseBranchMismatch { .. } => "Type mismatch in if/else branches", Self::IfElseBranchMismatch { .. } => "Type mismatch in if/else branches",
Self::IfMissingElse { .. } => "If statement missing else branch", Self::IfMissingElse { .. } => "If statement missing else branch",
Self::InstructionIndexOutOfBounds { .. } => "Instruction index out of bounds",
Self::InvalidAssignmentTarget { .. } => "Invalid assignment target", Self::InvalidAssignmentTarget { .. } => "Invalid assignment target",
Self::Lex(error) => error.description(), Self::Lex(error) => error.description(),
Self::ListItemTypeConflict { .. } => "List item type conflict", Self::ListItemTypeConflict { .. } => "List item type conflict",
Self::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
Self::ParseFloatError { .. } => "Failed to parse float", Self::ParseFloatError { .. } => "Failed to parse float",
Self::ParseIntError { .. } => "Failed to parse integer", Self::ParseIntError { .. } => "Failed to parse integer",
Self::ReturnTypeConflict { .. } => "Return type conflict", Self::ReturnTypeConflict { .. } => "Return type conflict",
@ -2030,7 +2055,6 @@ impl AnnotatedError for CompileError {
Self::CannotMutateImmutableVariable { identifier, .. } => { Self::CannotMutateImmutableVariable { identifier, .. } => {
Some(format!("{identifier} is immutable")) Some(format!("{identifier} is immutable"))
} }
Self::Chunk { error, .. } => Some(error.to_string()),
Self::ExpectedExpression { found, .. } => Some(format!("Found {found}")), Self::ExpectedExpression { found, .. } => Some(format!("Found {found}")),
Self::ExpectedFunction { found, actual_type, .. } => { Self::ExpectedFunction { found, actual_type, .. } => {
Some(format!("Expected \"{found}\" to be a function but it has type {actual_type}")) Some(format!("Expected \"{found}\" to be a function but it has type {actual_type}"))
@ -2102,7 +2126,7 @@ impl AnnotatedError for CompileError {
Self::CannotMutateImmutableVariable { position, .. } => *position, Self::CannotMutateImmutableVariable { position, .. } => *position,
Self::CannotResolveRegisterType { position, .. } => *position, Self::CannotResolveRegisterType { position, .. } => *position,
Self::CannotResolveVariableType { position, .. } => *position, Self::CannotResolveVariableType { position, .. } => *position,
Self::Chunk { position, .. } => *position, Self::ConstantIndexOutOfBounds { position, .. } => *position,
Self::ExpectedExpression { position, .. } => *position, Self::ExpectedExpression { position, .. } => *position,
Self::ExpectedFunction { position, .. } => *position, Self::ExpectedFunction { position, .. } => *position,
Self::ExpectedFunctionType { position, .. } => *position, Self::ExpectedFunctionType { position, .. } => *position,
@ -2111,9 +2135,11 @@ impl AnnotatedError for CompileError {
Self::ExpectedTokenMultiple { position, .. } => *position, Self::ExpectedTokenMultiple { position, .. } => *position,
Self::IfElseBranchMismatch { position, .. } => *position, Self::IfElseBranchMismatch { position, .. } => *position,
Self::IfMissingElse { position } => *position, Self::IfMissingElse { position } => *position,
Self::InstructionIndexOutOfBounds { position, .. } => *position,
Self::InvalidAssignmentTarget { position, .. } => *position, Self::InvalidAssignmentTarget { position, .. } => *position,
Self::Lex(error) => error.position(), Self::Lex(error) => error.position(),
Self::ListItemTypeConflict { position, .. } => *position, Self::ListItemTypeConflict { position, .. } => *position,
Self::LocalIndexOutOfBounds { position, .. } => *position,
Self::ParseFloatError { position, .. } => *position, Self::ParseFloatError { position, .. } => *position,
Self::ParseIntError { position, .. } => *position, Self::ParseIntError { position, .. } => *position,
Self::ReturnTypeConflict { position, .. } => *position, Self::ReturnTypeConflict { position, .. } => *position,

View File

@ -20,7 +20,7 @@
//! - Use the associated function on `Instruction` //! - Use the associated function on `Instruction`
//! - Use the corresponding struct and call `Instruction::from` //! - Use the corresponding struct and call `Instruction::from`
//! //!
//! # Example //! # Examples
//! //!
//! ``` //! ```
//! # use dust_lang::instruction::{Instruction, Move}; //! # use dust_lang::instruction::{Instruction, Move};
@ -30,8 +30,6 @@
//! assert_eq!(move_1, move_2); //! assert_eq!(move_1, move_2);
//! ``` //! ```
//! //!
//! # Example
//!
//! Use the `Destination` and `Argument` enums to create instructions. This is easier to read and //! Use the `Destination` and `Argument` enums to create instructions. This is easier to read and
//! enforces consistency in how the `Instruction` methods are called. //! enforces consistency in how the `Instruction` methods are called.
//! //!

View File

@ -15,7 +15,7 @@ pub mod r#type;
pub mod value; pub mod value;
pub mod vm; pub mod vm;
pub use crate::chunk::{Chunk, ChunkError, Local}; pub use crate::chunk::{Chunk, Local};
pub use crate::compiler::{compile, CompileError, Compiler}; pub use crate::compiler::{compile, CompileError, Compiler};
pub use crate::disassembler::Disassembler; pub use crate::disassembler::Disassembler;
pub use crate::dust_error::{AnnotatedError, DustError}; pub use crate::dust_error::{AnnotatedError, DustError};

View File

@ -1,7 +1,7 @@
//! Built-in functions that implement extended functionality. //! Built-in functions that implement extended functionality.
//! //!
//! Native functions are used either to implement features that are not possible to implement in //! Native functions are used to implement features that are not possible to implement in Dust
//! Dust itself or that are more efficient to implement in Rust. //! itself or that are more efficient to implement in Rust.
mod logic; mod logic;
use std::{ use std::{
@ -15,14 +15,14 @@ use serde::{Deserialize, Serialize};
use crate::{AnnotatedError, FunctionType, Instruction, Span, Type, Value, Vm, VmError}; use crate::{AnnotatedError, FunctionType, Instruction, Span, Type, Value, Vm, VmError};
macro_rules! define_native_function { macro_rules! define_native_function {
($(($name:ident, $byte:literal, $str:expr, $type:expr, $function:expr)),*) => { ($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => {
/// A dust-native function. /// A dust-native function.
/// ///
/// See the [module-level documentation](index.html) for more information. /// See the [module-level documentation](index.html) for more information.
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum NativeFunction { pub enum NativeFunction {
$( $(
$name = $byte as isize, $name = $bytes as isize,
)* )*
} }
@ -75,14 +75,14 @@ macro_rules! define_native_function {
} }
impl From<u16> for NativeFunction { impl From<u16> for NativeFunction {
fn from(byte: u16) -> Self { fn from(bytes: u16) -> Self {
match byte { match bytes {
$( $(
$byte => NativeFunction::$name, $bytes => NativeFunction::$name,
)* )*
_ => { _ => {
if cfg!(test) { if cfg!(test) {
panic!("Invalid native function byte: {}", byte) panic!("Invalid native function byte: {}", bytes)
} else { } else {
NativeFunction::Panic NativeFunction::Panic
} }
@ -91,11 +91,11 @@ macro_rules! define_native_function {
} }
} }
impl From<NativeFunction> for u8 { impl From<NativeFunction> for u16 {
fn from(native_function: NativeFunction) -> Self { fn from(native_function: NativeFunction) -> Self {
match native_function { match native_function {
$( $(
NativeFunction::$name => $byte, NativeFunction::$name => $bytes,
)* )*
} }
} }

View File

@ -1,6 +1,23 @@
//! Tools used by the compiler to optimize a chunk's bytecode. //! Tools used by the compiler to optimize a chunk's bytecode.
use crate::{instruction::SetLocal, Chunk, Operation}; use crate::{instruction::SetLocal, Instruction, Operation, Span, Type};
fn get_last_operations<const COUNT: usize>(
instructions: &[(Instruction, Type, Span)],
) -> Option<[Operation; COUNT]> {
let mut n_operations = [Operation::Return; COUNT];
for (nth, operation) in n_operations.iter_mut().rev().zip(
instructions
.iter()
.rev()
.map(|(instruction, _, _)| instruction.operation()),
) {
*nth = operation;
}
Some(n_operations)
}
/// Optimizes a short control flow pattern. /// Optimizes a short control flow pattern.
/// ///
@ -22,9 +39,9 @@ use crate::{instruction::SetLocal, Chunk, Operation};
/// - `Jump` /// - `Jump`
/// - `LoadBoolean` or `LoadConstant` /// - `LoadBoolean` or `LoadConstant`
/// - `LoadBoolean` or `LoadConstant` /// - `LoadBoolean` or `LoadConstant`
pub fn optimize_control_flow(chunk: &mut Chunk) { pub fn optimize_control_flow(instructions: &mut [(Instruction, Type, Span)]) {
if !matches!( if !matches!(
chunk.get_last_operations(), get_last_operations(instructions),
Some([ Some([
Operation::Equal | Operation::Less | Operation::LessEqual | Operation::Test, Operation::Equal | Operation::Less | Operation::LessEqual | Operation::Test,
Operation::Jump, Operation::Jump,
@ -37,7 +54,6 @@ pub fn optimize_control_flow(chunk: &mut Chunk) {
log::debug!("Consolidating registers for control flow optimization"); log::debug!("Consolidating registers for control flow optimization");
let instructions = chunk.instructions_mut();
let first_loader = &mut instructions.iter_mut().nth_back(1).unwrap().0; let first_loader = &mut instructions.iter_mut().nth_back(1).unwrap().0;
first_loader.set_c_to_boolean(true); first_loader.set_c_to_boolean(true);
@ -67,9 +83,9 @@ pub fn optimize_control_flow(chunk: &mut Chunk) {
/// The instructions must be in the following order: /// The instructions must be in the following order:
/// - `Add`, `Subtract`, `Multiply`, `Divide` or `Modulo` /// - `Add`, `Subtract`, `Multiply`, `Divide` or `Modulo`
/// - `SetLocal` /// - `SetLocal`
pub fn optimize_set_local(chunk: &mut Chunk) { pub fn optimize_set_local(instructions: &mut Vec<(Instruction, Type, Span)>) {
if !matches!( if !matches!(
chunk.get_last_operations(), get_last_operations(instructions),
Some([ Some([
Operation::Add Operation::Add
| Operation::Subtract | Operation::Subtract
@ -84,7 +100,6 @@ pub fn optimize_set_local(chunk: &mut Chunk) {
log::debug!("Condensing math and SetLocal to math instruction"); log::debug!("Condensing math and SetLocal to math instruction");
let instructions = chunk.instructions_mut();
let set_local = SetLocal::from(&instructions.pop().unwrap().0); let set_local = SetLocal::from(&instructions.pop().unwrap().0);
let math_instruction = instructions.last_mut().unwrap().0; let math_instruction = instructions.last_mut().unwrap().0;
let math_instruction_new = *math_instruction let math_instruction_new = *math_instruction

View File

@ -6,9 +6,9 @@ use std::{
}; };
use crate::{ use crate::{
compile, instruction::*, AbstractValue, AnnotatedError, Argument, Chunk, ChunkError, compile, instruction::*, AbstractValue, AnnotatedError, Argument, Chunk, ConcreteValue,
ConcreteValue, Destination, DustError, Instruction, NativeFunctionError, Operation, Span, Destination, DustError, Instruction, NativeFunctionError, Operation, Span, Value, ValueError,
Value, ValueError, ValueRef, ValueRef,
}; };
pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> { pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> {
@ -690,9 +690,10 @@ impl<'a> Vm<'a> {
fn get_constant(&self, constant_index: u16) -> Result<&ConcreteValue, VmError> { fn get_constant(&self, constant_index: u16) -> Result<&ConcreteValue, VmError> {
self.chunk self.chunk
.get_constant(constant_index) .constants()
.map_err(|error| VmError::Chunk { .get(constant_index as usize)
error, .ok_or_else(|| VmError::ConstantIndexOutOfBounds {
index: constant_index as usize,
position: self.current_position, position: self.current_position,
}) })
} }
@ -715,11 +716,11 @@ impl<'a> Vm<'a> {
fn read(&mut self) -> Result<Instruction, VmError> { fn read(&mut self) -> Result<Instruction, VmError> {
let (instruction, _type, position) = let (instruction, _type, position) =
self.chunk self.chunk.instructions().get(self.ip).ok_or_else(|| {
.get_instruction(self.ip) VmError::InstructionIndexOutOfBounds {
.map_err(|error| VmError::Chunk { index: self.ip,
error,
position: self.current_position, position: self.current_position,
}
})?; })?;
self.ip += 1; self.ip += 1;
@ -816,11 +817,21 @@ pub enum VmError {
position: Span, position: Span,
}, },
// Wrappers for foreign errors // Chunk errors
Chunk { ConstantIndexOutOfBounds {
error: ChunkError, index: usize,
position: Span, position: Span,
}, },
InstructionIndexOutOfBounds {
index: usize,
position: Span,
},
LocalIndexOutOfBounds {
index: usize,
position: Span,
},
// Wrappers for foreign errors
NativeFunction(NativeFunctionError), NativeFunction(NativeFunctionError),
Value { Value {
error: ValueError, error: ValueError,
@ -835,13 +846,15 @@ impl AnnotatedError for VmError {
fn description(&self) -> &'static str { fn description(&self) -> &'static str {
match self { match self {
Self::Chunk { .. } => "Chunk error", Self::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
Self::EmptyRegister { .. } => "Empty register", Self::EmptyRegister { .. } => "Empty register",
Self::ExpectedBoolean { .. } => "Expected boolean", Self::ExpectedBoolean { .. } => "Expected boolean",
Self::ExpectedConcreteValue { .. } => "Expected concrete value", Self::ExpectedConcreteValue { .. } => "Expected concrete value",
Self::ExpectedFunction { .. } => "Expected function", Self::ExpectedFunction { .. } => "Expected function",
Self::ExpectedParent { .. } => "Expected parent", Self::ExpectedParent { .. } => "Expected parent",
Self::ExpectedValue { .. } => "Expected value", Self::ExpectedValue { .. } => "Expected value",
Self::InstructionIndexOutOfBounds { .. } => "Instruction index out of bounds",
Self::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
Self::NativeFunction(error) => error.description(), Self::NativeFunction(error) => error.description(),
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds", Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
Self::StackOverflow { .. } => "Stack overflow", Self::StackOverflow { .. } => "Stack overflow",
@ -854,7 +867,6 @@ impl AnnotatedError for VmError {
fn details(&self) -> Option<String> { fn details(&self) -> Option<String> {
match self { match self {
Self::Chunk { error, .. } => Some(error.to_string()),
Self::EmptyRegister { index, .. } => Some(format!("Register R{index} is empty")), Self::EmptyRegister { index, .. } => Some(format!("Register R{index} is empty")),
Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")), Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")),
@ -870,13 +882,15 @@ impl AnnotatedError for VmError {
fn position(&self) -> Span { fn position(&self) -> Span {
match self { match self {
Self::Chunk { position, .. } => *position, Self::ConstantIndexOutOfBounds { position, .. } => *position,
Self::EmptyRegister { position, .. } => *position, Self::EmptyRegister { position, .. } => *position,
Self::ExpectedBoolean { position, .. } => *position, Self::ExpectedBoolean { position, .. } => *position,
Self::ExpectedConcreteValue { position, .. } => *position, Self::ExpectedConcreteValue { position, .. } => *position,
Self::ExpectedFunction { position, .. } => *position, Self::ExpectedFunction { position, .. } => *position,
Self::ExpectedParent { position } => *position, Self::ExpectedParent { position } => *position,
Self::ExpectedValue { position, .. } => *position, Self::ExpectedValue { position, .. } => *position,
Self::InstructionIndexOutOfBounds { position, .. } => *position,
Self::LocalIndexOutOfBounds { position, .. } => *position,
Self::NativeFunction(error) => error.position(), Self::NativeFunction(error) => error.position(),
Self::RegisterIndexOutOfBounds { position, .. } => *position, Self::RegisterIndexOutOfBounds { position, .. } => *position,
Self::StackOverflow { position } => *position, Self::StackOverflow { position } => *position,