Add error reports and byte operations
This commit is contained in:
parent
8f20e53880
commit
8798efc0af
@ -43,7 +43,7 @@ impl Chunk {
|
|||||||
pub fn get_code(&self, offset: usize) -> Result<&(u8, Span), ChunkError> {
|
pub fn get_code(&self, offset: usize) -> Result<&(u8, Span), ChunkError> {
|
||||||
self.code
|
self.code
|
||||||
.get(offset)
|
.get(offset)
|
||||||
.ok_or(ChunkError::CodeIndextOfBounds(offset))
|
.ok_or(ChunkError::CodeIndexOfBounds(offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_code(&mut self, instruction: u8, position: Span) {
|
pub fn push_code(&mut self, instruction: u8, position: Span) {
|
||||||
@ -169,22 +169,47 @@ impl Default for Chunk {
|
|||||||
|
|
||||||
impl Display for Chunk {
|
impl Display for Chunk {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "{}", self.disassemble("Chunk"))
|
write!(f, "{}", self.disassemble("Chunk Disassembly"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Chunk {
|
impl Debug for Chunk {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "{}", self.disassemble("Chunk"))
|
write!(f, "{}", self.disassemble("Chunk Disassembly"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum ChunkError {
|
pub enum ChunkError {
|
||||||
CodeIndextOfBounds(usize),
|
CodeIndexOfBounds(usize),
|
||||||
ConstantOverflow,
|
ConstantOverflow,
|
||||||
ConstantIndexOutOfBounds(u8),
|
ConstantIndexOutOfBounds(u8),
|
||||||
IdentifierIndexOutOfBounds(u8),
|
IdentifierIndexOutOfBounds(u8),
|
||||||
IdentifierOverflow,
|
IdentifierOverflow,
|
||||||
IdentifierNotFound(Identifier),
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use annotate_snippets::{Level, Renderer, Snippet};
|
||||||
|
|
||||||
use crate::{vm::VmError, LexError, ParseError};
|
use crate::{vm::VmError, LexError, ParseError};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@ -15,3 +17,30 @@ pub enum DustError<'src> {
|
|||||||
source: &'src str,
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -206,6 +206,7 @@ impl<'src> Parser<'src> {
|
|||||||
TokenKind::Minus => Instruction::Subtract as u8,
|
TokenKind::Minus => Instruction::Subtract as u8,
|
||||||
TokenKind::Star => Instruction::Multiply as u8,
|
TokenKind::Star => Instruction::Multiply as u8,
|
||||||
TokenKind::Slash => Instruction::Divide as u8,
|
TokenKind::Slash => Instruction::Divide as u8,
|
||||||
|
TokenKind::DoubleAmpersand => Instruction::And as u8,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParseError::ExpectedTokenMultiple {
|
return Err(ParseError::ExpectedTokenMultiple {
|
||||||
expected: vec![
|
expected: vec![
|
||||||
@ -319,8 +320,8 @@ impl<'src> Parser<'src> {
|
|||||||
fn parse(&mut self, precedence: Precedence) -> Result<(), ParseError> {
|
fn parse(&mut self, precedence: Precedence) -> Result<(), ParseError> {
|
||||||
self.advance()?;
|
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!(
|
log::trace!(
|
||||||
"Parsing {} as prefix with precedence {precedence}",
|
"Parsing {} as prefix with precedence {precedence}",
|
||||||
self.previous_token,
|
self.previous_token,
|
||||||
@ -333,17 +334,14 @@ impl<'src> Parser<'src> {
|
|||||||
position: self.previous_position,
|
position: self.previous_position,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let allow_assignment = precedence <= Precedence::Assignment;
|
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 {
|
while precedence < ParseRule::from(&self.current_token.kind()).precedence {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
let infix_rule = ParseRule::from(&self.previous_token.kind()).infix;
|
if let Some(infix_parser) = ParseRule::from(&self.previous_token.kind()).infix {
|
||||||
|
|
||||||
if let Some(infix) = infix_rule {
|
|
||||||
log::trace!(
|
log::trace!(
|
||||||
"Parsing {} as infix with precedence {precedence}",
|
"Parsing {} as infix with precedence {precedence}",
|
||||||
self.previous_token,
|
self.previous_token,
|
||||||
@ -356,7 +354,7 @@ impl<'src> Parser<'src> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
infix(self)?;
|
infix_parser(self)?;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -485,7 +483,11 @@ impl From<&TokenKind> for ParseRule<'_> {
|
|||||||
TokenKind::Colon => todo!(),
|
TokenKind::Colon => todo!(),
|
||||||
TokenKind::Comma => todo!(),
|
TokenKind::Comma => todo!(),
|
||||||
TokenKind::Dot => todo!(),
|
TokenKind::Dot => todo!(),
|
||||||
TokenKind::DoubleAmpersand => todo!(),
|
TokenKind::DoubleAmpersand => ParseRule {
|
||||||
|
prefix: None,
|
||||||
|
infix: Some(Parser::parse_binary),
|
||||||
|
precedence: Precedence::LogicalAnd,
|
||||||
|
},
|
||||||
TokenKind::DoubleDot => todo!(),
|
TokenKind::DoubleDot => todo!(),
|
||||||
TokenKind::DoubleEqual => todo!(),
|
TokenKind::DoubleEqual => todo!(),
|
||||||
TokenKind::DoublePipe => todo!(),
|
TokenKind::DoublePipe => todo!(),
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
error::Error,
|
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
ops::{Range, RangeInclusive},
|
ops::{Range, RangeInclusive},
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
@ -1006,6 +1005,9 @@ impl ValueData {
|
|||||||
|
|
||||||
pub fn add(&self, other: &ValueData) -> Option<ValueData> {
|
pub fn add(&self, other: &ValueData) -> Option<ValueData> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
|
(ValueData::Byte(left), ValueData::Byte(right)) => {
|
||||||
|
Some(ValueData::Byte(left.saturating_add(*right)))
|
||||||
|
}
|
||||||
(ValueData::Float(left), ValueData::Float(right)) => {
|
(ValueData::Float(left), ValueData::Float(right)) => {
|
||||||
Some(ValueData::Float(left + right))
|
Some(ValueData::Float(left + right))
|
||||||
}
|
}
|
||||||
@ -1021,6 +1023,9 @@ impl ValueData {
|
|||||||
|
|
||||||
pub fn subtract(&self, other: &ValueData) -> Option<ValueData> {
|
pub fn subtract(&self, other: &ValueData) -> Option<ValueData> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
|
(ValueData::Byte(left), ValueData::Byte(right)) => {
|
||||||
|
Some(ValueData::Byte(left.saturating_sub(*right)))
|
||||||
|
}
|
||||||
(ValueData::Float(left), ValueData::Float(right)) => {
|
(ValueData::Float(left), ValueData::Float(right)) => {
|
||||||
Some(ValueData::Float(left - right))
|
Some(ValueData::Float(left - right))
|
||||||
}
|
}
|
||||||
@ -1033,6 +1038,9 @@ impl ValueData {
|
|||||||
|
|
||||||
pub fn multiply(&self, other: &ValueData) -> Option<ValueData> {
|
pub fn multiply(&self, other: &ValueData) -> Option<ValueData> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
|
(ValueData::Byte(left), ValueData::Byte(right)) => {
|
||||||
|
Some(ValueData::Byte(left.saturating_mul(*right)))
|
||||||
|
}
|
||||||
(ValueData::Float(left), ValueData::Float(right)) => {
|
(ValueData::Float(left), ValueData::Float(right)) => {
|
||||||
Some(ValueData::Float(left * right))
|
Some(ValueData::Float(left * right))
|
||||||
}
|
}
|
||||||
@ -1045,6 +1053,9 @@ impl ValueData {
|
|||||||
|
|
||||||
pub fn divide(&self, other: &ValueData) -> Option<ValueData> {
|
pub fn divide(&self, other: &ValueData) -> Option<ValueData> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
|
(ValueData::Byte(left), ValueData::Byte(right)) => {
|
||||||
|
Some(ValueData::Byte(left.saturating_div(*right)))
|
||||||
|
}
|
||||||
(ValueData::Float(left), ValueData::Float(right)) => {
|
(ValueData::Float(left), ValueData::Float(right)) => {
|
||||||
Some(ValueData::Float(left / right))
|
Some(ValueData::Float(left / right))
|
||||||
}
|
}
|
||||||
@ -1170,7 +1181,7 @@ impl Display for ValueData {
|
|||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
ValueData::Boolean(boolean) => write!(f, "{boolean}"),
|
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::Character(character) => write!(f, "{character}"),
|
||||||
ValueData::Enum(r#enum) => write!(f, "{enum}"),
|
ValueData::Enum(r#enum) => write!(f, "{enum}"),
|
||||||
ValueData::Float(float) => {
|
ValueData::Float(float) => {
|
||||||
@ -1757,12 +1768,68 @@ pub enum ValueError {
|
|||||||
CannotNot(Value),
|
CannotNot(Value),
|
||||||
CannotSubtract(Value, Value),
|
CannotSubtract(Value, Value),
|
||||||
CannotOr(Value, Value),
|
CannotOr(Value, Value),
|
||||||
DivisionByZero,
|
|
||||||
ExpectedList(Value),
|
|
||||||
IndexOutOfBounds { value: Value, index: i64 },
|
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 {
|
impl Display for ValueError {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
@ -1810,11 +1877,9 @@ impl Display for ValueError {
|
|||||||
left, right
|
left, right
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ValueError::DivisionByZero => write!(f, "Division by zero"),
|
|
||||||
ValueError::IndexOutOfBounds { value, index } => {
|
ValueError::IndexOutOfBounds { value, index } => {
|
||||||
write!(f, "{} does not have an index of {}", value, index)
|
write!(f, "{} does not have an index of {}", value, index)
|
||||||
}
|
}
|
||||||
ValueError::ExpectedList(value) => write!(f, "{} is not a list", value),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,11 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) -> Result<Option<Value>, VmError> {
|
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)
|
let instruction = Instruction::from_byte(byte)
|
||||||
.ok_or_else(|| VmError::InvalidInstruction(byte, position))?;
|
.ok_or_else(|| VmError::InvalidInstruction(byte, position))?;
|
||||||
|
|
||||||
@ -38,142 +42,179 @@ impl Vm {
|
|||||||
|
|
||||||
match instruction {
|
match instruction {
|
||||||
Instruction::Constant => {
|
Instruction::Constant => {
|
||||||
let (index, _) = self.read().copied()?;
|
let (index, _) = self.read(position).copied()?;
|
||||||
let value = self.read_constant(index)?;
|
let value = self.read_constant(index, position)?.clone();
|
||||||
|
|
||||||
self.push(value)?;
|
self.push(value, position)?;
|
||||||
}
|
}
|
||||||
Instruction::Return => {
|
Instruction::Return => {
|
||||||
let value = self.pop()?;
|
let value = self.pop(position)?;
|
||||||
|
|
||||||
return Ok(Some(value));
|
return Ok(Some(value));
|
||||||
}
|
}
|
||||||
Instruction::Pop => {
|
Instruction::Pop => {
|
||||||
self.pop()?;
|
self.pop(position)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
Instruction::DefineVariable => {
|
Instruction::DefineVariable => {
|
||||||
let (index, _) = *self.read()?;
|
let (index, _) = *self.read(position)?;
|
||||||
let value = self.read_constant(index)?;
|
let value = self
|
||||||
|
.read_constant(index, position)?
|
||||||
|
.clone()
|
||||||
|
.into_reference();
|
||||||
|
|
||||||
self.stack.insert(index as usize, value);
|
self.stack.insert(index as usize, value);
|
||||||
}
|
}
|
||||||
Instruction::GetVariable => {
|
Instruction::GetVariable => {
|
||||||
let (index, _) = *self.read()?;
|
let (index, _) = *self.read(position)?;
|
||||||
let value = self.stack[index as usize].clone();
|
let value = self.stack[index as usize].clone();
|
||||||
|
|
||||||
self.push(value)?;
|
self.push(value, position)?;
|
||||||
}
|
}
|
||||||
Instruction::SetVariable => {
|
Instruction::SetVariable => {
|
||||||
let (index, _) = *self.read()?;
|
let (index, _) = *self.read(position)?;
|
||||||
let identifier = self.chunk.get_identifier(index)?.clone();
|
let identifier = self
|
||||||
|
.chunk
|
||||||
|
.get_identifier(index)
|
||||||
|
.map_err(|error| VmError::Chunk { error, position })?
|
||||||
|
.clone();
|
||||||
|
|
||||||
if !self.chunk.contains_identifier(&identifier) {
|
if !self.chunk.contains_identifier(&identifier) {
|
||||||
return Err(VmError::UndefinedVariable(identifier, position));
|
return Err(VmError::UndefinedVariable(identifier, position));
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = self.pop()?;
|
let value = self.pop(position)?;
|
||||||
|
|
||||||
self.stack[index as usize] = value;
|
self.stack[index as usize] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unary
|
// Unary
|
||||||
Instruction::Negate => {
|
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 => {
|
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
|
// Binary
|
||||||
Instruction::Add => {
|
Instruction::Add => {
|
||||||
let right = self.pop()?;
|
let right = self.pop(position)?;
|
||||||
let left = self.pop()?;
|
let left = self.pop(position)?;
|
||||||
let sum = left.add(&right)?;
|
let sum = left
|
||||||
|
.add(&right)
|
||||||
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.push(sum)?;
|
self.push(sum, position)?;
|
||||||
}
|
}
|
||||||
Instruction::Subtract => {
|
Instruction::Subtract => {
|
||||||
let right = self.pop()?;
|
let right = self.pop(position)?;
|
||||||
let left = self.pop()?;
|
let left = self.pop(position)?;
|
||||||
let difference = left.subtract(&right)?;
|
let difference = left
|
||||||
|
.subtract(&right)
|
||||||
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.push(difference)?;
|
self.push(difference, position)?;
|
||||||
}
|
}
|
||||||
Instruction::Multiply => {
|
Instruction::Multiply => {
|
||||||
let right = self.pop()?;
|
let right = self.pop(position)?;
|
||||||
let left = self.pop()?;
|
let left = self.pop(position)?;
|
||||||
let product = left.multiply(&right)?;
|
let product = left
|
||||||
|
.multiply(&right)
|
||||||
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.push(product)?;
|
self.push(product, position)?;
|
||||||
}
|
}
|
||||||
Instruction::Divide => {
|
Instruction::Divide => {
|
||||||
let right = self.pop()?;
|
let right = self.pop(position)?;
|
||||||
let left = self.pop()?;
|
let left = self.pop(position)?;
|
||||||
let quotient = left.divide(&right)?;
|
let quotient = left
|
||||||
|
.divide(&right)
|
||||||
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.push(quotient)?;
|
self.push(quotient, position)?;
|
||||||
}
|
}
|
||||||
Instruction::Greater => {
|
Instruction::Greater => {
|
||||||
let right = self.pop()?;
|
let right = self.pop(position)?;
|
||||||
let left = self.pop()?;
|
let left = self.pop(position)?;
|
||||||
let greater = left.greater_than(&right)?;
|
let greater = left
|
||||||
|
.greater_than(&right)
|
||||||
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.push(greater)?;
|
self.push(greater, position)?;
|
||||||
}
|
}
|
||||||
Instruction::Less => {
|
Instruction::Less => {
|
||||||
let right = self.pop()?;
|
let right = self.pop(position)?;
|
||||||
let left = self.pop()?;
|
let left = self.pop(position)?;
|
||||||
let less = left.less_than(&right)?;
|
let less = left
|
||||||
|
.less_than(&right)
|
||||||
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.push(less)?;
|
self.push(less, position)?;
|
||||||
}
|
}
|
||||||
Instruction::GreaterEqual => {
|
Instruction::GreaterEqual => {
|
||||||
let right = self.pop()?;
|
let right = self.pop(position)?;
|
||||||
let left = self.pop()?;
|
let left = self.pop(position)?;
|
||||||
let greater_equal = left.greater_than_or_equal(&right)?;
|
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 => {
|
Instruction::LessEqual => {
|
||||||
let right = self.pop()?;
|
let right = self.pop(position)?;
|
||||||
let left = self.pop()?;
|
let left = self.pop(position)?;
|
||||||
let less_equal = left.less_than_or_equal(&right)?;
|
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 => {
|
Instruction::Equal => {
|
||||||
let right = self.pop()?;
|
let right = self.pop(position)?;
|
||||||
let left = self.pop()?;
|
let left = self.pop(position)?;
|
||||||
let equal = left.equal(&right)?;
|
let equal = left
|
||||||
|
.equal(&right)
|
||||||
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.push(equal)?;
|
self.push(equal, position)?;
|
||||||
}
|
}
|
||||||
Instruction::NotEqual => {
|
Instruction::NotEqual => {
|
||||||
let right = self.pop()?;
|
let right = self.pop(position)?;
|
||||||
let left = self.pop()?;
|
let left = self.pop(position)?;
|
||||||
let not_equal = left.not_equal(&right)?;
|
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 => {
|
Instruction::And => {
|
||||||
let right = self.pop()?;
|
let right = self.pop(position)?;
|
||||||
let left = self.pop()?;
|
let left = self.pop(position)?;
|
||||||
let and = left.and(&right)?;
|
let and = left
|
||||||
|
.and(&right)
|
||||||
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.push(and)?;
|
self.push(and, position)?;
|
||||||
}
|
}
|
||||||
Instruction::Or => {
|
Instruction::Or => {
|
||||||
let right = self.pop()?;
|
let right = self.pop(position)?;
|
||||||
let left = self.pop()?;
|
let left = self.pop(position)?;
|
||||||
let or = left.or(&right)?;
|
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())
|
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 {
|
if self.stack.len() == Self::STACK_SIZE {
|
||||||
Err(VmError::StackOverflow)
|
Err(VmError::StackOverflow(position))
|
||||||
} else {
|
} else {
|
||||||
self.stack.push(value);
|
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() {
|
if let Some(value) = self.stack.pop() {
|
||||||
Ok(value)
|
Ok(value)
|
||||||
} else {
|
} else {
|
||||||
Err(VmError::StackUnderflow)
|
Err(VmError::StackUnderflow(position))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read(&mut self) -> Result<&(u8, Span), VmError> {
|
fn read(&mut self, position: Span) -> Result<&(u8, Span), VmError> {
|
||||||
let current = self.chunk.get_code(self.ip)?;
|
let current = self
|
||||||
|
.chunk
|
||||||
|
.get_code(self.ip)
|
||||||
|
.map_err(|error| VmError::Chunk { error, position })?;
|
||||||
|
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
|
|
||||||
Ok(current)
|
Ok(current)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_constant(&self, index: u8) -> Result<Value, VmError> {
|
fn read_constant(&self, index: u8, position: Span) -> Result<&Value, VmError> {
|
||||||
Ok(self.chunk.get_constant(index)?.clone())
|
let value = self
|
||||||
|
.chunk
|
||||||
|
.get_constant(index)
|
||||||
|
.map_err(|error| VmError::Chunk { error, position })?;
|
||||||
|
|
||||||
|
Ok(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum VmError {
|
pub enum VmError {
|
||||||
InvalidInstruction(u8, Span),
|
InvalidInstruction(u8, Span),
|
||||||
StackUnderflow,
|
StackOverflow(Span),
|
||||||
StackOverflow,
|
StackUnderflow(Span),
|
||||||
UndefinedVariable(Identifier, Span),
|
UndefinedVariable(Identifier, Span),
|
||||||
|
|
||||||
// Wrappers for foreign errors
|
// Wrappers for foreign errors
|
||||||
Chunk(ChunkError),
|
Chunk { error: ChunkError, position: Span },
|
||||||
Value(ValueError),
|
Value { error: ValueError, position: Span },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ChunkError> for VmError {
|
impl VmError {
|
||||||
fn from(error: ChunkError) -> Self {
|
pub fn chunk(error: ChunkError, position: Span) -> Self {
|
||||||
Self::Chunk(error)
|
Self::Chunk { error, position }
|
||||||
|
}
|
||||||
|
|
||||||
|
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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ValueError> for VmError {
|
pub fn position(&self) -> Span {
|
||||||
fn from(error: ValueError) -> Self {
|
match self {
|
||||||
Self::Value(error)
|
Self::InvalidInstruction(_, position) => *position,
|
||||||
|
Self::StackUnderflow(position) => *position,
|
||||||
|
Self::StackOverflow(position) => *position,
|
||||||
|
Self::UndefinedVariable(_, position) => *position,
|
||||||
|
Self::Chunk { position, .. } => *position,
|
||||||
|
Self::Value { position, .. } => *position,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ fn run_and_display_errors(source: &str) {
|
|||||||
Ok(Some(value)) => println!("{}", value),
|
Ok(Some(value)) => println!("{}", value),
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
eprintln!("{:?}", error);
|
eprintln!("{}", error.report());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user