diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index cc6ae09..eee1823 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -37,7 +37,7 @@ use std::{ cmp::Ordering, env::current_exe, - fmt::{self, Debug, Display}, + fmt::{self, Debug, Display, Formatter}, }; use colored::Colorize; @@ -127,6 +127,12 @@ impl Chunk { &mut self.instructions } + pub fn get_instruction(&self, index: usize) -> Result<&(Instruction, Span), ChunkError> { + self.instructions + .get(index) + .ok_or(ChunkError::InstructionIndexOutOfBounds { index }) + } + pub fn locals(&self) -> &Vec { &self.locals } @@ -135,12 +141,32 @@ impl Chunk { &mut self.locals } + pub fn get_local(&self, index: u8) -> 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: u8) -> Result<&mut Local, ChunkError> { + self.locals + .get_mut(index as usize) + .ok_or(ChunkError::LocalIndexOutOfBounds { + index: index as usize, + }) + } + pub fn current_scope(&self) -> Scope { self.current_scope } - pub fn get_constant(&self, index: u8) -> Option<&Value> { - self.constants.get(index as usize) + pub fn get_constant(&self, index: u8) -> Result<&Value, ChunkError> { + self.constants + .get(index as usize) + .ok_or(ChunkError::ConstantIndexOutOfBounds { + index: index as usize, + }) } pub fn push_or_get_constant(&mut self, value: Value) -> u8 { @@ -181,6 +207,14 @@ impl Chunk { } } + pub fn expect_not_poisoned(&self) -> Result<(), ChunkError> { + if self.is_poisoned { + Err(ChunkError::PoisonedChunk) + } else { + Ok(()) + } + } + pub fn get_constant_type(&self, constant_index: u8) -> Option { self.constants .get(constant_index as usize) @@ -652,3 +686,28 @@ impl<'a> ChunkDisassembler<'a> { self.output } } + +#[derive(Clone, Debug, PartialEq)] +pub enum ChunkError { + ConstantIndexOutOfBounds { index: usize }, + InstructionIndexOutOfBounds { index: usize }, + LocalIndexOutOfBounds { index: usize }, + PoisonedChunk, +} + +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::InstructionIndexOutOfBounds { index } => { + write!(f, "Instruction index {} out of bounds", index) + } + ChunkError::LocalIndexOutOfBounds { index } => { + write!(f, "Local index {} out of bounds", index) + } + ChunkError::PoisonedChunk => write!(f, "Chunk is poisoned"), + } + } +} diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 483e1af..2b67964 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -14,7 +14,7 @@ pub mod r#type; pub mod value; pub mod vm; -pub use crate::chunk::{Chunk, ChunkDisassembler, Local, Scope}; +pub use crate::chunk::{Chunk, ChunkDisassembler, ChunkError, Local, Scope}; pub use crate::dust_error::{AnnotatedError, DustError}; pub use crate::formatter::{format, Formatter}; pub use crate::instruction::Instruction; diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index fc2c859..a889a1d 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -13,8 +13,9 @@ use std::{ use colored::Colorize; use crate::{ - optimize, AnnotatedError, Chunk, DustError, FunctionType, Instruction, LexError, Lexer, Local, - NativeFunction, Operation, Scope, Span, Token, TokenKind, TokenOwned, Type, Value, + optimize, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Instruction, LexError, + Lexer, Local, NativeFunction, Operation, Scope, Span, Token, TokenKind, TokenOwned, Type, + Value, }; /// Parses the input and returns a chunk. @@ -127,13 +128,10 @@ impl<'src> Parser<'src> { } fn get_local(&self, index: u8) -> Result<&Local, ParseError> { - let index = index as usize; - self.chunk - .locals() - .get(index) - .ok_or(ParseError::LocalIndexOutOfBounds { - index, + .get_local(index) + .map_err(|error| ParseError::Chunk { + error, position: self.current_position, }) } @@ -1882,7 +1880,7 @@ impl From<&Token<'_>> for ParseRule<'_> { } } -#[derive(Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum ParseError { // Token errors ExpectedToken { @@ -1934,19 +1932,11 @@ pub enum ParseError { position: Span, }, - // Chunk errors - LocalIndexOutOfBounds { - index: usize, - position: Span, - }, - RegisterOverflow { - position: Span, - }, - RegisterUnderflow { - position: Span, - }, - // Wrappers around foreign errors + Chunk { + error: ChunkError, + position: Span, + }, Lex(LexError), ParseFloatError { error: ParseFloatError, @@ -1967,17 +1957,15 @@ impl AnnotatedError for ParseError { match self { Self::CannotChainComparison { .. } => "Cannot chain comparison", Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable", + Self::Chunk { .. } => "Chunk error", Self::ExpectedExpression { .. } => "Expected an expression", Self::ExpectedMutableVariable { .. } => "Expected a mutable variable", Self::ExpectedToken { .. } => "Expected a specific token", Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens", Self::InvalidAssignmentTarget { .. } => "Invalid assignment target", Self::Lex(error) => error.description(), - Self::LocalIndexOutOfBounds { .. } => "Local index out of bounds", Self::ParseFloatError { .. } => "Failed to parse float", Self::ParseIntError { .. } => "Failed to parse integer", - Self::RegisterOverflow { .. } => "Register overflow", - Self::RegisterUnderflow { .. } => "Register underflow", Self::UndeclaredVariable { .. } => "Undeclared variable", Self::UnexpectedReturn { .. } => "Unexpected return", Self::VariableOutOfScope { .. } => "Variable out of scope", @@ -1992,6 +1980,7 @@ impl AnnotatedError for ParseError { Self::CannotMutateImmutableVariable { identifier, .. } => { Some(format!("Cannot mutate immutable variable {identifier}")) } + Self::Chunk { error, .. } => Some(error.to_string()), Self::ExpectedExpression { found, .. } => Some(format!("Found {found}")), Self::ExpectedToken { expected, found, .. @@ -2024,13 +2013,9 @@ impl AnnotatedError for ParseError { Some(format!("Invalid assignment target, found {found}")) } Self::Lex(error) => error.details(), - Self::LocalIndexOutOfBounds { index, .. } => { - Some(format!("Local index {index} out of bounds")) - } + Self::ParseFloatError { error, .. } => Some(error.to_string()), Self::ParseIntError { error, .. } => Some(error.to_string()), - Self::RegisterOverflow { .. } => None, - Self::RegisterUnderflow { .. } => None, Self::UndeclaredVariable { identifier, .. } => { Some(format!("Undeclared variable {identifier}")) } @@ -2045,17 +2030,15 @@ impl AnnotatedError for ParseError { match self { Self::CannotChainComparison { position } => *position, Self::CannotMutateImmutableVariable { position, .. } => *position, + Self::Chunk { position, .. } => *position, Self::ExpectedExpression { position, .. } => *position, Self::ExpectedMutableVariable { position, .. } => *position, Self::ExpectedToken { position, .. } => *position, Self::ExpectedTokenMultiple { position, .. } => *position, Self::InvalidAssignmentTarget { position, .. } => *position, Self::Lex(error) => error.position(), - Self::LocalIndexOutOfBounds { position, .. } => *position, Self::ParseFloatError { position, .. } => *position, Self::ParseIntError { position, .. } => *position, - Self::RegisterOverflow { position } => *position, - Self::RegisterUnderflow { position } => *position, Self::UndeclaredVariable { position, .. } => *position, Self::UnexpectedReturn { position } => *position, Self::VariableOutOfScope { position, .. } => *position, diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 308a728..c455758 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -2,8 +2,9 @@ use std::{cmp::Ordering, mem::replace}; use crate::{ - parse, value::ConcreteValue, AnnotatedError, Chunk, DustError, FunctionType, Instruction, - Local, NativeFunction, NativeFunctionError, Operation, Span, Type, Value, ValueError, + parse, value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, + Instruction, Local, NativeFunction, NativeFunctionError, Operation, Span, Type, Value, + ValueError, }; pub fn run(source: &str) -> Result, DustError> { @@ -45,22 +46,12 @@ impl Vm { position: Span, ) -> Result<(&Value, &Value), VmError> { let left = if instruction.b_is_constant() { - vm.chunk.get_constant(instruction.b()).ok_or_else(|| { - VmError::ConstantIndexOutOfBounds { - index: instruction.b() as usize, - position, - } - })? + vm.get_constant(instruction.b(), position)? } else { vm.get_register(instruction.b(), position)? }; let right = if instruction.c_is_constant() { - vm.chunk.get_constant(instruction.c()).ok_or_else(|| { - VmError::ConstantIndexOutOfBounds { - index: instruction.c() as usize, - position, - } - })? + vm.get_constant(instruction.c(), position)? } else { vm.get_register(instruction.c(), position)? }; @@ -336,12 +327,7 @@ impl Vm { } Operation::Negate => { let value = if instruction.b_is_constant() { - self.chunk.get_constant(instruction.b()).ok_or_else(|| { - VmError::ConstantIndexOutOfBounds { - index: instruction.b() as usize, - position, - } - })? + self.get_constant(instruction.b(), position)? } else { self.get_register(instruction.b(), position)? }; @@ -353,12 +339,7 @@ impl Vm { } Operation::Not => { let value = if instruction.b_is_constant() { - self.chunk.get_constant(instruction.b()).ok_or_else(|| { - VmError::ConstantIndexOutOfBounds { - index: instruction.b() as usize, - position, - } - })? + self.get_constant(instruction.b(), position)? } else { self.get_register(instruction.b(), position)? }; @@ -588,6 +569,12 @@ impl Vm { } } + fn get_constant(&self, index: u8, position: Span) -> Result<&Value, VmError> { + self.chunk + .get_constant(index) + .map_err(|error| VmError::Chunk { error, position }) + } + pub fn get_register(&self, index: u8, position: Span) -> Result<&Value, VmError> { let index = index as usize; let register = self @@ -598,13 +585,7 @@ impl Vm { 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::Constant(constant_index) => self.get_constant(*constant_index, position), Register::Empty => Err(VmError::EmptyRegister { index, position }), } } @@ -640,24 +621,19 @@ impl Vm { } fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> { - if self.chunk.is_poisoned { - return Err(VmError::PoisonedChunk { position }); + self.chunk + .expect_not_poisoned() + .map_err(|error| VmError::Chunk { error, position })?; + + let max_ip = self.chunk.len() - 1; + + if self.ip > max_ip { + return self.get_instruction(max_ip, position); + } else { + self.ip += 1; } - 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) + self.get_instruction(self.ip - 1, position) } fn define_local( @@ -668,12 +644,8 @@ impl Vm { ) -> 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, - })?; + .get_local_mut(local_index) + .map_err(|error| VmError::Chunk { error, position })?; log::debug!("Define local L{}", local_index); @@ -683,15 +655,9 @@ impl Vm { } 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, - }) + .get_local(local_index) + .map_err(|error| VmError::Chunk { error, position }) } fn get_instruction( @@ -700,9 +666,8 @@ impl Vm { position: Span, ) -> Result<&(Instruction, Span), VmError> { self.chunk - .instructions() - .get(index) - .ok_or_else(|| VmError::InstructionIndexOutOfBounds { index, position }) + .get_instruction(index) + .map_err(|error| VmError::Chunk { error, position }) } } @@ -714,7 +679,7 @@ enum Register { Constant(u8), } -#[derive(Debug, Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum VmError { // Stack errors StackOverflow { position: Span }, @@ -724,17 +689,12 @@ pub enum VmError { 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 + Chunk { error: ChunkError, position: Span }, NativeFunction(NativeFunctionError), Value { error: ValueError, position: Span }, } @@ -746,14 +706,11 @@ impl AnnotatedError for VmError { fn description(&self) -> &'static str { match self { - Self::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds", + Self::Chunk { .. } => "Chunk error", Self::EmptyRegister { .. } => "Empty register", Self::ExpectedBoolean { .. } => "Expected boolean", 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", @@ -763,20 +720,12 @@ impl AnnotatedError for VmError { fn details(&self) -> Option { match self { - Self::ConstantIndexOutOfBounds { index, .. } => { - Some(format!("Constant C{index} does not exist")) - } + Self::Chunk { error, .. } => Some(error.to_string()), Self::EmptyRegister { index, .. } => Some(format!("Register R{index} is empty")), Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")), Self::RegisterIndexOutOfBounds { index, .. } => { Some(format!("Register {index} does not exist")) } - Self::InstructionIndexOutOfBounds { index, .. } => { - Some(format!("Instruction {index} does not exist")) - } - Self::LocalIndexOutOfBounds { index, .. } => { - Some(format!("Local L{index} does not exist")) - } Self::NativeFunction(error) => error.details(), Self::Value { error, .. } => Some(error.to_string()), _ => None, @@ -785,14 +734,11 @@ impl AnnotatedError for VmError { fn position(&self) -> Span { match self { - Self::ConstantIndexOutOfBounds { position, .. } => *position, + Self::Chunk { position, .. } => *position, Self::EmptyRegister { position, .. } => *position, Self::ExpectedBoolean { position, .. } => *position, Self::ExpectedFunction { position, .. } => *position, - Self::InstructionIndexOutOfBounds { position, .. } => *position, - Self::LocalIndexOutOfBounds { position, .. } => *position, Self::NativeFunction(error) => error.position(), - Self::PoisonedChunk { position } => *position, Self::RegisterIndexOutOfBounds { position, .. } => *position, Self::StackOverflow { position } => *position, Self::StackUnderflow { position } => *position,