Go to great lengths to avoid cloning Values; Extend error reports
This commit is contained in:
parent
0ed2733991
commit
f936c30b4f
@ -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();
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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),
|
||||||
|
@ -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;
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user