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> {
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)
}
}
}
}

View File

@ -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
}
}

View File

@ -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,8 +320,8 @@ 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
{
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,
@ -333,17 +334,14 @@ impl<'src> Parser<'src> {
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!(),

View File

@ -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<ValueData> {
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<ValueData> {
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<ValueData> {
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<ValueData> {
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),
}
}
}

View File

@ -30,7 +30,11 @@ impl Vm {
}
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)
.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<Value, VmError> {
fn pop(&mut self, position: Span) -> Result<Value, VmError> {
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<Value, VmError> {
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<ChunkError> 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<ValueError> 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,
}
}
}

View File

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