Pass all but one test

This commit is contained in:
Jeff 2024-09-19 11:41:18 -04:00
parent 03113fdf5e
commit be77d64c39
6 changed files with 372 additions and 245 deletions

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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"),

View File

@ -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}\""))
}

View File

@ -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![]

View File

@ -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,