Add error reports and byte operations

This commit is contained in:
Jeff 2024-09-10 01:04:30 -04:00
parent 8f20e53880
commit 8798efc0af
6 changed files with 315 additions and 116 deletions

View File

@ -43,7 +43,7 @@ impl Chunk {
pub fn get_code(&self, offset: usize) -> Result<&(u8, Span), ChunkError> { pub fn get_code(&self, offset: usize) -> Result<&(u8, Span), ChunkError> {
self.code self.code
.get(offset) .get(offset)
.ok_or(ChunkError::CodeIndextOfBounds(offset)) .ok_or(ChunkError::CodeIndexOfBounds(offset))
} }
pub fn push_code(&mut self, instruction: u8, position: Span) { pub fn push_code(&mut self, instruction: u8, position: Span) {
@ -169,22 +169,47 @@ impl Default for Chunk {
impl Display for Chunk { impl Display for Chunk {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.disassemble("Chunk")) write!(f, "{}", self.disassemble("Chunk Disassembly"))
} }
} }
impl Debug for Chunk { impl Debug for Chunk {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.disassemble("Chunk")) write!(f, "{}", self.disassemble("Chunk Disassembly"))
} }
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum ChunkError { pub enum ChunkError {
CodeIndextOfBounds(usize), CodeIndexOfBounds(usize),
ConstantOverflow, ConstantOverflow,
ConstantIndexOutOfBounds(u8), ConstantIndexOutOfBounds(u8),
IdentifierIndexOutOfBounds(u8), IdentifierIndexOutOfBounds(u8),
IdentifierOverflow, IdentifierOverflow,
IdentifierNotFound(Identifier), IdentifierNotFound(Identifier),
} }
impl ChunkError {
pub fn title(&self) -> &'static str {
"Chunk Error"
}
pub fn description(&self) -> String {
match self {
Self::CodeIndexOfBounds(offset) => format!("{offset} is out of bounds",),
Self::ConstantOverflow => "More than 256 constants declared in one chunk".to_string(),
Self::ConstantIndexOutOfBounds(index) => {
format!("{index} is out of bounds")
}
Self::IdentifierIndexOutOfBounds(index) => {
format!("{index} is out of bounds")
}
Self::IdentifierOverflow => {
"More than 256 identifiers declared in one chunk".to_string()
}
Self::IdentifierNotFound(identifier) => {
format!("{} does not exist in this scope", identifier)
}
}
}
}

View File

@ -1,3 +1,5 @@
use annotate_snippets::{Level, Renderer, Snippet};
use crate::{vm::VmError, LexError, ParseError}; use crate::{vm::VmError, LexError, ParseError};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -15,3 +17,30 @@ pub enum DustError<'src> {
source: &'src str, source: &'src str,
}, },
} }
impl<'src> DustError<'src> {
pub fn report(&self) -> String {
let mut report = String::new();
let renderer = Renderer::styled();
match self {
DustError::Runtime { error, source } => {
let position = error.position();
let label = format!("Runtime {}", error.title());
let description = error.description();
let message = Level::Error.title(&label).snippet(
Snippet::source(source).fold(true).annotation(
Level::Error
.span(position.0..position.1)
.label(&description),
),
);
report.push_str(&renderer.render(message).to_string());
}
_ => todo!(),
}
report
}
}

View File

