From f936c30b4fee40bd0400c7b6fe0ca17fc4f39207 Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 10 Sep 2024 03:42:25 -0400 Subject: [PATCH] Go to great lengths to avoid cloning Values; Extend error reports --- dust-lang/src/chunk.rs | 10 ++ dust-lang/src/dust_error.rs | 16 ++- dust-lang/src/lexer.rs | 18 +++ dust-lang/src/parser.rs | 79 ++++++++++--- dust-lang/src/value.rs | 14 ++- dust-lang/src/vm.rs | 217 ++++++++++++++++++++++-------------- 6 files changed, 254 insertions(+), 100 deletions(-) diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 0569092..b5e5b1f 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -56,6 +56,16 @@ impl Chunk { .ok_or(ChunkError::ConstantIndexOutOfBounds(index)) } + pub fn remove_constant(&mut self, index: u8) -> Result { + let index = index as usize; + + if index >= self.constants.len() { + Err(ChunkError::ConstantIndexOutOfBounds(index as u8)) + } else { + Ok(self.constants.remove(index)) + } + } + pub fn push_constant(&mut self, value: Value) -> Result { let starting_length = self.constants.len(); diff --git a/dust-lang/src/dust_error.rs b/dust-lang/src/dust_error.rs index fa7ba34..ffef00e 100644 --- a/dust-lang/src/dust_error.rs +++ b/dust-lang/src/dust_error.rs @@ -26,9 +26,21 @@ impl<'src> DustError<'src> { 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( + let message = Level::Error.title(VmError::title()).snippet( + Snippet::source(source).fold(true).annotation( + Level::Error + .span(position.0..position.1) + .label(&description), + ), + ); + + report.push_str(&renderer.render(message).to_string()); + } + DustError::Parse { error, source } => { + let position = error.position(); + let description = error.description(); + let message = Level::Error.title(ParseError::title()).snippet( Snippet::source(source).fold(true).annotation( Level::Error .span(position.0..position.1) diff --git a/dust-lang/src/lexer.rs b/dust-lang/src/lexer.rs index 06c09c9..af40d0b 100644 --- a/dust-lang/src/lexer.rs +++ b/dust-lang/src/lexer.rs @@ -530,6 +530,24 @@ pub enum LexError { } impl LexError { + pub fn title() -> &'static str { + "Lex Error" + } + + pub fn description(&self) -> String { + match self { + Self::ExpectedCharacter { + expected, actual, .. + } => { + format!("Expected character \"{}\", found \"{}\"", expected, actual) + } + Self::UnexpectedCharacter { actual, .. } => { + format!("Unexpected character \"{}\"", actual) + } + Self::UnexpectedEndOfFile { .. } => "Unexpected end of file".to_string(), + } + } + pub fn position(&self) -> Span { match self { Self::ExpectedCharacter { position, .. } => Span(*position, *position), diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index 32eadee..ec58a2a 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -95,8 +95,11 @@ impl<'src> Parser<'src> { } fn emit_constant(&mut self, value: Value) -> Result<(), ParseError> { - let constant_index = self.chunk.push_constant(value)?; let position = self.previous_position; + let constant_index = self + .chunk + .push_constant(value) + .map_err(|error| ParseError::Chunk { error, position })?; self.emit_byte(Instruction::Constant as u8, position); self.emit_byte(constant_index, position); @@ -117,7 +120,11 @@ impl<'src> Parser<'src> { fn parse_byte(&mut self, _allow_assignment: bool) -> Result<(), ParseError> { if let Token::Byte(text) = self.previous_token { - let byte = u8::from_str_radix(&text[2..], 16)?; + let byte = + u8::from_str_radix(&text[2..], 16).map_err(|error| ParseError::ParseIntError { + error, + position: self.previous_position, + })?; let value = Value::byte(byte); self.emit_constant(value)?; @@ -249,7 +256,13 @@ impl<'src> Parser<'src> { fn parse_identifier_from(&mut self, token: TokenOwned) -> Result { if let TokenOwned::Identifier(text) = token { let identifier = Identifier::new(text); - let identifier_index = self.chunk.get_identifier_index(&identifier)?; + let identifier_index = + self.chunk + .get_identifier_index(&identifier) + .map_err(|error| ParseError::Chunk { + error, + position: self.previous_position, + })?; Ok(identifier_index) } else { @@ -300,7 +313,9 @@ impl<'src> Parser<'src> { let identifier = Identifier::new(text); - self.chunk.push_identifier(identifier)? + self.chunk + .push_identifier(identifier) + .map_err(|error| ParseError::Chunk { error, position })? } else { return Err(ParseError::ExpectedToken { expected: TokenKind::Identifier, @@ -566,14 +581,54 @@ pub enum ParseError { }, // Wrappers around foreign errors - Chunk(ChunkError), + Chunk { + error: ChunkError, + position: Span, + }, Lex(LexError), - ParseIntError(ParseIntError), + ParseIntError { + error: ParseIntError, + position: Span, + }, } -impl From for ParseError { - fn from(error: ParseIntError) -> Self { - Self::ParseIntError(error) +impl ParseError { + pub fn title() -> &'static str { + "Parse Error" + } + + pub fn description(&self) -> String { + match self { + Self::ExpectedExpression { found, .. } => { + format!("Expected an expression, found \"{found}\"") + } + Self::ExpectedToken { + expected, found, .. + } => { + format!("Expected \"{expected}\", found \"{found}\"") + } + Self::ExpectedTokenMultiple { + expected, found, .. + } => format!("Expected one of {:?}, found \"{found}\"", expected,), + Self::InvalidAssignmentTarget { found, .. } => { + format!("Invalid assignment target \"{found}\"") + } + Self::Chunk { error, .. } => error.description(), + Self::Lex(error) => error.description(), + Self::ParseIntError { error, .. } => error.to_string(), + } + } + + pub fn position(&self) -> Span { + match self { + Self::ExpectedExpression { position, .. } => *position, + Self::ExpectedToken { position, .. } => *position, + Self::ExpectedTokenMultiple { position, .. } => *position, + Self::InvalidAssignmentTarget { position, .. } => *position, + Self::Chunk { position, .. } => *position, + Self::Lex(error) => error.position(), + Self::ParseIntError { position, .. } => *position, + } } } @@ -583,12 +638,6 @@ impl From for ParseError { } } -impl From for ParseError { - fn from(error: ChunkError) -> Self { - Self::Chunk(error) - } -} - #[cfg(test)] mod tests { use crate::identifier_stack::Local; diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index be861ec..ef92c69 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -46,7 +46,7 @@ use crate::{EnumType, FunctionType, Identifier, RangeableType, StructType, Type} /// /// assert_eq!(value.r#type(), Type::Integer); /// ``` -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum Value { Raw(ValueData), Reference(Arc), @@ -794,6 +794,18 @@ impl From<&str> for Value { } } +impl Clone for Value { + fn clone(&self) -> Self { + log::trace!("Cloning value: {:?}", self); + + match self { + Value::Raw(data) => Value::Raw(data.clone()), + Value::Reference(data) => Value::Reference(data.clone()), + Value::Mutable(data) => Value::Mutable(data.clone()), + } + } +} + impl Eq for Value {} impl PartialEq for Value { diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index bf27ee2..fb3f633 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use crate::{ parse, Chunk, ChunkError, DustError, Identifier, Instruction, Span, Value, ValueError, }; @@ -13,9 +15,9 @@ pub fn run(source: &str) -> Result, DustError> { #[derive(Debug, Clone, Eq, PartialEq)] pub struct Vm { - chunk: Chunk, + chunk: Rc, ip: usize, - stack: Vec, + stack: Vec, } impl Vm { @@ -23,17 +25,17 @@ impl Vm { pub fn new(chunk: Chunk) -> Self { Self { - chunk, + chunk: Rc::new(chunk), ip: 0, stack: Vec::with_capacity(Self::STACK_SIZE), } } pub fn run(&mut self) -> Result, VmError> { - let mut current_postion = Span(0, 0); + let mut current_position = Span(0, 0); - while let Ok((byte, position)) = self.read(current_postion).copied() { - current_postion = position; + while let Ok((byte, position)) = self.read(current_position).copied() { + current_position = position; let instruction = Instruction::from_byte(byte) .ok_or_else(|| VmError::InvalidInstruction(byte, position))?; @@ -43,12 +45,11 @@ impl Vm { match instruction { Instruction::Constant => { let (index, _) = self.read(position).copied()?; - let value = self.read_constant(index, position)?.clone(); - self.push(value, position)?; + self.push_constant_value(index, position)?; } Instruction::Return => { - let value = self.pop(position)?; + let value = self.pop(position)?.resolve(&self.chunk, position)?.clone(); return Ok(Some(value)); } @@ -59,18 +60,14 @@ impl Vm { // Variables Instruction::DefineVariable => { let (index, _) = *self.read(position)?; - let value = self - .read_constant(index, position)? - .clone() - .into_reference(); - self.stack.insert(index as usize, value); + self.stack + .insert(index as usize, StackedValue::Constant(index)); } Instruction::GetVariable => { let (index, _) = *self.read(position)?; - let value = self.stack[index as usize].clone(); - self.push(value, position)?; + self.push_constant_value(index, position)?; } Instruction::SetVariable => { let (index, _) = *self.read(position)?; @@ -84,157 +81,205 @@ impl Vm { return Err(VmError::UndefinedVariable(identifier, position)); } - let value = self.pop(position)?; + let stacked = self.pop(position)?; - self.stack[index as usize] = value; + self.stack[index as usize] = stacked; } // Unary Instruction::Negate => { let negated = self .pop(position)? + .resolve(&self.chunk, position)? .negate() .map_err(|error| VmError::Value { error, position })?; - self.push(negated, position)?; + self.push_runtime_value(negated, position)?; } Instruction::Not => { let not = self .pop(position)? + .resolve(&self.chunk, position)? .not() .map_err(|error| VmError::Value { error, position })?; - self.push(not, position)?; + self.push_runtime_value(not, position)?; } // Binary Instruction::Add => { - let right = self.pop(position)?; - let left = self.pop(position)?; + let chunk = self.chunk.clone(); + let right_stacked = self.pop(position)?; + let right = right_stacked.resolve(chunk.as_ref(), position)?; + let left_stacked = self.pop(position)?; + let left = left_stacked.resolve(&self.chunk, position)?; let sum = left - .add(&right) + .add(right) .map_err(|error| VmError::Value { error, position })?; - self.push(sum, position)?; + self.push_runtime_value(sum, position)?; } Instruction::Subtract => { - let right = self.pop(position)?; - let left = self.pop(position)?; + let chunk = self.chunk.clone(); + let right_stacked = self.pop(position)?; + let right = right_stacked.resolve(chunk.as_ref(), position)?; + let left_stacked = self.pop(position)?; + let left = left_stacked.resolve(&self.chunk, position)?; let difference = left - .subtract(&right) + .subtract(right) .map_err(|error| VmError::Value { error, position })?; - self.push(difference, position)?; + self.push_runtime_value(difference, position)?; } Instruction::Multiply => { - let right = self.pop(position)?; - let left = self.pop(position)?; + let chunk = self.chunk.clone(); + let right_stacked = self.pop(position)?; + let right = right_stacked.resolve(chunk.as_ref(), position)?; + let left_stacked = self.pop(position)?; + let left = left_stacked.resolve(&self.chunk, position)?; let product = left - .multiply(&right) + .multiply(right) .map_err(|error| VmError::Value { error, position })?; - self.push(product, position)?; + self.push_runtime_value(product, position)?; } Instruction::Divide => { - let right = self.pop(position)?; - let left = self.pop(position)?; + let chunk = self.chunk.clone(); + let right_stacked = self.pop(position)?; + let right = right_stacked.resolve(chunk.as_ref(), position)?; + let left_stacked = self.pop(position)?; + let left = left_stacked.resolve(&self.chunk, position)?; let quotient = left - .divide(&right) + .divide(right) .map_err(|error| VmError::Value { error, position })?; - self.push(quotient, position)?; + self.push_runtime_value(quotient, position)?; } Instruction::Greater => { - let right = self.pop(position)?; - let left = self.pop(position)?; + let chunk = self.chunk.clone(); + let right_stacked = self.pop(position)?; + let right = right_stacked.resolve(chunk.as_ref(), position)?; + let left_stacked = self.pop(position)?; + let left = left_stacked.resolve(&self.chunk, position)?; let greater = left - .greater_than(&right) + .greater_than(right) .map_err(|error| VmError::Value { error, position })?; - self.push(greater, position)?; + self.push_runtime_value(greater, position)?; } Instruction::Less => { - let right = self.pop(position)?; - let left = self.pop(position)?; + let chunk = self.chunk.clone(); + let right_stacked = self.pop(position)?; + let right = right_stacked.resolve(chunk.as_ref(), position)?; + let left_stacked = self.pop(position)?; + let left = left_stacked.resolve(&self.chunk, position)?; let less = left - .less_than(&right) + .less_than(right) .map_err(|error| VmError::Value { error, position })?; - self.push(less, position)?; + self.push_runtime_value(less, position)?; } Instruction::GreaterEqual => { - let right = self.pop(position)?; - let left = self.pop(position)?; + let chunk = self.chunk.clone(); + let right_stacked = self.pop(position)?; + let right = right_stacked.resolve(chunk.as_ref(), position)?; + let left_stacked = self.pop(position)?; + let left = left_stacked.resolve(&self.chunk, position)?; let greater_equal = left - .greater_than_or_equal(&right) + .greater_than_or_equal(right) .map_err(|error| VmError::Value { error, position })?; - self.push(greater_equal, position)?; + self.push_runtime_value(greater_equal, position)?; } Instruction::LessEqual => { - let right = self.pop(position)?; - let left = self.pop(position)?; + let chunk = self.chunk.clone(); + let right_stacked = self.pop(position)?; + let right = right_stacked.resolve(chunk.as_ref(), position)?; + let left_stacked = self.pop(position)?; + let left = left_stacked.resolve(&self.chunk, position)?; let less_equal = left - .less_than_or_equal(&right) + .less_than_or_equal(right) .map_err(|error| VmError::Value { error, position })?; - self.push(less_equal, position)?; + self.push_runtime_value(less_equal, position)?; } Instruction::Equal => { - let right = self.pop(position)?; - let left = self.pop(position)?; + let chunk = self.chunk.clone(); + let right_stacked = self.pop(position)?; + let right = right_stacked.resolve(chunk.as_ref(), position)?; + let left_stacked = self.pop(position)?; + let left = left_stacked.resolve(&self.chunk, position)?; let equal = left - .equal(&right) + .equal(right) .map_err(|error| VmError::Value { error, position })?; - self.push(equal, position)?; + self.push_runtime_value(equal, position)?; } Instruction::NotEqual => { - let right = self.pop(position)?; - let left = self.pop(position)?; + let chunk = self.chunk.clone(); + let right_stacked = self.pop(position)?; + let right = right_stacked.resolve(chunk.as_ref(), position)?; + let left_stacked = self.pop(position)?; + let left = left_stacked.resolve(&self.chunk, position)?; let not_equal = left - .not_equal(&right) + .not_equal(right) .map_err(|error| VmError::Value { error, position })?; - self.push(not_equal, position)?; + self.push_runtime_value(not_equal, position)?; } Instruction::And => { - let right = self.pop(position)?; - let left = self.pop(position)?; + let chunk = self.chunk.clone(); + let right_stacked = self.pop(position)?; + let right = right_stacked.resolve(chunk.as_ref(), position)?; + let left_stacked = self.pop(position)?; + let left = left_stacked.resolve(&self.chunk, position)?; let and = left - .and(&right) + .and(right) .map_err(|error| VmError::Value { error, position })?; - self.push(and, position)?; + self.push_runtime_value(and, position)?; } Instruction::Or => { - let right = self.pop(position)?; - let left = self.pop(position)?; + let chunk = self.chunk.clone(); + let right_stacked = self.pop(position)?; + let right = right_stacked.resolve(chunk.as_ref(), position)?; + let left_stacked = self.pop(position)?; + let left = left_stacked.resolve(&self.chunk, position)?; let or = left - .or(&right) + .or(right) .map_err(|error| VmError::Value { error, position })?; - self.push(or, position)?; + self.push_runtime_value(or, position)?; } } } - Ok(self.stack.pop()) + Ok(None) } - fn push(&mut self, value: Value, position: Span) -> Result<(), VmError> { + fn push_runtime_value(&mut self, value: Value, position: Span) -> Result<(), VmError> { if self.stack.len() == Self::STACK_SIZE { Err(VmError::StackOverflow(position)) } else { - self.stack.push(value); + self.stack.push(StackedValue::Runtime(value)); Ok(()) } } - fn pop(&mut self, position: Span) -> Result { - if let Some(value) = self.stack.pop() { - Ok(value) + fn push_constant_value(&mut self, index: u8, position: Span) -> Result<(), VmError> { + if self.stack.len() == Self::STACK_SIZE { + Err(VmError::StackOverflow(position)) + } else { + self.stack.push(StackedValue::Constant(index)); + + Ok(()) + } + } + + fn pop(&mut self, position: Span) -> Result { + if let Some(stacked) = self.stack.pop() { + Ok(stacked) } else { Err(VmError::StackUnderflow(position)) } @@ -250,14 +295,22 @@ impl Vm { Ok(current) } +} - 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 })?; +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum StackedValue { + Runtime(Value), + Constant(u8), +} - Ok(value) +impl StackedValue { + fn resolve<'a>(&'a self, chunk: &'a Chunk, position: Span) -> Result<&'a Value, VmError> { + match self { + Self::Runtime(value) => Ok(value), + Self::Constant(index) => chunk + .get_constant(*index) + .map_err(|error| VmError::Chunk { error, position }), + } } } @@ -282,8 +335,8 @@ impl VmError { Self::Value { error, position } } - pub fn title(&self) -> &'static str { - "VM Error" + pub fn title() -> &'static str { + "Runtime Error" } pub fn description(&self) -> String {