Pass all but one test
This commit is contained in:
parent
03113fdf5e
commit
be77d64c39
@ -3,7 +3,7 @@ use std::fmt::{self, Debug, Display, Formatter};
|
||||
use colored::Colorize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{AnnotatedError, Identifier, Instruction, Span, Value};
|
||||
use crate::{AnnotatedError, Identifier, Instruction, Operation, Span, Value};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Chunk {
|
||||
@ -40,6 +40,14 @@ impl Chunk {
|
||||
self.scope_depth
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.instructions.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.instructions.is_empty()
|
||||
}
|
||||
|
||||
pub fn get_instruction(
|
||||
&self,
|
||||
offset: usize,
|
||||
@ -54,18 +62,27 @@ impl Chunk {
|
||||
self.instructions.push((instruction, position));
|
||||
}
|
||||
|
||||
pub fn insert_instruction(&mut self, index: usize, instruction: Instruction, position: Span) {
|
||||
self.instructions.insert(index, (instruction, position));
|
||||
}
|
||||
|
||||
pub fn pop_instruction(&mut self, position: Span) -> Result<(Instruction, Span), ChunkError> {
|
||||
self.instructions
|
||||
.pop()
|
||||
.ok_or(ChunkError::InstructionUnderflow { position })
|
||||
}
|
||||
|
||||
pub fn get_last_instruction(&self, position: Span) -> Result<&(Instruction, Span), ChunkError> {
|
||||
pub fn get_previous(&self, position: Span) -> Result<&(Instruction, Span), ChunkError> {
|
||||
self.instructions
|
||||
.last()
|
||||
.ok_or(ChunkError::InstructionUnderflow { position })
|
||||
}
|
||||
|
||||
pub fn get_last_operation(&self, position: Span) -> Result<Operation, ChunkError> {
|
||||
self.get_previous(position)
|
||||
.map(|(instruction, _)| instruction.operation())
|
||||
}
|
||||
|
||||
pub fn get_constant(&self, index: u8, position: Span) -> Result<&Value, ChunkError> {
|
||||
let index = index as usize;
|
||||
|
||||
|
@ -43,39 +43,40 @@ impl Instruction {
|
||||
instruction
|
||||
}
|
||||
|
||||
pub fn load_list(to_register: u8, list_length: u8) -> Instruction {
|
||||
pub fn load_list(to_register: u8, start_register: u8, list_length: u8) -> Instruction {
|
||||
let mut instruction = Instruction(Operation::LoadList as u32);
|
||||
|
||||
instruction.set_destination(to_register);
|
||||
instruction.set_first_argument(list_length);
|
||||
instruction.set_first_argument(start_register);
|
||||
instruction.set_second_argument(list_length);
|
||||
|
||||
instruction
|
||||
}
|
||||
|
||||
pub fn define_local(to_register: u8, variable_index: u8, is_mutable: bool) -> Instruction {
|
||||
pub fn define_local(to_register: u8, local_index: u8, is_mutable: bool) -> Instruction {
|
||||
let mut instruction = Instruction(Operation::DefineLocal as u32);
|
||||
|
||||
instruction.set_destination(to_register);
|
||||
instruction.set_first_argument(variable_index);
|
||||
instruction.set_first_argument(local_index);
|
||||
instruction.set_second_argument(if is_mutable { 1 } else { 0 });
|
||||
|
||||
instruction
|
||||
}
|
||||
|
||||
pub fn get_local(to_register: u8, variable_index: u8) -> Instruction {
|
||||
pub fn get_local(to_register: u8, local_index: u8) -> Instruction {
|
||||
let mut instruction = Instruction(Operation::GetLocal as u32);
|
||||
|
||||
instruction.set_destination(to_register);
|
||||
instruction.set_first_argument(variable_index);
|
||||
instruction.set_first_argument(local_index);
|
||||
|
||||
instruction
|
||||
}
|
||||
|
||||
pub fn set_local(from_register: u8, variable_index: u8) -> Instruction {
|
||||
pub fn set_local(from_register: u8, local_index: u8) -> Instruction {
|
||||
let mut instruction = Instruction(Operation::SetLocal as u32);
|
||||
|
||||
instruction.set_destination(from_register);
|
||||
instruction.set_first_argument(variable_index);
|
||||
instruction.set_first_argument(local_index);
|
||||
|
||||
instruction
|
||||
}
|
||||
@ -130,22 +131,21 @@ impl Instruction {
|
||||
instruction
|
||||
}
|
||||
|
||||
pub fn and(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
||||
let mut instruction = Instruction(Operation::And as u32);
|
||||
pub fn test(to_register: u8, test_value: bool) -> Instruction {
|
||||
let mut instruction = Instruction(Operation::Test as u32);
|
||||
|
||||
instruction.set_destination(to_register);
|
||||
instruction.set_first_argument(left_index);
|
||||
instruction.set_second_argument(right_index);
|
||||
instruction.set_second_argument_to_boolean(test_value);
|
||||
|
||||
instruction
|
||||
}
|
||||
|
||||
pub fn or(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
||||
let mut instruction = Instruction(Operation::Or as u32);
|
||||
pub fn test_set(to_register: u8, argument_index: u8, test_value: bool) -> Instruction {
|
||||
let mut instruction = Instruction(Operation::TestSet as u32);
|
||||
|
||||
instruction.set_destination(to_register);
|
||||
instruction.set_first_argument(left_index);
|
||||
instruction.set_second_argument(right_index);
|
||||
instruction.set_first_argument(argument_index);
|
||||
instruction.set_second_argument_to_boolean(test_value);
|
||||
|
||||
instruction
|
||||
}
|
||||
@ -187,8 +187,13 @@ impl Instruction {
|
||||
instruction
|
||||
}
|
||||
|
||||
pub fn r#return() -> Instruction {
|
||||
Instruction(Operation::Return as u32)
|
||||
pub fn r#return(from_register: u8, to_register: u8) -> Instruction {
|
||||
let mut instruction = Instruction(Operation::Return as u32);
|
||||
|
||||
instruction.set_destination(from_register);
|
||||
instruction.set_first_argument(to_register);
|
||||
|
||||
instruction
|
||||
}
|
||||
|
||||
pub fn operation(&self) -> Operation {
|
||||
@ -339,7 +344,7 @@ impl Instruction {
|
||||
}
|
||||
Operation::LoadList => {
|
||||
let destination = self.destination();
|
||||
let first_index = destination - (self.first_argument() - 1);
|
||||
let first_index = self.first_argument();
|
||||
let last_index = destination - 1;
|
||||
|
||||
format!("R{} = [R{}..=R{}]", destination, first_index, last_index)
|
||||
@ -416,17 +421,22 @@ impl Instruction {
|
||||
|
||||
format!("R{destination} = {first_argument} % {second_argument}",)
|
||||
}
|
||||
Operation::And => {
|
||||
Operation::Test => {
|
||||
let destination = self.destination();
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
let test_value = self.second_argument_as_boolean();
|
||||
let bang = if test_value { "" } else { "!" };
|
||||
|
||||
format!("R{destination} = {first_argument} && {second_argument}",)
|
||||
format!("if {bang}R{destination} {{ IP++ }}",)
|
||||
}
|
||||
Operation::Or => {
|
||||
Operation::TestSet => {
|
||||
let destination = self.destination();
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
let argument = format!("R{}", self.first_argument());
|
||||
let test_value = self.second_argument_as_boolean();
|
||||
let bang = if test_value { "" } else { "!" };
|
||||
|
||||
format!("R{destination} = {first_argument} || {second_argument}",)
|
||||
format!(
|
||||
"if {bang}R{destination} {{ R{destination} = R{argument} }} else {{ IP++ }}",
|
||||
)
|
||||
}
|
||||
Operation::Equal => {
|
||||
let comparison_symbol = if self.destination_as_boolean() {
|
||||
@ -489,7 +499,12 @@ impl Instruction {
|
||||
format!("IP -= {}", offset)
|
||||
}
|
||||
}
|
||||
Operation::Return => return None,
|
||||
Operation::Return => {
|
||||
let from_register = self.destination();
|
||||
let to_register = self.first_argument();
|
||||
|
||||
format!("R{from_register}..=R{to_register}")
|
||||
}
|
||||
};
|
||||
let trucated_length = 30;
|
||||
let with_elipsis = trucated_length - 3;
|
||||
@ -637,32 +652,21 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn and() {
|
||||
let mut instruction = Instruction::and(0, 1, 2);
|
||||
let instruction = Instruction::test(4, true);
|
||||
|
||||
instruction.set_first_argument_to_constant();
|
||||
instruction.set_second_argument_to_constant();
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::And);
|
||||
assert_eq!(instruction.destination(), 0);
|
||||
assert_eq!(instruction.first_argument(), 1);
|
||||
assert_eq!(instruction.second_argument(), 2);
|
||||
assert!(instruction.first_argument_is_constant());
|
||||
assert!(instruction.second_argument_is_constant());
|
||||
assert_eq!(instruction.operation(), Operation::Test);
|
||||
assert_eq!(instruction.destination(), 4);
|
||||
assert!(instruction.second_argument_as_boolean());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn or() {
|
||||
let mut instruction = Instruction::or(0, 1, 2);
|
||||
let instruction = Instruction::test_set(4, 1, true);
|
||||
|
||||
instruction.set_first_argument_to_constant();
|
||||
instruction.set_second_argument_to_constant();
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Or);
|
||||
assert_eq!(instruction.destination(), 0);
|
||||
assert_eq!(instruction.operation(), Operation::TestSet);
|
||||
assert_eq!(instruction.destination(), 4);
|
||||
assert_eq!(instruction.first_argument(), 1);
|
||||
assert_eq!(instruction.second_argument(), 2);
|
||||
assert!(instruction.first_argument_is_constant());
|
||||
assert!(instruction.second_argument_is_constant());
|
||||
assert!(instruction.second_argument_as_boolean());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -710,10 +714,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn jump() {
|
||||
let mut instruction = Instruction::jump(4, true);
|
||||
|
||||
instruction.set_first_argument_to_constant();
|
||||
instruction.set_second_argument_to_constant();
|
||||
let instruction = Instruction::jump(4, true);
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Jump);
|
||||
assert_eq!(instruction.first_argument(), 4);
|
||||
@ -722,13 +723,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn r#return() {
|
||||
let mut instruction = Instruction::r#return();
|
||||
|
||||
instruction.set_first_argument_to_constant();
|
||||
instruction.set_second_argument_to_constant();
|
||||
let instruction = Instruction::r#return(4, 8);
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Return);
|
||||
assert!(instruction.first_argument_is_constant());
|
||||
assert!(instruction.second_argument_is_constant());
|
||||
assert_eq!(instruction.destination(), 4);
|
||||
assert_eq!(instruction.first_argument(), 8);
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,9 @@ const SUBTRACT: u8 = 0b0000_1001;
|
||||
const MULTIPLY: u8 = 0b0000_1010;
|
||||
const DIVIDE: u8 = 0b0000_1011;
|
||||
const MODULO: u8 = 0b0000_1100;
|
||||
const AND: u8 = 0b0000_1101;
|
||||
const OR: u8 = 0b0000_1110;
|
||||
|
||||
const TEST: u8 = 0b0000_1101;
|
||||
const TEST_SET: u8 = 0b0000_1110;
|
||||
|
||||
const EQUAL: u8 = 0b0000_1111;
|
||||
const LESS: u8 = 0b0001_0000;
|
||||
@ -51,8 +52,10 @@ pub enum Operation {
|
||||
Multiply = MULTIPLY as isize,
|
||||
Divide = DIVIDE as isize,
|
||||
Modulo = MODULO as isize,
|
||||
And = AND as isize,
|
||||
Or = OR as isize,
|
||||
|
||||
// Logical operations
|
||||
Test = TEST as isize,
|
||||
TestSet = TEST_SET as isize,
|
||||
|
||||
// Relational operations
|
||||
Equal = EQUAL as isize,
|
||||
@ -77,8 +80,6 @@ impl Operation {
|
||||
| Operation::Multiply
|
||||
| Operation::Divide
|
||||
| Operation::Modulo
|
||||
| Operation::And
|
||||
| Operation::Or
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -99,8 +100,8 @@ impl From<u8> for Operation {
|
||||
MULTIPLY => Operation::Multiply,
|
||||
DIVIDE => Operation::Divide,
|
||||
MODULO => Operation::Modulo,
|
||||
AND => Operation::And,
|
||||
OR => Operation::Or,
|
||||
TEST => Operation::Test,
|
||||
TEST_SET => Operation::TestSet,
|
||||
EQUAL => Operation::Equal,
|
||||
LESS => Operation::Less,
|
||||
LESS_EQUAL => Operation::LessEqual,
|
||||
@ -135,8 +136,8 @@ impl From<Operation> for u8 {
|
||||
Operation::Multiply => MULTIPLY,
|
||||
Operation::Divide => DIVIDE,
|
||||
Operation::Modulo => MODULO,
|
||||
Operation::And => AND,
|
||||
Operation::Or => OR,
|
||||
Operation::Test => TEST,
|
||||
Operation::TestSet => TEST_SET,
|
||||
Operation::Equal => EQUAL,
|
||||
Operation::Less => LESS,
|
||||
Operation::LessEqual => LESS_EQUAL,
|
||||
@ -164,8 +165,8 @@ impl Display for Operation {
|
||||
Operation::Multiply => write!(f, "MULTIPLY"),
|
||||
Operation::Divide => write!(f, "DIVIDE"),
|
||||
Operation::Modulo => write!(f, "MODULO"),
|
||||
Operation::And => write!(f, "AND"),
|
||||
Operation::Or => write!(f, "OR"),
|
||||
Operation::Test => write!(f, "TEST"),
|
||||
Operation::TestSet => write!(f, "TEST_SET"),
|
||||
Operation::Equal => write!(f, "EQUAL"),
|
||||
Operation::Less => write!(f, "LESS"),
|
||||
Operation::LessEqual => write!(f, "LESS_EQUAL"),
|
||||
|
@ -18,7 +18,7 @@ pub fn parse(source: &str) -> Result<Chunk, DustError> {
|
||||
|
||||
while !parser.is_eof() {
|
||||
parser
|
||||
.parse_statement()
|
||||
.parse_statement(true)
|
||||
.map_err(|error| DustError::Parse { error, source })?;
|
||||
}
|
||||
|
||||
@ -143,31 +143,31 @@ impl<'src> Parser<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_boolean(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||
fn parse_boolean(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
if let Token::Boolean(text) = self.current_token {
|
||||
let position = self.current_position;
|
||||
|
||||
self.advance()?;
|
||||
|
||||
let boolean = text.parse::<bool>().unwrap();
|
||||
|
||||
if let Ok((last_instruction, _)) = self
|
||||
.chunk
|
||||
.get_last_instruction(self.current_position)
|
||||
.copied()
|
||||
if let Ok((last_instruction, _)) =
|
||||
self.chunk.get_previous(self.current_position).copied()
|
||||
{
|
||||
let skip = last_instruction.operation() == Operation::Jump;
|
||||
|
||||
self.emit_instruction(
|
||||
Instruction::load_boolean(self.current_register, boolean, skip),
|
||||
self.previous_position,
|
||||
position,
|
||||
);
|
||||
|
||||
if let Operation::LoadBoolean = last_instruction.operation() {
|
||||
self.increment_register()?;
|
||||
}
|
||||
} else {
|
||||
self.emit_instruction(
|
||||
Instruction::load_boolean(self.current_register, boolean, false),
|
||||
self.previous_position,
|
||||
position,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -175,7 +175,11 @@ impl<'src> Parser<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_byte(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||
fn parse_byte(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
if let Token::Byte(text) = self.current_token {
|
||||
self.advance()?;
|
||||
|
||||
@ -192,7 +196,11 @@ impl<'src> Parser<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_character(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||
fn parse_character(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
if let Token::Character(character) = self.current_token {
|
||||
self.advance()?;
|
||||
|
||||
@ -204,7 +212,11 @@ impl<'src> Parser<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_float(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||
fn parse_float(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
if let Token::Float(text) = self.current_token {
|
||||
self.advance()?;
|
||||
|
||||
@ -222,7 +234,11 @@ impl<'src> Parser<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_integer(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||
fn parse_integer(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
if let Token::Integer(text) = self.current_token {
|
||||
self.advance()?;
|
||||
|
||||
@ -240,7 +256,11 @@ impl<'src> Parser<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_string(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||
fn parse_string(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
if let Token::String(text) = self.current_token {
|
||||
self.advance()?;
|
||||
|
||||
@ -252,13 +272,21 @@ impl<'src> Parser<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_grouped(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||
fn parse_grouped(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
self.allow(TokenKind::LeftParenthesis)?;
|
||||
self.parse_expression()?;
|
||||
self.expect(TokenKind::RightParenthesis)
|
||||
}
|
||||
|
||||
fn parse_unary(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||
fn parse_unary(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
let operator = self.current_token;
|
||||
let operator_position = self.current_position;
|
||||
|
||||
@ -308,89 +336,65 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
fn parse_binary(&mut self) -> Result<(), ParseError> {
|
||||
fn handle_argument(
|
||||
parser: &mut Parser,
|
||||
instruction: &Instruction,
|
||||
) -> Result<(bool, bool, u8), ParseError> {
|
||||
let mut push_back = false;
|
||||
let mut is_constant = false;
|
||||
let argument = match instruction.operation() {
|
||||
Operation::GetLocal => {
|
||||
parser.decrement_register()?;
|
||||
instruction.destination()
|
||||
}
|
||||
Operation::LoadConstant => {
|
||||
is_constant = true;
|
||||
|
||||
parser.decrement_register()?;
|
||||
instruction.first_argument()
|
||||
}
|
||||
Operation::LoadBoolean => {
|
||||
is_constant = true;
|
||||
push_back = true;
|
||||
|
||||
instruction.destination()
|
||||
}
|
||||
Operation::Close => {
|
||||
return Err(ParseError::ExpectedExpression {
|
||||
found: parser.previous_token.to_owned(),
|
||||
position: parser.previous_position,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
push_back = true;
|
||||
|
||||
instruction.destination()
|
||||
}
|
||||
};
|
||||
|
||||
Ok((push_back, is_constant, argument))
|
||||
}
|
||||
|
||||
let (left_instruction, left_position) =
|
||||
self.chunk.pop_instruction(self.current_position)?;
|
||||
let mut push_back_left = false;
|
||||
let mut left_is_constant = false;
|
||||
let left = match left_instruction.operation() {
|
||||
Operation::LoadConstant | Operation::GetLocal => {
|
||||
log::trace!(
|
||||
"Condensing {} to binary expression",
|
||||
left_instruction.operation()
|
||||
);
|
||||
|
||||
left_is_constant = matches!(left_instruction.operation(), Operation::LoadConstant);
|
||||
|
||||
self.decrement_register()?;
|
||||
left_instruction.first_argument()
|
||||
}
|
||||
Operation::LoadBoolean => {
|
||||
self.decrement_register()?;
|
||||
left_instruction.destination()
|
||||
}
|
||||
Operation::Close => {
|
||||
return Err(ParseError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
position: self.previous_position,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
push_back_left = true;
|
||||
|
||||
left_instruction.destination()
|
||||
}
|
||||
};
|
||||
let (push_back_left, left_is_constant, left) = handle_argument(self, &left_instruction)?;
|
||||
|
||||
let operator = self.current_token;
|
||||
let operator_position = self.current_position;
|
||||
let rule = ParseRule::from(&operator.kind());
|
||||
|
||||
self.advance()?;
|
||||
self.parse(rule.precedence.increment())?;
|
||||
let (mut instruction, push_args_first) = match operator.kind() {
|
||||
TokenKind::Plus => (Instruction::add(self.current_register, left, 0), true),
|
||||
TokenKind::Minus => (Instruction::subtract(self.current_register, left, 0), true),
|
||||
TokenKind::Star => (Instruction::multiply(self.current_register, left, 0), true),
|
||||
TokenKind::Slash => (Instruction::divide(self.current_register, left, 0), true),
|
||||
TokenKind::Percent => (Instruction::modulo(self.current_register, left, 0), true),
|
||||
TokenKind::DoubleEqual => (Instruction::equal(true, left, 0), false),
|
||||
TokenKind::DoubleAmpersand => {
|
||||
let and_test = Instruction::test(self.current_register, false);
|
||||
|
||||
let (right_instruction, right_position) =
|
||||
self.chunk.pop_instruction(self.current_position)?;
|
||||
let mut push_back_right = false;
|
||||
let mut right_is_constant = false;
|
||||
let right = match right_instruction.operation() {
|
||||
Operation::LoadConstant | Operation::GetLocal => {
|
||||
log::trace!(
|
||||
"Condensing {} to binary expression",
|
||||
right_instruction.operation()
|
||||
);
|
||||
|
||||
right_is_constant =
|
||||
matches!(right_instruction.operation(), Operation::LoadConstant);
|
||||
|
||||
self.decrement_register()?;
|
||||
right_instruction.first_argument()
|
||||
(and_test, false)
|
||||
}
|
||||
Operation::LoadBoolean => {
|
||||
self.decrement_register()?;
|
||||
right_instruction.destination()
|
||||
}
|
||||
Operation::Close => {
|
||||
return Err(ParseError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
position: self.previous_position,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
push_back_right = true;
|
||||
|
||||
right_instruction.destination()
|
||||
}
|
||||
};
|
||||
|
||||
let mut instruction = match operator.kind() {
|
||||
TokenKind::Plus => Instruction::add(self.current_register, left, right),
|
||||
TokenKind::Minus => Instruction::subtract(self.current_register, left, right),
|
||||
TokenKind::Star => Instruction::multiply(self.current_register, left, right),
|
||||
TokenKind::Slash => Instruction::divide(self.current_register, left, right),
|
||||
TokenKind::Percent => Instruction::modulo(self.current_register, left, right),
|
||||
TokenKind::DoubleAmpersand => Instruction::and(self.current_register, left, right),
|
||||
TokenKind::DoublePipe => Instruction::or(self.current_register, left, right),
|
||||
TokenKind::DoubleEqual => Instruction::equal(true, left, right),
|
||||
_ => {
|
||||
return Err(ParseError::ExpectedTokenMultiple {
|
||||
expected: &[
|
||||
@ -409,6 +413,20 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
};
|
||||
|
||||
if !(operator == Token::DoubleEqual) {
|
||||
self.increment_register()?;
|
||||
}
|
||||
|
||||
self.advance()?;
|
||||
self.parse(rule.precedence.increment())?;
|
||||
|
||||
let (right_instruction, right_position) =
|
||||
self.chunk.pop_instruction(self.current_position)?;
|
||||
let (push_back_right, right_is_constant, right) =
|
||||
handle_argument(self, &right_instruction)?;
|
||||
|
||||
instruction.set_second_argument(right);
|
||||
|
||||
if left_is_constant {
|
||||
instruction.set_first_argument_to_constant();
|
||||
}
|
||||
@ -417,38 +435,47 @@ impl<'src> Parser<'src> {
|
||||
instruction.set_second_argument_to_constant();
|
||||
}
|
||||
|
||||
if push_back_left {
|
||||
self.emit_instruction(left_instruction, left_position);
|
||||
if push_args_first {
|
||||
if push_back_left {
|
||||
self.emit_instruction(left_instruction, left_position);
|
||||
}
|
||||
|
||||
if push_back_right {
|
||||
self.emit_instruction(right_instruction, right_position);
|
||||
}
|
||||
|
||||
self.emit_instruction(instruction, operator_position);
|
||||
}
|
||||
|
||||
if push_back_right {
|
||||
self.emit_instruction(right_instruction, right_position);
|
||||
}
|
||||
if !push_args_first {
|
||||
let push_left_first = self.current_register.saturating_sub(1) == left;
|
||||
|
||||
self.emit_instruction(instruction, operator_position);
|
||||
if push_back_left && push_left_first {
|
||||
self.emit_instruction(left_instruction, left_position);
|
||||
}
|
||||
|
||||
if operator == Token::DoubleEqual {
|
||||
let distance = if push_back_left && push_back_right {
|
||||
3
|
||||
} else if push_back_left || push_back_right {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
};
|
||||
let jump_distance = if left_is_constant { 1 } else { 2 };
|
||||
|
||||
self.emit_instruction(Instruction::jump(distance, true), operator_position);
|
||||
} else {
|
||||
self.increment_register()?;
|
||||
self.emit_instruction(instruction, operator_position);
|
||||
self.emit_instruction(Instruction::jump(jump_distance, true), operator_position);
|
||||
|
||||
if push_back_left && !push_left_first {
|
||||
self.emit_instruction(left_instruction, left_position);
|
||||
}
|
||||
|
||||
if push_back_right {
|
||||
self.emit_instruction(right_instruction, right_position);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_variable(&mut self, allow_assignment: bool) -> Result<(), ParseError> {
|
||||
self.parse_named_variable(allow_assignment)
|
||||
}
|
||||
|
||||
fn parse_named_variable(&mut self, allow_assignment: bool) -> Result<(), ParseError> {
|
||||
fn parse_variable(
|
||||
&mut self,
|
||||
allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
let token = self.current_token.to_owned();
|
||||
let start_position = self.current_position;
|
||||
let local_index = self.parse_identifier_from(token, start_position)?;
|
||||
@ -482,6 +509,8 @@ impl<'src> Parser<'src> {
|
||||
|
||||
previous_instruction.set_destination(register_index);
|
||||
self.emit_instruction(previous_instruction, self.current_position);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
@ -491,11 +520,11 @@ impl<'src> Parser<'src> {
|
||||
start_position,
|
||||
);
|
||||
} else {
|
||||
self.increment_register()?;
|
||||
self.emit_instruction(
|
||||
Instruction::get_local(self.current_register, local_index),
|
||||
self.previous_position,
|
||||
);
|
||||
self.increment_register()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -526,12 +555,16 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_block(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||
fn parse_block(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
self.advance()?;
|
||||
self.chunk.begin_scope();
|
||||
|
||||
while !self.allow(TokenKind::RightCurlyBrace)? && !self.is_eof() {
|
||||
self.parse_statement()?;
|
||||
self.parse_statement(allow_return)?;
|
||||
}
|
||||
|
||||
self.chunk.end_scope();
|
||||
@ -539,11 +572,16 @@ impl<'src> Parser<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_list(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||
fn parse_list(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
let start = self.current_position.0;
|
||||
|
||||
self.advance()?;
|
||||
|
||||
let start_register = self.current_register;
|
||||
let mut length = 0;
|
||||
|
||||
while !self.allow(TokenKind::RightSquareBrace)? && !self.is_eof() {
|
||||
@ -570,29 +608,39 @@ impl<'src> Parser<'src> {
|
||||
let end = self.current_position.1;
|
||||
|
||||
self.emit_instruction(
|
||||
Instruction::load_list(self.current_register, length),
|
||||
Instruction::load_list(self.current_register, start_register, length),
|
||||
Span(start, end),
|
||||
);
|
||||
self.increment_register()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_if(&mut self, allow_assignment: bool) -> Result<(), ParseError> {
|
||||
fn parse_if(&mut self, allow_assignment: bool, _allow_return: bool) -> Result<(), ParseError> {
|
||||
self.advance()?;
|
||||
self.parse_expression()?;
|
||||
self.parse_block(allow_assignment)?;
|
||||
|
||||
self.parse_block(allow_assignment, false)?;
|
||||
|
||||
if self.allow(TokenKind::Else)? {
|
||||
self.parse_block(allow_assignment)?;
|
||||
if self.allow(TokenKind::If)? {
|
||||
self.parse_if(allow_assignment, false)?;
|
||||
} else {
|
||||
self.parse_block(allow_assignment, false)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_while(&mut self, allow_assignment: bool) -> Result<(), ParseError> {
|
||||
fn parse_while(
|
||||
&mut self,
|
||||
allow_assignment: bool,
|
||||
allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
self.advance()?;
|
||||
self.parse_expression()?;
|
||||
self.parse_block(allow_assignment)?;
|
||||
self.parse_block(allow_assignment, allow_return)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -601,18 +649,25 @@ impl<'src> Parser<'src> {
|
||||
self.parse(Precedence::None)
|
||||
}
|
||||
|
||||
fn parse_statement(&mut self) -> Result<(), ParseError> {
|
||||
fn parse_statement(&mut self, allow_return: bool) -> Result<(), ParseError> {
|
||||
let start = self.current_position.0;
|
||||
let is_expression = match self.current_token {
|
||||
Token::Let => {
|
||||
self.parse_let_statement(true)?;
|
||||
self.parse_let_statement(true, allow_return)?;
|
||||
|
||||
false
|
||||
}
|
||||
Token::LeftCurlyBrace => {
|
||||
self.parse_block(true)?;
|
||||
self.parse_block(true, true)?;
|
||||
|
||||
self.previous_token != Token::Semicolon
|
||||
let last_operation = self.chunk.get_last_operation(self.current_position)?;
|
||||
let ends_in_return = last_operation == Operation::Return;
|
||||
|
||||
if ends_in_return {
|
||||
self.chunk.pop_instruction(self.current_position)?;
|
||||
}
|
||||
|
||||
ends_in_return
|
||||
}
|
||||
_ => {
|
||||
self.parse_expression()?;
|
||||
@ -620,18 +675,36 @@ impl<'src> Parser<'src> {
|
||||
true
|
||||
}
|
||||
};
|
||||
|
||||
if !allow_return || !is_expression {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (last_instruction, _) = *self.chunk.get_previous(self.current_position)?;
|
||||
let ends_in_assignment = matches!(
|
||||
last_instruction.operation(),
|
||||
Operation::DefineLocal | Operation::SetLocal
|
||||
);
|
||||
let has_semicolon = self.allow(TokenKind::Semicolon)?;
|
||||
|
||||
if (!has_semicolon && is_expression) && self.is_eof() {
|
||||
if !ends_in_assignment && !has_semicolon {
|
||||
let end = self.previous_position.1;
|
||||
let return_register = last_instruction.destination();
|
||||
|
||||
self.emit_instruction(Instruction::r#return(), Span(start, end));
|
||||
self.emit_instruction(
|
||||
Instruction::r#return(return_register, return_register),
|
||||
Span(start, end),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_let_statement(&mut self, allow_assignment: bool) -> Result<(), ParseError> {
|
||||
fn parse_let_statement(
|
||||
&mut self,
|
||||
allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
if !allow_assignment {
|
||||
return Err(ParseError::ExpectedExpression {
|
||||
found: self.current_token.to_owned(),
|
||||
@ -658,8 +731,7 @@ impl<'src> Parser<'src> {
|
||||
self.expect(TokenKind::Equal)?;
|
||||
self.parse_expression()?;
|
||||
|
||||
let (previous_instruction, previous_position) =
|
||||
*self.chunk.get_last_instruction(position)?;
|
||||
let (previous_instruction, previous_position) = *self.chunk.get_previous(position)?;
|
||||
let register = previous_instruction.destination();
|
||||
let local_index =
|
||||
self.chunk
|
||||
@ -671,7 +743,7 @@ impl<'src> Parser<'src> {
|
||||
|
||||
if let Operation::Equal = self
|
||||
.chunk
|
||||
.get_last_instruction(self.current_position)?
|
||||
.get_previous(self.current_position)?
|
||||
.0
|
||||
.operation()
|
||||
{
|
||||
@ -700,6 +772,7 @@ impl<'src> Parser<'src> {
|
||||
|
||||
fn parse(&mut self, precedence: Precedence) -> Result<(), ParseError> {
|
||||
let allow_assignment = precedence < Precedence::Assignment;
|
||||
let allow_return = precedence == Precedence::None;
|
||||
|
||||
if let Some(prefix_parser) = ParseRule::from(&self.current_token.kind()).prefix {
|
||||
log::trace!(
|
||||
@ -707,7 +780,7 @@ impl<'src> Parser<'src> {
|
||||
self.current_token,
|
||||
);
|
||||
|
||||
prefix_parser(self, allow_assignment)?;
|
||||
prefix_parser(self, allow_assignment, allow_return)?;
|
||||
}
|
||||
|
||||
let mut infix_rule = ParseRule::from(&self.current_token.kind());
|
||||
@ -779,7 +852,7 @@ impl Display for Precedence {
|
||||
}
|
||||
}
|
||||
|
||||
type PrefixFunction<'a> = fn(&mut Parser<'a>, bool) -> Result<(), ParseError>;
|
||||
type PrefixFunction<'a> = fn(&mut Parser<'a>, bool, bool) -> Result<(), ParseError>;
|
||||
type InfixFunction<'a> = fn(&mut Parser<'a>) -> Result<(), ParseError>;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@ -1057,7 +1130,14 @@ impl AnnotatedError for ParseError {
|
||||
} => Some(format!("Expected \"{expected}\", found \"{found}\"")),
|
||||
Self::ExpectedTokenMultiple {
|
||||
expected, found, ..
|
||||
} => Some(format!("Expected one of {expected:?}, found \"{found}\"")),
|
||||
} => {
|
||||
let expected = expected
|
||||
.iter()
|
||||
.map(|kind| kind.to_string() + ", ")
|
||||
.collect::<String>();
|
||||
|
||||
Some(format!("Expected one of {expected}, found \"{found}\""))
|
||||
}
|
||||
Self::InvalidAssignmentTarget { found, .. } => {
|
||||
Some(format!("Invalid assignment target, found \"{found}\""))
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ fn if_else_expression() {
|
||||
(Instruction::jump(1, true), Span(5, 7)),
|
||||
(Instruction::load_constant(0, 2), Span(12, 13)),
|
||||
(Instruction::load_constant(1, 3), Span(23, 24)),
|
||||
(Instruction::r#return(), Span(0, 26)),
|
||||
(Instruction::r#return(1, 1), Span(0, 26)),
|
||||
],
|
||||
vec![
|
||||
Value::integer(1),
|
||||
@ -98,8 +98,8 @@ fn list_with_expression() {
|
||||
Span(6, 7)
|
||||
),
|
||||
(Instruction::load_constant(2, 3), Span(11, 12)),
|
||||
(Instruction::load_list(3, 3), Span(0, 13)),
|
||||
(Instruction::r#return(), Span(0, 13)),
|
||||
(Instruction::load_list(3, 0, 3), Span(0, 13)),
|
||||
(Instruction::r#return(3, 3), Span(0, 13)),
|
||||
],
|
||||
vec![
|
||||
Value::integer(1),
|
||||
@ -123,8 +123,8 @@ fn list() {
|
||||
(Instruction::load_constant(0, 0), Span(1, 2)),
|
||||
(Instruction::load_constant(1, 1), Span(4, 5)),
|
||||
(Instruction::load_constant(2, 2), Span(7, 8)),
|
||||
(Instruction::load_list(3, 3), Span(0, 9)),
|
||||
(Instruction::r#return(), Span(0, 9)),
|
||||
(Instruction::load_list(3, 0, 3), Span(0, 9)),
|
||||
(Instruction::r#return(3, 3), Span(0, 9)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2), Value::integer(3),],
|
||||
vec![]
|
||||
@ -217,7 +217,7 @@ fn parentheses_precedence() {
|
||||
*Instruction::multiply(1, 0, 2).set_second_argument_to_constant(),
|
||||
Span(8, 9)
|
||||
),
|
||||
(Instruction::r#return(), Span(0, 11)),
|
||||
(Instruction::r#return(1, 1), Span(0, 11)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2), Value::integer(3)],
|
||||
vec![]
|
||||
@ -232,7 +232,7 @@ fn math_operator_precedence() {
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(
|
||||
*Instruction::multiply(1, 2, 3)
|
||||
*Instruction::multiply(2, 2, 3)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(10, 11)
|
||||
@ -244,11 +244,11 @@ fn math_operator_precedence() {
|
||||
Span(2, 3)
|
||||
),
|
||||
(
|
||||
*Instruction::divide(2, 1, 4).set_second_argument_to_constant(),
|
||||
*Instruction::divide(3, 2, 4).set_second_argument_to_constant(),
|
||||
Span(14, 15)
|
||||
),
|
||||
(Instruction::subtract(3, 0, 2), Span(6, 7)),
|
||||
(Instruction::r#return(), Span(0, 17)),
|
||||
(Instruction::subtract(1, 0, 3), Span(6, 7)),
|
||||
(Instruction::r#return(1, 1), Span(0, 17)),
|
||||
],
|
||||
vec![
|
||||
Value::integer(1),
|
||||
@ -280,18 +280,16 @@ fn declare_local() {
|
||||
#[test]
|
||||
fn and() {
|
||||
assert_eq!(
|
||||
parse("1 && 2"),
|
||||
parse("true && false"),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(
|
||||
*Instruction::and(0, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 4)
|
||||
),
|
||||
(Instruction::r#return(), Span(0, 6)),
|
||||
(Instruction::load_boolean(0, true, false), Span(0, 4)),
|
||||
(Instruction::test(0, true), Span(5, 7)),
|
||||
(Instruction::jump(1, true), Span(5, 7)),
|
||||
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
||||
(Instruction::r#return(1, 1), Span(0, 13)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
@ -309,7 +307,7 @@ fn divide() {
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),
|
||||
(Instruction::r#return(), Span(0, 5)),
|
||||
(Instruction::r#return(0, 0), Span(0, 5)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
@ -329,7 +327,7 @@ fn multiply() {
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),
|
||||
(Instruction::r#return(), Span(0, 5)),
|
||||
(Instruction::r#return(0, 0), Span(0, 5)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
@ -349,7 +347,7 @@ fn add() {
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),
|
||||
(Instruction::r#return(), Span(0, 5)),
|
||||
(Instruction::r#return(0, 0), Span(0, 5)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
@ -369,7 +367,7 @@ fn subtract() {
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),
|
||||
(Instruction::r#return(), Span(0, 5)),
|
||||
(Instruction::r#return(0, 0), Span(0, 5)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
@ -384,7 +382,7 @@ fn constant() {
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::load_constant(0, 0), Span(0, 2)),
|
||||
(Instruction::r#return(), Span(0, 2)),
|
||||
(Instruction::r#return(0, 0), Span(0, 2)),
|
||||
],
|
||||
vec![Value::integer(42)],
|
||||
vec![]
|
||||
|
@ -186,21 +186,34 @@ impl Vm {
|
||||
|
||||
self.insert(remainder, instruction.destination(), position)?;
|
||||
}
|
||||
Operation::And => {
|
||||
let (left, right) = take_constants_or_clone(self, instruction, position)?;
|
||||
let and = left
|
||||
.and(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
Operation::Test => {
|
||||
let register = instruction.destination();
|
||||
let test_value = instruction.second_argument_as_boolean();
|
||||
let value = self.clone(register, position)?;
|
||||
let boolean = value.as_boolean().ok_or_else(|| VmError::ExpectedBoolean {
|
||||
found: value,
|
||||
position,
|
||||
})?;
|
||||
|
||||
self.insert(and, instruction.destination(), position)?;
|
||||
if boolean != test_value {
|
||||
self.ip += 1;
|
||||
}
|
||||
}
|
||||
Operation::Or => {
|
||||
let (left, right) = take_constants_or_clone(self, instruction, position)?;
|
||||
let or = left
|
||||
.or(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
Operation::TestSet => {
|
||||
let to_register = instruction.destination();
|
||||
let argument = instruction.first_argument();
|
||||
let test_value = instruction.second_argument_as_boolean();
|
||||
let value = self.clone(argument, position)?;
|
||||
let boolean = value.as_boolean().ok_or_else(|| VmError::ExpectedBoolean {
|
||||
found: value.clone(),
|
||||
position,
|
||||
})?;
|
||||
|
||||
self.insert(or, instruction.destination(), position)?;
|
||||
if boolean == test_value {
|
||||
self.insert(value, to_register, position)?;
|
||||
} else {
|
||||
self.ip += 1;
|
||||
}
|
||||
}
|
||||
Operation::Equal => {
|
||||
let (left, right) = take_constants_or_clone(self, instruction, position)?;
|
||||
@ -279,9 +292,9 @@ impl Vm {
|
||||
self.ip = new_ip;
|
||||
}
|
||||
Operation::Return => {
|
||||
let value = self.pop(position)?;
|
||||
let return_value = self.pop(position)?;
|
||||
|
||||
return Ok(Some(value));
|
||||
return Ok(Some(return_value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -305,6 +318,20 @@ impl Vm {
|
||||
}
|
||||
}
|
||||
|
||||
fn take(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
|
||||
let index = index as usize;
|
||||
|
||||
if let Some(register) = self.register_stack.get_mut(index) {
|
||||
let value = register
|
||||
.take()
|
||||
.ok_or_else(|| VmError::EmptyRegister { index, position })?;
|
||||
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(VmError::RegisterIndexOutOfBounds { position })
|
||||
}
|
||||
}
|
||||
|
||||
fn clone(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
|
||||
let index = index as usize;
|
||||
|
||||
@ -397,6 +424,10 @@ pub enum VmError {
|
||||
index: usize,
|
||||
position: Span,
|
||||
},
|
||||
ExpectedBoolean {
|
||||
found: Value,
|
||||
position: Span,
|
||||
},
|
||||
RegisterIndexOutOfBounds {
|
||||
position: Span,
|
||||
},
|
||||
@ -437,6 +468,7 @@ impl AnnotatedError for VmError {
|
||||
fn description(&self) -> &'static str {
|
||||
match self {
|
||||
Self::EmptyRegister { .. } => "Empty register",
|
||||
Self::ExpectedBoolean { .. } => "Expected boolean",
|
||||
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
||||
Self::InvalidInstruction { .. } => "Invalid instruction",
|
||||
Self::StackOverflow { .. } => "Stack overflow",
|
||||
@ -462,6 +494,7 @@ impl AnnotatedError for VmError {
|
||||
fn position(&self) -> Span {
|
||||
match self {
|
||||
Self::EmptyRegister { position, .. } => *position,
|
||||
Self::ExpectedBoolean { position, .. } => *position,
|
||||
Self::RegisterIndexOutOfBounds { position } => *position,
|
||||
Self::InvalidInstruction { position, .. } => *position,
|
||||
Self::StackUnderflow { position } => *position,
|
||||
|
Loading…
Reference in New Issue
Block a user