Go to great lengths to avoid cloning Values; Extend error reports

This commit is contained in:
Jeff 2024-09-10 03:42:25 -04:00
parent 0ed2733991
commit f936c30b4f
6 changed files with 254 additions and 100 deletions

View File

@ -56,6 +56,16 @@ impl Chunk {
.ok_or(ChunkError::ConstantIndexOutOfBounds(index)) .ok_or(ChunkError::ConstantIndexOutOfBounds(index))
} }
pub fn remove_constant(&mut self, index: u8) -> Result<Value, ChunkError> {
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<u8, ChunkError> { pub fn push_constant(&mut self, value: Value) -> Result<u8, ChunkError> {
let starting_length = self.constants.len(); let starting_length = self.constants.len();

View File

@ -26,9 +26,21 @@ impl<'src> DustError<'src> {
match self { match self {
DustError::Runtime { error, source } => { DustError::Runtime { error, source } => {
let position = error.position(); let position = error.position();
let label = format!("Runtime {}", error.title());
let description = error.description(); 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( Snippet::source(source).fold(true).annotation(
Level::Error Level::Error
.span(position.0..position.1) .span(position.0..position.1)

View File

@ -530,6 +530,24 @@ pub enum LexError {
} }
impl 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 { pub fn position(&self) -> Span {
match self { match self {
Self::ExpectedCharacter { position, .. } => Span(*position, *position), Self::ExpectedCharacter { position, .. } => Span(*position, *position),

View File

@ -95,8 +95,11 @@ impl<'src> Parser<'src> {
} }
fn emit_constant(&mut self, value: Value) -> Result<(), ParseError> { fn emit_constant(&mut self, value: Value) -> Result<(), ParseError> {
let constant_index = self.chunk.push_constant(value)?;
let position = self.previous_position; 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(Instruction::Constant as u8, position);
self.emit_byte(constant_index, 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> { fn parse_byte(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
if let Token::Byte(text) = self.previous_token { 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); let value = Value::byte(byte);
self.emit_constant(value)?; self.emit_constant(value)?;
@ -249,7 +256,13 @@ impl<'src> Parser<'src> {
fn parse_identifier_from(&mut self, token: TokenOwned) -> Result<u8, ParseError> { fn parse_identifier_from(&mut self, token: TokenOwned) -> Result<u8, ParseError> {
if let TokenOwned::Identifier(text) = token { if let TokenOwned::Identifier(text) = token {
let identifier = Identifier::new(text); 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) Ok(identifier_index)
} else { } else {
@ -300,7 +313,9 @@ impl<'src> Parser<'src> {
let identifier = Identifier::new(text); let identifier = Identifier::new(text);
self.chunk.push_identifier(identifier)? self.chunk
.push_identifier(identifier)
.map_err(|error| ParseError::Chunk { error, position })?
} else { } else {
return Err(ParseError::ExpectedToken { return Err(ParseError::ExpectedToken {
expected: TokenKind::Identifier, expected: TokenKind::Identifier,
@ -566,14 +581,54 @@ pub enum ParseError {
}, },
// Wrappers around foreign errors // Wrappers around foreign errors
Chunk(ChunkError), Chunk {
error: ChunkError,
position: Span,
},
Lex(LexError), Lex(LexError),
ParseIntError(ParseIntError), ParseIntError {
error: ParseIntError,
position: Span,
},
} }
impl From<ParseIntError> for ParseError { impl ParseError {
fn from(error: ParseIntError) -> Self { pub fn title() -> &'static str {
Self::ParseIntError(error) "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<LexError> for ParseError {
} }
} }
impl From<ChunkError> for ParseError {
fn from(error: ChunkError) -> Self {
Self::Chunk(error)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::identifier_stack::Local; use crate::identifier_stack::Local;

View File

@ -46,7 +46,7 @@ use crate::{EnumType, FunctionType, Identifier, RangeableType, StructType, Type}
/// ///
/// assert_eq!(value.r#type(), Type::Integer); /// assert_eq!(value.r#type(), Type::Integer);
/// ``` /// ```
#[derive(Debug, Clone)] #[derive(Debug)]
pub enum Value { pub enum Value {
Raw(ValueData), Raw(ValueData),
Reference(Arc<ValueData>), Reference(Arc<ValueData>),
@ -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 Eq for Value {}
impl PartialEq for Value { impl PartialEq for Value {

View File

@ -1,3 +1,5 @@
use std::rc::Rc;
use crate::{ use crate::{
parse, Chunk, ChunkError, DustError, Identifier, Instruction, Span, Value, ValueError, parse, Chunk, ChunkError, DustError, Identifier, Instruction, Span, Value, ValueError,
}; };
@ -13,9 +15,9 @@ pub fn run(source: &str) -> Result<Option<Value>, DustError> {
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub struct Vm { pub struct Vm {
chunk: Chunk, chunk: Rc<Chunk>,
ip: usize, ip: usize,
stack: Vec<Value>, stack: Vec<StackedValue>,
} }
impl Vm { impl Vm {
@ -23,17 +25,17 @@ impl Vm {
pub fn new(chunk: Chunk) -> Self { pub fn new(chunk: Chunk) -> Self {
Self { Self {
chunk, chunk: Rc::new(chunk),
ip: 0, ip: 0,
stack: Vec::with_capacity(Self::STACK_SIZE), stack: Vec::with_capacity(Self::STACK_SIZE),
} }
} }
pub fn run(&mut self) -> Result<Option<Value>, VmError> { pub fn run(&mut self) -> Result<Option<Value>, 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() { while let Ok((byte, position)) = self.read(current_position).copied() {
current_postion = position; current_position = 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))?;
@ -43,12 +45,11 @@ impl Vm {
match instruction { match instruction {
Instruction::Constant => { Instruction::Constant => {
let (index, _) = self.read(position).copied()?; 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 => { Instruction::Return => {
let value = self.pop(position)?; let value = self.pop(position)?.resolve(&self.chunk, position)?.clone();
return Ok(Some(value)); return Ok(Some(value));
} }
@ -59,18 +60,14 @@ impl Vm {
// Variables // Variables
Instruction::DefineVariable => { Instruction::DefineVariable => {
let (index, _) = *self.read(position)?; 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 => { Instruction::GetVariable => {
let (index, _) = *self.read(position)?; 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 => { Instruction::SetVariable => {
let (index, _) = *self.read(position)?; let (index, _) = *self.read(position)?;
@ -84,157 +81,205 @@ impl Vm {
return Err(VmError::UndefinedVariable(identifier, position)); 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 // Unary
Instruction::Negate => { Instruction::Negate => {
let negated = self let negated = self
.pop(position)? .pop(position)?
.resolve(&self.chunk, position)?
.negate() .negate()
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push(negated, position)?; self.push_runtime_value(negated, position)?;
} }
Instruction::Not => { Instruction::Not => {
let not = self let not = self
.pop(position)? .pop(position)?
.resolve(&self.chunk, position)?
.not() .not()
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push(not, position)?; self.push_runtime_value(not, position)?;
} }
// Binary // Binary
Instruction::Add => { Instruction::Add => {
let right = self.pop(position)?; let chunk = self.chunk.clone();
let left = self.pop(position)?; 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 let sum = left
.add(&right) .add(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push(sum, position)?; self.push_runtime_value(sum, position)?;
} }
Instruction::Subtract => { Instruction::Subtract => {
let right = self.pop(position)?; let chunk = self.chunk.clone();
let left = self.pop(position)?; 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 let difference = left
.subtract(&right) .subtract(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push(difference, position)?; self.push_runtime_value(difference, position)?;
} }
Instruction::Multiply => { Instruction::Multiply => {
let right = self.pop(position)?; let chunk = self.chunk.clone();
let left = self.pop(position)?; 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 let product = left
.multiply(&right) .multiply(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push(product, position)?; self.push_runtime_value(product, position)?;
} }
Instruction::Divide => { Instruction::Divide => {
let right = self.pop(position)?; let chunk = self.chunk.clone();
let left = self.pop(position)?; 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 let quotient = left
.divide(&right) .divide(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push(quotient, position)?; self.push_runtime_value(quotient, position)?;
} }
Instruction::Greater => { Instruction::Greater => {
let right = self.pop(position)?; let chunk = self.chunk.clone();
let left = self.pop(position)?; 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 let greater = left
.greater_than(&right) .greater_than(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push(greater, position)?; self.push_runtime_value(greater, position)?;
} }
Instruction::Less => { Instruction::Less => {
let right = self.pop(position)?; let chunk = self.chunk.clone();
let left = self.pop(position)?; 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 let less = left
.less_than(&right) .less_than(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push(less, position)?; self.push_runtime_value(less, position)?;
} }
Instruction::GreaterEqual => { Instruction::GreaterEqual => {
let right = self.pop(position)?; let chunk = self.chunk.clone();
let left = self.pop(position)?; 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 let greater_equal = left
.greater_than_or_equal(&right) .greater_than_or_equal(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push(greater_equal, position)?; self.push_runtime_value(greater_equal, position)?;
} }
Instruction::LessEqual => { Instruction::LessEqual => {
let right = self.pop(position)?; let chunk = self.chunk.clone();
let left = self.pop(position)?; 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 let less_equal = left
.less_than_or_equal(&right) .less_than_or_equal(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push(less_equal, position)?; self.push_runtime_value(less_equal, position)?;
} }
Instruction::Equal => { Instruction::Equal => {
let right = self.pop(position)?; let chunk = self.chunk.clone();
let left = self.pop(position)?; 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 let equal = left
.equal(&right) .equal(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push(equal, position)?; self.push_runtime_value(equal, position)?;
} }
Instruction::NotEqual => { Instruction::NotEqual => {
let right = self.pop(position)?; let chunk = self.chunk.clone();
let left = self.pop(position)?; 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 let not_equal = left
.not_equal(&right) .not_equal(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push(not_equal, position)?; self.push_runtime_value(not_equal, position)?;
} }
Instruction::And => { Instruction::And => {
let right = self.pop(position)?; let chunk = self.chunk.clone();
let left = self.pop(position)?; 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 let and = left
.and(&right) .and(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push(and, position)?; self.push_runtime_value(and, position)?;
} }
Instruction::Or => { Instruction::Or => {
let right = self.pop(position)?; let chunk = self.chunk.clone();
let left = self.pop(position)?; 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 let or = left
.or(&right) .or(right)
.map_err(|error| VmError::Value { error, position })?; .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 { if self.stack.len() == Self::STACK_SIZE {
Err(VmError::StackOverflow(position)) Err(VmError::StackOverflow(position))
} else { } else {
self.stack.push(value); self.stack.push(StackedValue::Runtime(value));
Ok(()) Ok(())
} }
} }
fn pop(&mut self, position: Span) -> Result<Value, VmError> { fn push_constant_value(&mut self, index: u8, position: Span) -> Result<(), VmError> {
if let Some(value) = self.stack.pop() { if self.stack.len() == Self::STACK_SIZE {
Ok(value) Err(VmError::StackOverflow(position))
} else {
self.stack.push(StackedValue::Constant(index));
Ok(())
}
}
fn pop(&mut self, position: Span) -> Result<StackedValue, VmError> {
if let Some(stacked) = self.stack.pop() {
Ok(stacked)
} else { } else {
Err(VmError::StackUnderflow(position)) Err(VmError::StackUnderflow(position))
} }
@ -250,14 +295,22 @@ impl Vm {
Ok(current) Ok(current)
} }
}
fn read_constant(&self, index: u8, position: Span) -> Result<&Value, VmError> { #[derive(Debug, Clone, Eq, PartialEq)]
let value = self pub enum StackedValue {
.chunk Runtime(Value),
.get_constant(index) Constant(u8),
.map_err(|error| VmError::Chunk { error, position })?; }
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 } Self::Value { error, position }
} }
pub fn title(&self) -> &'static str { pub fn title() -> &'static str {
"VM Error" "Runtime Error"
} }
pub fn description(&self) -> String { pub fn description(&self) -> String {