@ -206,6 +206,7 @@ impl<'src> Parser<'src> {
TokenKind::Minus => Instruction::Subtract as u8, TokenKind::Minus => Instruction::Subtract as u8,
TokenKind::Star => Instruction::Multiply as u8, TokenKind::Star => Instruction::Multiply as u8,
TokenKind::Slash => Instruction::Divide as u8, TokenKind::Slash => Instruction::Divide as u8,
TokenKind::DoubleAmpersand => Instruction::And as u8,
_ => { _ => {
return Err(ParseError::ExpectedTokenMultiple { return Err(ParseError::ExpectedTokenMultiple {
expected: vec![ expected: vec![
@ -319,8 +320,8 @@ impl<'src> Parser<'src> {
fn parse(&mut self, precedence: Precedence) -> Result<(), ParseError> { fn parse(&mut self, precedence: Precedence) -> Result<(), ParseError> {
self.advance()?; self.advance()?;
let prefix_rule = if let Some(prefix) = ParseRule::from(&self.previous_token.kind()).prefix let prefix_parser =
{ if let Some(prefix) = ParseRule::from(&self.previous_token.kind()).prefix {
log::trace!( log::trace!(
"Parsing {} as prefix with precedence {precedence}", "Parsing {} as prefix with precedence {precedence}",
self.previous_token, self.previous_token,
@ -333,17 +334,14 @@ impl<'src> Parser<'src> {
position: self.previous_position, position: self.previous_position,
}); });
}; };
let allow_assignment = precedence <= Precedence::Assignment; let allow_assignment = precedence <= Precedence::Assignment;
prefix_rule(self, allow_assignment)?; prefix_parser(self, allow_assignment)?;
while precedence < ParseRule::from(&self.current_token.kind()).precedence { while precedence < ParseRule::from(&self.current_token.kind()).precedence {
self.advance()?; self.advance()?;
let infix_rule = ParseRule::from(&self.previous_token.kind()).infix; if let Some(infix_parser) = ParseRule::from(&self.previous_token.kind()).infix {
if let Some(infix) = infix_rule {
log::trace!( log::trace!(
"Parsing {} as infix with precedence {precedence}", "Parsing {} as infix with precedence {precedence}",
self.previous_token, self.previous_token,
@ -356,7 +354,7 @@ impl<'src> Parser<'src> {
}); });
} }
infix(self)?; infix_parser(self)?;
} else { } else {
break; break;
} }
@ -485,7 +483,11 @@ impl From<&TokenKind> for ParseRule<'_> {
TokenKind::Colon => todo!(), TokenKind::Colon => todo!(),
TokenKind::Comma => todo!(), TokenKind::Comma => todo!(),
TokenKind::Dot => todo!(), TokenKind::Dot => todo!(),
TokenKind::DoubleAmpersand => todo!(), TokenKind::DoubleAmpersand => ParseRule {
prefix: None,
infix: Some(Parser::parse_binary),
precedence: Precedence::LogicalAnd,
},
TokenKind::DoubleDot => todo!(), TokenKind::DoubleDot => todo!(),
TokenKind::DoubleEqual => todo!(), TokenKind::DoubleEqual => todo!(),
TokenKind::DoublePipe => todo!(), TokenKind::DoublePipe => todo!(),

View File

@ -2,7 +2,6 @@
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
collections::HashMap, collections::HashMap,
error::Error,
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
ops::{Range, RangeInclusive}, ops::{Range, RangeInclusive},
sync::{Arc, RwLock}, sync::{Arc, RwLock},
@ -1006,6 +1005,9 @@ impl ValueData {
pub fn add(&self, other: &ValueData) -> Option<ValueData> { pub fn add(&self, other: &ValueData) -> Option<ValueData> {
match (self, other) { match (self, other) {
(ValueData::Byte(left), ValueData::Byte(right)) => {
Some(ValueData::Byte(left.saturating_add(*right)))
}
(ValueData::Float(left), ValueData::Float(right)) => { (ValueData::Float(left), ValueData::Float(right)) => {
Some(ValueData::Float(left + right)) Some(ValueData::Float(left + right))
} }
@ -1021,6 +1023,9 @@ impl ValueData {
pub fn subtract(&self, other: &ValueData) -> Option<ValueData> { pub fn subtract(&self, other: &ValueData) -> Option<ValueData> {
match (self, other) { match (self, other) {
(ValueData::Byte(left), ValueData::Byte(right)) => {
Some(ValueData::Byte(left.saturating_sub(*right)))
}
(ValueData::Float(left), ValueData::Float(right)) => { (ValueData::Float(left), ValueData::Float(right)) => {
Some(ValueData::Float(left - right)) Some(ValueData::Float(left - right))
} }
@ -1033,6 +1038,9 @@ impl ValueData {
pub fn multiply(&self, other: &ValueData) -> Option<ValueData> { pub fn multiply(&self, other: &ValueData) -> Option<ValueData> {
match (self, other) { match (self, other) {
(ValueData::Byte(left), ValueData::Byte(right)) => {
Some(ValueData::Byte(left.saturating_mul(*right)))
}
(ValueData::Float(left), ValueData::Float(right)) => { (ValueData::Float(left), ValueData::Float(right)) => {
Some(ValueData::Float(left * right)) Some(ValueData::Float(left * right))
} }
@ -1045,6 +1053,9 @@ impl ValueData {
pub fn divide(&self, other: &ValueData) -> Option<ValueData> { pub fn divide(&self, other: &ValueData) -> Option<ValueData> {
match (self, other) { match (self, other) {
(ValueData::Byte(left), ValueData::Byte(right)) => {
Some(ValueData::Byte(left.saturating_div(*right)))
}
(ValueData::Float(left), ValueData::Float(right)) => { (ValueData::Float(left), ValueData::Float(right)) => {
Some(ValueData::Float(left / right)) Some(ValueData::Float(left / right))
} }
@ -1170,7 +1181,7 @@ impl Display for ValueData {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
ValueData::Boolean(boolean) => write!(f, "{boolean}"), ValueData::Boolean(boolean) => write!(f, "{boolean}"),
ValueData::Byte(byte) => write!(f, "{byte}"), ValueData::Byte(byte) => write!(f, "0x{byte:02x}"),
ValueData::Character(character) => write!(f, "{character}"), ValueData::Character(character) => write!(f, "{character}"),
ValueData::Enum(r#enum) => write!(f, "{enum}"), ValueData::Enum(r#enum) => write!(f, "{enum}"),
ValueData::Float(float) => { ValueData::Float(float) => {
@ -1757,12 +1768,68 @@ pub enum ValueError {
CannotNot(Value), CannotNot(Value),
CannotSubtract(Value, Value), CannotSubtract(Value, Value),
CannotOr(Value, Value), CannotOr(Value, Value),
DivisionByZero,
ExpectedList(Value),
IndexOutOfBounds { value: Value, index: i64 }, IndexOutOfBounds { value: Value, index: i64 },
} }
impl Error for ValueError {} impl ValueError {
pub fn title(&self) -> &'static str {
"Value Error"
}
pub fn description(&self) -> String {
match self {
ValueError::CannotAdd(left, right) => format!("Cannot add {} and {}", left, right),
ValueError::CannotAnd(left, right) => {
format!(
"Cannot use logical \"and\" operation on {} and {}",
left, right
)
}
ValueError::CannotDivide(left, right) => {
format!("Cannot divide {} by {}", left, right)
}
ValueError::CannotGreaterThan(left, right) => {
format!("Cannot compare {} and {}", left, right)
}
ValueError::CannotGreaterThanOrEqual(left, right) => {
format!("Cannot compare {} and {}", left, right)
}
ValueError::CannotIndex { value, index } => {
format!("Cannot index {} with {}", value, index)
}
ValueError::CannotLessThan(left, right) => {
format!("Cannot compare {} and {}", left, right)
}
ValueError::CannotLessThanOrEqual(left, right) => {
format!("Cannot compare {} and {}", left, right)
}
ValueError::CannotMakeMutable => "Cannot make this value mutable".to_string(),
ValueError::CannotModulo(left, right) => {
format!("Cannot modulo {} by {}", left, right)
}
ValueError::CannotMultiply(left, right) => {
format!("Cannot multiply {} and {}", left, right)
}
ValueError::CannotMutate(value) => format!("Cannot mutate {}", value),
ValueError::CannotNegate(value) => format!("Cannot negate {}", value),
ValueError::CannotNot(value) => {
format!("Cannot use logical not operation on {}", value)
}
ValueError::CannotSubtract(left, right) => {
format!("Cannot subtract {} and {}", left, right)
}
ValueError::CannotOr(left, right) => {
format!(
"Cannot use logical \"or\" operation on {} and {}",
left, right
)
}
ValueError::IndexOutOfBounds { value, index } => {
format!("Index out of bounds: {} with index {}", value, index)
}
}
}
}
impl Display for ValueError { impl Display for ValueError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
@ -1810,11 +1877,9 @@ impl Display for ValueError {
left, right left, right
) )
} }
ValueError::DivisionByZero => write!(f, "Division by zero"),
ValueError::IndexOutOfBounds { value, index } => { ValueError::IndexOutOfBounds { value, index } => {
write!(f, "{} does not have an index of {}", value, index) write!(f, "{} does not have an index of {}", value, index)
} }
ValueError::ExpectedList(value) => write!(f, "{} is not a list", value),
} }
} }
} }

View File

@ -30,7 +30,11 @@ impl Vm {
} }
pub fn run(&mut self) -> Result<Option<Value>, VmError> { pub fn run(&mut self) -> Result<Option<Value>, VmError> {
while let Ok((byte, position)) = self.read().copied() { let mut current_postion = Span(0, 0);
while let Ok((byte, position)) = self.read(current_postion).copied() {
current_postion = position;
let instruction = Instruction::from_byte(byte) let instruction = Instruction::from_byte(byte)
.ok_or_else(|| VmError::InvalidInstruction(byte, position))?; .ok_or_else(|| VmError::InvalidInstruction(byte, position))?;
@ -38,142 +42,179 @@ impl Vm {
match instruction { match instruction {
Instruction::Constant => { Instruction::Constant => {
let (index, _) = self.read().copied()?; let (index, _) = self.read(position).copied()?;
let value = self.read_constant(index)?; let value = self.read_constant(index, position)?.clone();
self.push(value)?; self.push(value, position)?;
} }
Instruction::Return => { Instruction::Return => {
let value = self.pop()?; let value = self.pop(position)?;
return Ok(Some(value)); return Ok(Some(value));
} }
Instruction::Pop => { Instruction::Pop => {
self.pop()?; self.pop(position)?;
} }
// Variables // Variables
Instruction::DefineVariable => { Instruction::DefineVariable => {
let (index, _) = *self.read()?; let (index, _) = *self.read(position)?;
let value = self.read_constant(index)?; let value = self
.read_constant(index, position)?
.clone()
.into_reference();
self.stack.insert(index as usize, value); self.stack.insert(index as usize, value);
} }
Instruction::GetVariable => { Instruction::GetVariable => {
let (index, _) = *self.read()?; let (index, _) = *self.read(position)?;
let value = self.stack[index as usize].clone(); let value = self.stack[index as usize].clone();
self.push(value)?; self.push(value, position)?;
} }
Instruction::SetVariable => { Instruction::SetVariable => {
let (index, _) = *self.read()?; let (index, _) = *self.read(position)?;
let identifier = self.chunk.get_identifier(index)?.clone(); let identifier = self
.chunk
.get_identifier(index)
.map_err(|error| VmError::Chunk { error, position })?
.clone();
if !self.chunk.contains_identifier(&identifier) { if !self.chunk.contains_identifier(&identifier) {
return Err(VmError::UndefinedVariable(identifier, position)); return Err(VmError::UndefinedVariable(identifier, position));
} }
let value = self.pop()?; let value = self.pop(position)?;
self.stack[index as usize] = value; self.stack[index as usize] = value;
} }
// Unary // Unary
Instruction::Negate => { Instruction::Negate => {
let negated = self.pop()?.negate()?; let negated = self
.pop(position)?
.negate()
.map_err(|error| VmError::Value { error, position })?;
self.push(negated)?; self.push(negated, position)?;
} }
Instruction::Not => { Instruction::Not => {
let not = self.pop()?.not()?; let not = self
.pop(position)?
.not()
.map_err(|error| VmError::Value { error, position })?;
self.push(not)?; self.push(not, position)?;
} }
// Binary // Binary
Instruction::Add => { Instruction::Add => {
let right = self.pop()?; let right = self.pop(position)?;
let left = self.pop()?; let left = self.pop(position)?;
let sum = left.add(&right)?; let sum = left
.add(&right)
.map_err(|error| VmError::Value { error, position })?;
self.push(sum)?; self.push(sum, position)?;
} }
Instruction::Subtract => { Instruction::Subtract => {
let right = self.pop()?; let right = self.pop(position)?;
let left = self.pop()?; let left = self.pop(position)?;
let difference = left.subtract(&right)?; let difference = left
.subtract(&right)
.map_err(|error| VmError::Value { error, position })?;
self.push(difference)?; self.push(difference, position)?;
} }
Instruction::Multiply => { Instruction::Multiply => {
let right = self.pop()?; let right = self.pop(position)?;
let left = self.pop()?; let left = self.pop(position)?;
let product = left.multiply(&right)?; let product = left
.multiply(&right)
.map_err(|error| VmError::Value { error, position })?;
self.push(product)?; self.push(product, position)?;
} }
Instruction::Divide => { Instruction::Divide => {
let right = self.pop()?; let right = self.pop(position)?;
let left = self.pop()?; let left = self.pop(position)?;
let quotient = left.divide(&right)?; let quotient = left
.divide(&right)
.map_err(|error| VmError::Value { error, position })?;
self.push(quotient)?; self.push(quotient, position)?;
} }
Instruction::Greater => { Instruction::Greater => {
let right = self.pop()?; let right = self.pop(position)?;
let left = self.pop()?; let left = self.pop(position)?;
let greater = left.greater_than(&right)?; let greater = left
.greater_than(&right)
.map_err(|error| VmError::Value { error, position })?;
self.push(greater)?; self.push(greater, position)?;
} }
Instruction::Less => { Instruction::Less => {
let right = self.pop()?; let right = self.pop(position)?;
let left = self.pop()?; let left = self.pop(position)?;
let less = left.less_than(&right)?; let less = left
.less_than(&right)
.map_err(|error| VmError::Value { error, position })?;
self.push(less)?; self.push(less, position)?;
} }
Instruction::GreaterEqual => { Instruction::GreaterEqual => {
let right = self.pop()?; let right = self.pop(position)?;
let left = self.pop()?; let left = self.pop(position)?;
let greater_equal = left.greater_than_or_equal(&right)?; let greater_equal = left
.greater_than_or_equal(&right)
.map_err(|error| VmError::Value { error, position })?;
self.push(greater_equal)?; self.push(greater_equal, position)?;
} }
Instruction::LessEqual => { Instruction::LessEqual => {
let right = self.pop()?; let right = self.pop(position)?;
let left = self.pop()?; let left = self.pop(position)?;
let less_equal = left.less_than_or_equal(&right)?; let less_equal = left
.less_than_or_equal(&right)
.map_err(|error| VmError::Value { error, position })?;
self.push(less_equal)?; self.push(less_equal, position)?;
} }
Instruction::Equal => { Instruction::Equal => {
let right = self.pop()?; let right = self.pop(position)?;
let left = self.pop()?; let left = self.pop(position)?;
let equal = left.equal(&right)?; let equal = left
.equal(&right)
.map_err(|error| VmError::Value { error, position })?;
self.push(equal)?; self.push(equal, position)?;
} }
Instruction::NotEqual => { Instruction::NotEqual => {
let right = self.pop()?; let right = self.pop(position)?;
let left = self.pop()?; let left = self.pop(position)?;
let not_equal = left.not_equal(&right)?; let not_equal = left
.not_equal(&right)
.map_err(|error| VmError::Value { error, position })?;
self.push(not_equal)?; self.push(not_equal, position)?;
} }
Instruction::And => { Instruction::And => {
let right = self.pop()?; let right = self.pop(position)?;
let left = self.pop()?; let left = self.pop(position)?;
let and = left.and(&right)?; let and = left
.and(&right)
.map_err(|error| VmError::Value { error, position })?;
self.push(and)?; self.push(and, position)?;
} }
Instruction::Or => { Instruction::Or => {
let right = self.pop()?; let right = self.pop(position)?;
let left = self.pop()?; let left = self.pop(position)?;
let or = left.or(&right)?; let or = left
.or(&right)
.map_err(|error| VmError::Value { error, position })?;
self.push(or)?; self.push(or, position)?;
} }
} }
} }
@ -181,9 +222,9 @@ impl Vm {
Ok(self.stack.pop()) Ok(self.stack.pop())
} }
fn push(&mut self, value: Value) -> Result<(), VmError> { fn push(&mut self, value: Value, position: Span) -> Result<(), VmError> {
if self.stack.len() == Self::STACK_SIZE { if self.stack.len() == Self::STACK_SIZE {
Err(VmError::StackOverflow) Err(VmError::StackOverflow(position))
} else { } else {
self.stack.push(value); self.stack.push(value);
@ -191,48 +232,85 @@ impl Vm {
} }
} }
fn pop(&mut self) -> Result<Value, VmError> { fn pop(&mut self, position: Span) -> Result<Value, VmError> {
if let Some(value) = self.stack.pop() { if let Some(value) = self.stack.pop() {
Ok(value) Ok(value)
} else { } else {
Err(VmError::StackUnderflow) Err(VmError::StackUnderflow(position))
} }
} }
fn read(&mut self) -> Result<&(u8, Span), VmError> { fn read(&mut self, position: Span) -> Result<&(u8, Span), VmError> {
let current = self.chunk.get_code(self.ip)?; let current = self
.chunk
.get_code(self.ip)
.map_err(|error| VmError::Chunk { error, position })?;
self.ip += 1; self.ip += 1;
Ok(current) Ok(current)
} }
fn read_constant(&self, index: u8) -> Result<Value, VmError> { fn read_constant(&self, index: u8, position: Span) -> Result<&Value, VmError> {
Ok(self.chunk.get_constant(index)?.clone()) let value = self
.chunk
.get_constant(index)
.map_err(|error| VmError::Chunk { error, position })?;
Ok(value)
} }
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum VmError { pub enum VmError {
InvalidInstruction(u8, Span), InvalidInstruction(u8, Span),
StackUnderflow, StackOverflow(Span),
StackOverflow, StackUnderflow(Span),
UndefinedVariable(Identifier, Span), UndefinedVariable(Identifier, Span),
// Wrappers for foreign errors // Wrappers for foreign errors
Chunk(ChunkError), Chunk { error: ChunkError, position: Span },
Value(ValueError), Value { error: ValueError, position: Span },
} }
impl From<ChunkError> for VmError { impl VmError {
fn from(error: ChunkError) -> Self { pub fn chunk(error: ChunkError, position: Span) -> Self {
Self::Chunk(error) Self::Chunk { error, position }
} }
}
impl From<ValueError> for VmError { pub fn value(error: ValueError, position: Span) -> Self {
fn from(error: ValueError) -> Self { Self::Value { error, position }
Self::Value(error) }
pub fn title(&self) -> &'static str {
"VM Error"
}
pub fn description(&self) -> String {
match self {
Self::InvalidInstruction(byte, _) => {
format!("The byte {byte} does not correspond to a valid instruction")
}
Self::StackOverflow(position) => format!("Stack overflow at {position}"),
Self::StackUnderflow(position) => format!("Stack underflow at {position}"),
Self::UndefinedVariable(identifier, position) => {
format!("{identifier} is not in scope at {position}")
}
Self::Chunk { error, .. } => error.description(),
Self::Value { error, .. } => error.description(),
}
}
pub fn position(&self) -> Span {
match self {
Self::InvalidInstruction(_, position) => *position,
Self::StackUnderflow(position) => *position,
Self::StackOverflow(position) => *position,
Self::UndefinedVariable(_, position) => *position,
Self::Chunk { position, .. } => *position,
Self::Value { position, .. } => *position,
}
} }
} }

View File

@ -50,7 +50,7 @@ fn run_and_display_errors(source: &str) {
Ok(Some(value)) => println!("{}", value), Ok(Some(value)) => println!("{}", value),
Ok(_) => {} Ok(_) => {}
Err(error) => { Err(error) => {
eprintln!("{:?}", error); eprintln!("{}", error.report());
} }
} }
} }