1
0

Bring back chunk errors

This commit is contained in:
Jeff 2024-11-06 13:50:49 -05:00
parent c0b998c0d8
commit 78840cf3e7
4 changed files with 114 additions and 126 deletions

View File

@ -37,7 +37,7 @@
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
env::current_exe, env::current_exe,
fmt::{self, Debug, Display}, fmt::{self, Debug, Display, Formatter},
}; };
use colored::Colorize; use colored::Colorize;
@ -127,6 +127,12 @@ impl Chunk {
&mut self.instructions &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<Local> { pub fn locals(&self) -> &Vec<Local> {
&self.locals &self.locals
} }
@ -135,12 +141,32 @@ impl Chunk {
&mut self.locals &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 { pub fn current_scope(&self) -> Scope {
self.current_scope self.current_scope
} }
pub fn get_constant(&self, index: u8) -> Option<&Value> { pub fn get_constant(&self, index: u8) -> Result<&Value, ChunkError> {
self.constants.get(index as usize) 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 { 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<Type> { pub fn get_constant_type(&self, constant_index: u8) -> Option<Type> {
self.constants self.constants
.get(constant_index as usize) .get(constant_index as usize)
@ -652,3 +686,28 @@ impl<'a> ChunkDisassembler<'a> {
self.output 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"),
}
}
}

View File

@ -14,7 +14,7 @@ pub mod r#type;
pub mod value; pub mod value;
pub mod vm; 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::dust_error::{AnnotatedError, DustError};
pub use crate::formatter::{format, Formatter}; pub use crate::formatter::{format, Formatter};
pub use crate::instruction::Instruction; pub use crate::instruction::Instruction;

View File

@ -13,8 +13,9 @@ use std::{
use colored::Colorize; use colored::Colorize;
use crate::{ use crate::{
optimize, AnnotatedError, Chunk, DustError, FunctionType, Instruction, LexError, Lexer, Local, optimize, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Instruction, LexError,
NativeFunction, Operation, Scope, Span, Token, TokenKind, TokenOwned, Type, Value, Lexer, Local, NativeFunction, Operation, Scope, Span, Token, TokenKind, TokenOwned, Type,
Value,
}; };
/// Parses the input and returns a chunk. /// Parses the input and returns a chunk.
@ -127,13 +128,10 @@ impl<'src> Parser<'src> {
} }
fn get_local(&self, index: u8) -> Result<&Local, ParseError> { fn get_local(&self, index: u8) -> Result<&Local, ParseError> {
let index = index as usize;
self.chunk self.chunk
.locals() .get_local(index)
.get(index) .map_err(|error| ParseError::Chunk {
.ok_or(ParseError::LocalIndexOutOfBounds { error,
index,
position: self.current_position, position: self.current_position,
}) })
} }
@ -1882,7 +1880,7 @@ impl From<&Token<'_>> for ParseRule<'_> {
} }
} }
#[derive(Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum ParseError { pub enum ParseError {
// Token errors // Token errors
ExpectedToken { ExpectedToken {
@ -1934,19 +1932,11 @@ pub enum ParseError {
position: Span, position: Span,
}, },
// Chunk errors
LocalIndexOutOfBounds {
index: usize,
position: Span,
},
RegisterOverflow {
position: Span,
},
RegisterUnderflow {
position: Span,
},
// Wrappers around foreign errors // Wrappers around foreign errors
Chunk {
error: ChunkError,
position: Span,
},
Lex(LexError), Lex(LexError),
ParseFloatError { ParseFloatError {
error: ParseFloatError, error: ParseFloatError,
@ -1967,17 +1957,15 @@ impl AnnotatedError for ParseError {
match self { match self {
Self::CannotChainComparison { .. } => "Cannot chain comparison", Self::CannotChainComparison { .. } => "Cannot chain comparison",
Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable", Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable",
Self::Chunk { .. } => "Chunk error",
Self::ExpectedExpression { .. } => "Expected an expression", Self::ExpectedExpression { .. } => "Expected an expression",
Self::ExpectedMutableVariable { .. } => "Expected a mutable variable", Self::ExpectedMutableVariable { .. } => "Expected a mutable variable",
Self::ExpectedToken { .. } => "Expected a specific token", Self::ExpectedToken { .. } => "Expected a specific token",
Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens", Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens",
Self::InvalidAssignmentTarget { .. } => "Invalid assignment target", Self::InvalidAssignmentTarget { .. } => "Invalid assignment target",
Self::Lex(error) => error.description(), Self::Lex(error) => error.description(),
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::RegisterOverflow { .. } => "Register overflow",
Self::RegisterUnderflow { .. } => "Register underflow",
Self::UndeclaredVariable { .. } => "Undeclared variable", Self::UndeclaredVariable { .. } => "Undeclared variable",
Self::UnexpectedReturn { .. } => "Unexpected return", Self::UnexpectedReturn { .. } => "Unexpected return",
Self::VariableOutOfScope { .. } => "Variable out of scope", Self::VariableOutOfScope { .. } => "Variable out of scope",
@ -1992,6 +1980,7 @@ impl AnnotatedError for ParseError {
Self::CannotMutateImmutableVariable { identifier, .. } => { Self::CannotMutateImmutableVariable { identifier, .. } => {
Some(format!("Cannot mutate immutable variable {identifier}")) Some(format!("Cannot mutate immutable variable {identifier}"))
} }
Self::Chunk { error, .. } => Some(error.to_string()),
Self::ExpectedExpression { found, .. } => Some(format!("Found {found}")), Self::ExpectedExpression { found, .. } => Some(format!("Found {found}")),
Self::ExpectedToken { Self::ExpectedToken {
expected, found, .. expected, found, ..
@ -2024,13 +2013,9 @@ impl AnnotatedError for ParseError {
Some(format!("Invalid assignment target, found {found}")) Some(format!("Invalid assignment target, found {found}"))
} }
Self::Lex(error) => error.details(), Self::Lex(error) => error.details(),
Self::LocalIndexOutOfBounds { index, .. } => {
Some(format!("Local index {index} out of bounds"))
}
Self::ParseFloatError { error, .. } => Some(error.to_string()), Self::ParseFloatError { error, .. } => Some(error.to_string()),
Self::ParseIntError { error, .. } => Some(error.to_string()), Self::ParseIntError { error, .. } => Some(error.to_string()),
Self::RegisterOverflow { .. } => None,
Self::RegisterUnderflow { .. } => None,
Self::UndeclaredVariable { identifier, .. } => { Self::UndeclaredVariable { identifier, .. } => {
Some(format!("Undeclared variable {identifier}")) Some(format!("Undeclared variable {identifier}"))
} }
@ -2045,17 +2030,15 @@ impl AnnotatedError for ParseError {
match self { match self {
Self::CannotChainComparison { position } => *position, Self::CannotChainComparison { position } => *position,
Self::CannotMutateImmutableVariable { position, .. } => *position, Self::CannotMutateImmutableVariable { position, .. } => *position,
Self::Chunk { position, .. } => *position,
Self::ExpectedExpression { position, .. } => *position, Self::ExpectedExpression { position, .. } => *position,
Self::ExpectedMutableVariable { position, .. } => *position, Self::ExpectedMutableVariable { position, .. } => *position,
Self::ExpectedToken { position, .. } => *position, Self::ExpectedToken { position, .. } => *position,
Self::ExpectedTokenMultiple { position, .. } => *position, Self::ExpectedTokenMultiple { position, .. } => *position,
Self::InvalidAssignmentTarget { position, .. } => *position, Self::InvalidAssignmentTarget { position, .. } => *position,
Self::Lex(error) => error.position(), Self::Lex(error) => error.position(),
Self::LocalIndexOutOfBounds { position, .. } => *position,
Self::ParseFloatError { position, .. } => *position, Self::ParseFloatError { position, .. } => *position,
Self::ParseIntError { position, .. } => *position, Self::ParseIntError { position, .. } => *position,
Self::RegisterOverflow { position } => *position,
Self::RegisterUnderflow { position } => *position,
Self::UndeclaredVariable { position, .. } => *position, Self::UndeclaredVariable { position, .. } => *position,
Self::UnexpectedReturn { position } => *position, Self::UnexpectedReturn { position } => *position,
Self::VariableOutOfScope { position, .. } => *position, Self::VariableOutOfScope { position, .. } => *position,

View File

@ -2,8 +2,9 @@
use std::{cmp::Ordering, mem::replace}; use std::{cmp::Ordering, mem::replace};
use crate::{ use crate::{
parse, value::ConcreteValue, AnnotatedError, Chunk, DustError, FunctionType, Instruction, parse, value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionType,
Local, NativeFunction, NativeFunctionError, Operation, Span, Type, Value, ValueError, Instruction, Local, NativeFunction, NativeFunctionError, Operation, Span, Type, Value,
ValueError,
}; };
pub fn run(source: &str) -> Result<Option<Value>, DustError> { pub fn run(source: &str) -> Result<Option<Value>, DustError> {
@ -45,22 +46,12 @@ impl Vm {
position: Span, position: Span,
) -> Result<(&Value, &Value), VmError> { ) -> Result<(&Value, &Value), VmError> {
let left = if instruction.b_is_constant() { let left = if instruction.b_is_constant() {
vm.chunk.get_constant(instruction.b()).ok_or_else(|| { vm.get_constant(instruction.b(), position)?
VmError::ConstantIndexOutOfBounds {
index: instruction.b() as usize,
position,
}
})?
} else { } else {
vm.get_register(instruction.b(), position)? vm.get_register(instruction.b(), position)?
}; };
let right = if instruction.c_is_constant() { let right = if instruction.c_is_constant() {
vm.chunk.get_constant(instruction.c()).ok_or_else(|| { vm.get_constant(instruction.c(), position)?
VmError::ConstantIndexOutOfBounds {
index: instruction.c() as usize,
position,
}
})?
} else { } else {
vm.get_register(instruction.c(), position)? vm.get_register(instruction.c(), position)?
}; };
@ -336,12 +327,7 @@ impl Vm {
} }
Operation::Negate => { Operation::Negate => {
let value = if instruction.b_is_constant() { let value = if instruction.b_is_constant() {
self.chunk.get_constant(instruction.b()).ok_or_else(|| { self.get_constant(instruction.b(), position)?
VmError::ConstantIndexOutOfBounds {
index: instruction.b() as usize,
position,
}
})?
} else { } else {
self.get_register(instruction.b(), position)? self.get_register(instruction.b(), position)?
}; };
@ -353,12 +339,7 @@ impl Vm {
} }
Operation::Not => { Operation::Not => {
let value = if instruction.b_is_constant() { let value = if instruction.b_is_constant() {
self.chunk.get_constant(instruction.b()).ok_or_else(|| { self.get_constant(instruction.b(), position)?
VmError::ConstantIndexOutOfBounds {
index: instruction.b() as usize,
position,
}
})?
} else { } else {
self.get_register(instruction.b(), position)? 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> { pub fn get_register(&self, index: u8, position: Span) -> Result<&Value, VmError> {
let index = index as usize; let index = index as usize;
let register = self let register = self
@ -598,13 +585,7 @@ impl Vm {
match register { match register {
Register::Value(value) => Ok(value), Register::Value(value) => Ok(value),
Register::Pointer(register_index) => self.get_register(*register_index, position), Register::Pointer(register_index) => self.get_register(*register_index, position),
Register::Constant(constant_index) => self Register::Constant(constant_index) => self.get_constant(*constant_index, position),
.chunk
.get_constant(*constant_index)
.ok_or_else(|| VmError::ConstantIndexOutOfBounds {
index: *constant_index as usize,
position,
}),
Register::Empty => Err(VmError::EmptyRegister { 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> { fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> {
if self.chunk.is_poisoned { self.chunk
return Err(VmError::PoisonedChunk { position }); .expect_not_poisoned()
} .map_err(|error| VmError::Chunk { error, position })?;
if self.ip >= self.chunk.len() { let max_ip = self.chunk.len() - 1;
self.ip = self.chunk.len() - 1;
}
let current = self.chunk.instructions().get(self.ip).ok_or_else(|| {
VmError::InstructionIndexOutOfBounds {
index: self.ip,
position,
}
})?;
if self.ip > max_ip {
return self.get_instruction(max_ip, position);
} else {
self.ip += 1; self.ip += 1;
}
Ok(current) self.get_instruction(self.ip - 1, position)
} }
fn define_local( fn define_local(
@ -668,12 +644,8 @@ impl Vm {
) -> Result<(), VmError> { ) -> Result<(), VmError> {
let local = self let local = self
.chunk .chunk
.locals_mut() .get_local_mut(local_index)
.get_mut(local_index as usize) .map_err(|error| VmError::Chunk { error, position })?;
.ok_or_else(|| VmError::LocalIndexOutOfBounds {
index: local_index as usize,
position,
})?;
log::debug!("Define local L{}", local_index); 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> { fn get_local(&self, local_index: u8, position: Span) -> Result<&Local, VmError> {
let local_index = local_index as usize;
self.chunk self.chunk
.locals() .get_local(local_index)
.get(local_index) .map_err(|error| VmError::Chunk { error, position })
.ok_or_else(|| VmError::LocalIndexOutOfBounds {
index: local_index,
position,
})
} }
fn get_instruction( fn get_instruction(
@ -700,9 +666,8 @@ impl Vm {
position: Span, position: Span,
) -> Result<&(Instruction, Span), VmError> { ) -> Result<&(Instruction, Span), VmError> {
self.chunk self.chunk
.instructions() .get_instruction(index)
.get(index) .map_err(|error| VmError::Chunk { error, position })
.ok_or_else(|| VmError::InstructionIndexOutOfBounds { index, position })
} }
} }
@ -714,7 +679,7 @@ enum Register {
Constant(u8), Constant(u8),
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum VmError { pub enum VmError {
// Stack errors // Stack errors
StackOverflow { position: Span }, StackOverflow { position: Span },
@ -724,17 +689,12 @@ pub enum VmError {
EmptyRegister { index: usize, position: Span }, EmptyRegister { index: usize, position: Span },
RegisterIndexOutOfBounds { 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 // Execution errors
ExpectedBoolean { found: Value, position: Span }, ExpectedBoolean { found: Value, position: Span },
ExpectedFunction { found: Value, position: Span }, ExpectedFunction { found: Value, position: Span },
// Wrappers for foreign errors // Wrappers for foreign errors
Chunk { error: ChunkError, position: Span },
NativeFunction(NativeFunctionError), NativeFunction(NativeFunctionError),
Value { error: ValueError, position: Span }, Value { error: ValueError, position: Span },
} }
@ -746,14 +706,11 @@ impl AnnotatedError for VmError {
fn description(&self) -> &'static str { fn description(&self) -> &'static str {
match self { match self {
Self::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds", Self::Chunk { .. } => "Chunk error",
Self::EmptyRegister { .. } => "Empty register", Self::EmptyRegister { .. } => "Empty register",
Self::ExpectedBoolean { .. } => "Expected boolean", Self::ExpectedBoolean { .. } => "Expected boolean",
Self::ExpectedFunction { .. } => "Expected function", Self::ExpectedFunction { .. } => "Expected function",
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::PoisonedChunk { .. } => "Poisoned chunk",
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds", Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
Self::StackOverflow { .. } => "Stack overflow", Self::StackOverflow { .. } => "Stack overflow",
Self::StackUnderflow { .. } => "Stack underflow", Self::StackUnderflow { .. } => "Stack underflow",
@ -763,20 +720,12 @@ impl AnnotatedError for VmError {
fn details(&self) -> Option<String> { fn details(&self) -> Option<String> {
match self { match self {
Self::ConstantIndexOutOfBounds { index, .. } => { Self::Chunk { error, .. } => Some(error.to_string()),
Some(format!("Constant C{index} does not exist"))
}
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")),
Self::RegisterIndexOutOfBounds { index, .. } => { Self::RegisterIndexOutOfBounds { index, .. } => {
Some(format!("Register {index} does not exist")) 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::NativeFunction(error) => error.details(),
Self::Value { error, .. } => Some(error.to_string()), Self::Value { error, .. } => Some(error.to_string()),
_ => None, _ => None,
@ -785,14 +734,11 @@ impl AnnotatedError for VmError {
fn position(&self) -> Span { fn position(&self) -> Span {
match self { match self {
Self::ConstantIndexOutOfBounds { position, .. } => *position, Self::Chunk { position, .. } => *position,
Self::EmptyRegister { position, .. } => *position, Self::EmptyRegister { position, .. } => *position,
Self::ExpectedBoolean { position, .. } => *position, Self::ExpectedBoolean { position, .. } => *position,
Self::ExpectedFunction { position, .. } => *position, Self::ExpectedFunction { position, .. } => *position,
Self::InstructionIndexOutOfBounds { position, .. } => *position,
Self::LocalIndexOutOfBounds { position, .. } => *position,
Self::NativeFunction(error) => error.position(), Self::NativeFunction(error) => error.position(),
Self::PoisonedChunk { position } => *position,
Self::RegisterIndexOutOfBounds { position, .. } => *position, Self::RegisterIndexOutOfBounds { position, .. } => *position,
Self::StackOverflow { position } => *position, Self::StackOverflow { position } => *position,
Self::StackUnderflow { position } => *position, Self::StackUnderflow { position } => *position,