From 8798efc0afeb27b3e99e8ceb62a78d5347189726 Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 10 Sep 2024 01:04:30 -0400 Subject: [PATCH] Add error reports and byte operations --- dust-lang/src/chunk.rs | 33 ++++- dust-lang/src/dust_error.rs | 29 +++++ dust-lang/src/parser.rs | 42 +++--- dust-lang/src/value.rs | 79 +++++++++++- dust-lang/src/vm.rs | 246 ++++++++++++++++++++++++------------ dust-shell/src/main.rs | 2 +- 6 files changed, 315 insertions(+), 116 deletions(-) diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 63f31d0..0569092 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -43,7 +43,7 @@ impl Chunk { pub fn get_code(&self, offset: usize) -> Result<&(u8, Span), ChunkError> { self.code .get(offset) - .ok_or(ChunkError::CodeIndextOfBounds(offset)) + .ok_or(ChunkError::CodeIndexOfBounds(offset)) } pub fn push_code(&mut self, instruction: u8, position: Span) { @@ -169,22 +169,47 @@ impl Default for Chunk { impl Display for Chunk { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", self.disassemble("Chunk")) + write!(f, "{}", self.disassemble("Chunk Disassembly")) } } impl Debug for Chunk { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", self.disassemble("Chunk")) + write!(f, "{}", self.disassemble("Chunk Disassembly")) } } #[derive(Debug, Clone, PartialEq)] pub enum ChunkError { - CodeIndextOfBounds(usize), + CodeIndexOfBounds(usize), ConstantOverflow, ConstantIndexOutOfBounds(u8), IdentifierIndexOutOfBounds(u8), IdentifierOverflow, 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) + } + } + } +} diff --git a/dust-lang/src/dust_error.rs b/dust-lang/src/dust_error.rs index eaf2c6c..fa7ba34 100644 --- a/dust-lang/src/dust_error.rs +++ b/dust-lang/src/dust_error.rs @@ -1,3 +1,5 @@ +use annotate_snippets::{Level, Renderer, Snippet}; + use crate::{vm::VmError, LexError, ParseError}; #[derive(Debug, PartialEq)] @@ -15,3 +17,30 @@ pub enum DustError<'src> { 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 + } +} diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index f97005b..32eadee 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -206,6 +206,7 @@ impl<'src> Parser<'src> { TokenKind::Minus => Instruction::Subtract as u8, TokenKind::Star => Instruction::Multiply as u8, TokenKind::Slash => Instruction::Divide as u8, + TokenKind::DoubleAmpersand => Instruction::And as u8, _ => { return Err(ParseError::ExpectedTokenMultiple { expected: vec![ @@ -319,31 +320,28 @@ impl<'src> Parser<'src> { fn parse(&mut self, precedence: Precedence) -> Result<(), ParseError> { self.advance()?; - let prefix_rule = if let Some(prefix) = ParseRule::from(&self.previous_token.kind()).prefix - { - log::trace!( - "Parsing {} as prefix with precedence {precedence}", - self.previous_token, - ); - - prefix - } else { - return Err(ParseError::ExpectedExpression { - found: self.previous_token.to_owned(), - position: self.previous_position, - }); - }; + let prefix_parser = + if let Some(prefix) = ParseRule::from(&self.previous_token.kind()).prefix { + log::trace!( + "Parsing {} as prefix with precedence {precedence}", + self.previous_token, + ); + prefix + } else { + return Err(ParseError::ExpectedExpression { + found: self.previous_token.to_owned(), + position: self.previous_position, + }); + }; 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 { self.advance()?; - let infix_rule = ParseRule::from(&self.previous_token.kind()).infix; - - if let Some(infix) = infix_rule { + if let Some(infix_parser) = ParseRule::from(&self.previous_token.kind()).infix { log::trace!( "Parsing {} as infix with precedence {precedence}", self.previous_token, @@ -356,7 +354,7 @@ impl<'src> Parser<'src> { }); } - infix(self)?; + infix_parser(self)?; } else { break; } @@ -485,7 +483,11 @@ impl From<&TokenKind> for ParseRule<'_> { TokenKind::Colon => todo!(), TokenKind::Comma => todo!(), TokenKind::Dot => todo!(), - TokenKind::DoubleAmpersand => todo!(), + TokenKind::DoubleAmpersand => ParseRule { + prefix: None, + infix: Some(Parser::parse_binary), + precedence: Precedence::LogicalAnd, + }, TokenKind::DoubleDot => todo!(), TokenKind::DoubleEqual => todo!(), TokenKind::DoublePipe => todo!(), diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index 1fac1b4..858f512 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -2,7 +2,6 @@ use std::{ cmp::Ordering, collections::HashMap, - error::Error, fmt::{self, Display, Formatter}, ops::{Range, RangeInclusive}, sync::{Arc, RwLock}, @@ -1006,6 +1005,9 @@ impl ValueData { pub fn add(&self, other: &ValueData) -> Option { match (self, other) { + (ValueData::Byte(left), ValueData::Byte(right)) => { + Some(ValueData::Byte(left.saturating_add(*right))) + } (ValueData::Float(left), ValueData::Float(right)) => { Some(ValueData::Float(left + right)) } @@ -1021,6 +1023,9 @@ impl ValueData { pub fn subtract(&self, other: &ValueData) -> Option { match (self, other) { + (ValueData::Byte(left), ValueData::Byte(right)) => { + Some(ValueData::Byte(left.saturating_sub(*right))) + } (ValueData::Float(left), ValueData::Float(right)) => { Some(ValueData::Float(left - right)) } @@ -1033,6 +1038,9 @@ impl ValueData { pub fn multiply(&self, other: &ValueData) -> Option { match (self, other) { + (ValueData::Byte(left), ValueData::Byte(right)) => { + Some(ValueData::Byte(left.saturating_mul(*right))) + } (ValueData::Float(left), ValueData::Float(right)) => { Some(ValueData::Float(left * right)) } @@ -1045,6 +1053,9 @@ impl ValueData { pub fn divide(&self, other: &ValueData) -> Option { match (self, other) { + (ValueData::Byte(left), ValueData::Byte(right)) => { + Some(ValueData::Byte(left.saturating_div(*right))) + } (ValueData::Float(left), ValueData::Float(right)) => { Some(ValueData::Float(left / right)) } @@ -1170,7 +1181,7 @@ impl Display for ValueData { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { 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::Enum(r#enum) => write!(f, "{enum}"), ValueData::Float(float) => { @@ -1757,12 +1768,68 @@ pub enum ValueError { CannotNot(Value), CannotSubtract(Value, Value), CannotOr(Value, Value), - DivisionByZero, - ExpectedList(Value), 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 { fn fmt(&self, f: &mut Formatter) -> fmt::Result { @@ -1810,11 +1877,9 @@ impl Display for ValueError { left, right ) } - ValueError::DivisionByZero => write!(f, "Division by zero"), ValueError::IndexOutOfBounds { value, index } => { write!(f, "{} does not have an index of {}", value, index) } - ValueError::ExpectedList(value) => write!(f, "{} is not a list", value), } } } diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 188ab71..bf27ee2 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -30,7 +30,11 @@ impl Vm { } pub fn run(&mut self) -> Result, 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) .ok_or_else(|| VmError::InvalidInstruction(byte, position))?; @@ -38,142 +42,179 @@ impl Vm { match instruction { Instruction::Constant => { - let (index, _) = self.read().copied()?; - let value = self.read_constant(index)?; + let (index, _) = self.read(position).copied()?; + let value = self.read_constant(index, position)?.clone(); - self.push(value)?; + self.push(value, position)?; } Instruction::Return => { - let value = self.pop()?; + let value = self.pop(position)?; return Ok(Some(value)); } Instruction::Pop => { - self.pop()?; + self.pop(position)?; } // Variables Instruction::DefineVariable => { - let (index, _) = *self.read()?; - let value = self.read_constant(index)?; + let (index, _) = *self.read(position)?; + let value = self + .read_constant(index, position)? + .clone() + .into_reference(); self.stack.insert(index as usize, value); } Instruction::GetVariable => { - let (index, _) = *self.read()?; + let (index, _) = *self.read(position)?; let value = self.stack[index as usize].clone(); - self.push(value)?; + self.push(value, position)?; } Instruction::SetVariable => { - let (index, _) = *self.read()?; - let identifier = self.chunk.get_identifier(index)?.clone(); + let (index, _) = *self.read(position)?; + let identifier = self + .chunk + .get_identifier(index) + .map_err(|error| VmError::Chunk { error, position })? + .clone(); if !self.chunk.contains_identifier(&identifier) { return Err(VmError::UndefinedVariable(identifier, position)); } - let value = self.pop()?; + let value = self.pop(position)?; self.stack[index as usize] = value; } // Unary 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 => { - 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 Instruction::Add => { - let right = self.pop()?; - let left = self.pop()?; - let sum = left.add(&right)?; + let right = self.pop(position)?; + let left = self.pop(position)?; + let sum = left + .add(&right) + .map_err(|error| VmError::Value { error, position })?; - self.push(sum)?; + self.push(sum, position)?; } Instruction::Subtract => { - let right = self.pop()?; - let left = self.pop()?; - let difference = left.subtract(&right)?; + let right = self.pop(position)?; + let left = self.pop(position)?; + let difference = left + .subtract(&right) + .map_err(|error| VmError::Value { error, position })?; - self.push(difference)?; + self.push(difference, position)?; } Instruction::Multiply => { - let right = self.pop()?; - let left = self.pop()?; - let product = left.multiply(&right)?; + let right = self.pop(position)?; + let left = self.pop(position)?; + let product = left + .multiply(&right) + .map_err(|error| VmError::Value { error, position })?; - self.push(product)?; + self.push(product, position)?; } Instruction::Divide => { - let right = self.pop()?; - let left = self.pop()?; - let quotient = left.divide(&right)?; + let right = self.pop(position)?; + let left = self.pop(position)?; + let quotient = left + .divide(&right) + .map_err(|error| VmError::Value { error, position })?; - self.push(quotient)?; + self.push(quotient, position)?; } Instruction::Greater => { - let right = self.pop()?; - let left = self.pop()?; - let greater = left.greater_than(&right)?; + let right = self.pop(position)?; + let left = self.pop(position)?; + let greater = left + .greater_than(&right) + .map_err(|error| VmError::Value { error, position })?; - self.push(greater)?; + self.push(greater, position)?; } Instruction::Less => { - let right = self.pop()?; - let left = self.pop()?; - let less = left.less_than(&right)?; + let right = self.pop(position)?; + let left = self.pop(position)?; + let less = left + .less_than(&right) + .map_err(|error| VmError::Value { error, position })?; - self.push(less)?; + self.push(less, position)?; } Instruction::GreaterEqual => { - let right = self.pop()?; - let left = self.pop()?; - let greater_equal = left.greater_than_or_equal(&right)?; + let right = self.pop(position)?; + let left = self.pop(position)?; + 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 => { - let right = self.pop()?; - let left = self.pop()?; - let less_equal = left.less_than_or_equal(&right)?; + let right = self.pop(position)?; + let left = self.pop(position)?; + 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 => { - let right = self.pop()?; - let left = self.pop()?; - let equal = left.equal(&right)?; + let right = self.pop(position)?; + let left = self.pop(position)?; + let equal = left + .equal(&right) + .map_err(|error| VmError::Value { error, position })?; - self.push(equal)?; + self.push(equal, position)?; } Instruction::NotEqual => { - let right = self.pop()?; - let left = self.pop()?; - let not_equal = left.not_equal(&right)?; + let right = self.pop(position)?; + let left = self.pop(position)?; + 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 => { - let right = self.pop()?; - let left = self.pop()?; - let and = left.and(&right)?; + let right = self.pop(position)?; + let left = self.pop(position)?; + let and = left + .and(&right) + .map_err(|error| VmError::Value { error, position })?; - self.push(and)?; + self.push(and, position)?; } Instruction::Or => { - let right = self.pop()?; - let left = self.pop()?; - let or = left.or(&right)?; + let right = self.pop(position)?; + let left = self.pop(position)?; + 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()) } - 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 { - Err(VmError::StackOverflow) + Err(VmError::StackOverflow(position)) } else { self.stack.push(value); @@ -191,48 +232,85 @@ impl Vm { } } - fn pop(&mut self) -> Result { + fn pop(&mut self, position: Span) -> Result { if let Some(value) = self.stack.pop() { Ok(value) } else { - Err(VmError::StackUnderflow) + Err(VmError::StackUnderflow(position)) } } - fn read(&mut self) -> Result<&(u8, Span), VmError> { - let current = self.chunk.get_code(self.ip)?; + fn read(&mut self, position: Span) -> Result<&(u8, Span), VmError> { + let current = self + .chunk + .get_code(self.ip) + .map_err(|error| VmError::Chunk { error, position })?; self.ip += 1; Ok(current) } - fn read_constant(&self, index: u8) -> Result { - Ok(self.chunk.get_constant(index)?.clone()) + fn read_constant(&self, index: u8, position: Span) -> Result<&Value, VmError> { + let value = self + .chunk + .get_constant(index) + .map_err(|error| VmError::Chunk { error, position })?; + + Ok(value) } } #[derive(Debug, Clone, PartialEq)] pub enum VmError { InvalidInstruction(u8, Span), - StackUnderflow, - StackOverflow, + StackOverflow(Span), + StackUnderflow(Span), UndefinedVariable(Identifier, Span), // Wrappers for foreign errors - Chunk(ChunkError), - Value(ValueError), + Chunk { error: ChunkError, position: Span }, + Value { error: ValueError, position: Span }, } -impl From for VmError { - fn from(error: ChunkError) -> Self { - Self::Chunk(error) +impl VmError { + pub fn chunk(error: ChunkError, position: Span) -> Self { + Self::Chunk { error, position } } -} -impl From for VmError { - fn from(error: ValueError) -> Self { - Self::Value(error) + pub fn value(error: ValueError, position: Span) -> Self { + Self::Value { error, position } + } + + 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, + } } } diff --git a/dust-shell/src/main.rs b/dust-shell/src/main.rs index ffea2ad..5f43c23 100644 --- a/dust-shell/src/main.rs +++ b/dust-shell/src/main.rs @@ -50,7 +50,7 @@ fn run_and_display_errors(source: &str) { Ok(Some(value)) => println!("{}", value), Ok(_) => {} Err(error) => { - eprintln!("{:?}", error); + eprintln!("{}", error.report()); } } }