1
0

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 colored::Colorize;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{AnnotatedError, Identifier, Instruction, Span, Value}; use crate::{AnnotatedError, Identifier, Instruction, Operation, Span, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct Chunk { pub struct Chunk {
@ -40,6 +40,14 @@ impl Chunk {
self.scope_depth 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( pub fn get_instruction(
&self, &self,
offset: usize, offset: usize,
@ -54,18 +62,27 @@ impl Chunk {
self.instructions.push((instruction, position)); 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> { pub fn pop_instruction(&mut self, position: Span) -> Result<(Instruction, Span), ChunkError> {
self.instructions self.instructions
.pop() .pop()
.ok_or(ChunkError::InstructionUnderflow { position }) .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 self.instructions
.last() .last()
.ok_or(ChunkError::InstructionUnderflow { position }) .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> { pub fn get_constant(&self, index: u8, position: Span) -> Result<&Value, ChunkError> {
let index = index as usize; let index = index as usize;

View File

@ -43,39 +43,40 @@ impl Instruction {
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); let mut instruction = Instruction(Operation::LoadList as u32);
instruction.set_destination(to_register); instruction.set_destination(to_register);
instruction.set_first_argument(list_length); instruction.set_first_argument(start_register);
instruction.set_second_argument(list_length);
instruction 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); let mut instruction = Instruction(Operation::DefineLocal as u32);
instruction.set_destination(to_register); 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.set_second_argument(if is_mutable { 1 } else { 0 });
instruction 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); let mut instruction = Instruction(Operation::GetLocal as u32);
instruction.set_destination(to_register); instruction.set_destination(to_register);
instruction.set_first_argument(variable_index); instruction.set_first_argument(local_index);
instruction 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); let mut instruction = Instruction(Operation::SetLocal as u32);
instruction.set_destination(from_register); instruction.set_destination(from_register);
instruction.set_first_argument(variable_index); instruction.set_first_argument(local_index);
instruction instruction
} }
@ -130,22 +131,21 @@ impl Instruction {
instruction instruction
} }
pub fn and(to_register: u8, left_index: u8, right_index: u8) -> Instruction { pub fn test(to_register: u8, test_value: bool) -> Instruction {
let mut instruction = Instruction(Operation::And as u32); let mut instruction = Instruction(Operation::Test as u32);
instruction.set_destination(to_register); instruction.set_destination(to_register);
instruction.set_first_argument(left_index); instruction.set_second_argument_to_boolean(test_value);
instruction.set_second_argument(right_index);
instruction instruction
} }
pub fn or(to_register: u8, left_index: u8, right_index: u8) -> Instruction { pub fn test_set(to_register: u8, argument_index: u8, test_value: bool) -> Instruction {
let mut instruction = Instruction(Operation::Or as u32); let mut instruction = Instruction(Operation::TestSet as u32);
instruction.set_destination(to_register); instruction.set_destination(to_register);
instruction.set_first_argument(left_index); instruction.set_first_argument(argument_index);
instruction.set_second_argument(right_index); instruction.set_second_argument_to_boolean(test_value);
instruction instruction
} }
@ -187,8 +187,13 @@ impl Instruction {
instruction instruction
} }
pub fn r#return() -> Instruction { pub fn r#return(from_register: u8, to_register: u8) -> Instruction {
Instruction(Operation::Return as u32) 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 { pub fn operation(&self) -> Operation {
@ -339,7 +344,7 @@ impl Instruction {
} }
Operation::LoadList => { Operation::LoadList => {
let destination = self.destination(); let destination = self.destination();
let first_index = destination - (self.first_argument() - 1); let first_index = self.first_argument();
let last_index = destination - 1; let last_index = destination - 1;
format!("R{} = [R{}..=R{}]", destination, first_index, last_index) format!("R{} = [R{}..=R{}]", destination, first_index, last_index)
@ -416,17 +421,22 @@ impl Instruction {
format!("R{destination} = {first_argument} % {second_argument}",) format!("R{destination} = {first_argument} % {second_argument}",)
} }
Operation::And => { Operation::Test => {
let destination = self.destination(); 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 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 => { Operation::Equal => {
let comparison_symbol = if self.destination_as_boolean() { let comparison_symbol = if self.destination_as_boolean() {
@ -489,7 +499,12 @@ impl Instruction {
format!("IP -= {}", offset) 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 trucated_length = 30;
let with_elipsis = trucated_length - 3; let with_elipsis = trucated_length - 3;
@ -637,32 +652,21 @@ mod tests {
#[test] #[test]
fn and() { fn and() {
let mut instruction = Instruction::and(0, 1, 2); let instruction = Instruction::test(4, true);
instruction.set_first_argument_to_constant(); assert_eq!(instruction.operation(), Operation::Test);
instruction.set_second_argument_to_constant(); assert_eq!(instruction.destination(), 4);
assert!(instruction.second_argument_as_boolean());
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());
} }
#[test] #[test]
fn or() { fn or() {
let mut instruction = Instruction::or(0, 1, 2); let instruction = Instruction::test_set(4, 1, true);
instruction.set_first_argument_to_constant(); assert_eq!(instruction.operation(), Operation::TestSet);
instruction.set_second_argument_to_constant(); assert_eq!(instruction.destination(), 4);
assert_eq!(instruction.operation(), Operation::Or);
assert_eq!(instruction.destination(), 0);
assert_eq!(instruction.first_argument(), 1); assert_eq!(instruction.first_argument(), 1);
assert_eq!(instruction.second_argument(), 2); assert!(instruction.second_argument_as_boolean());
assert!(instruction.first_argument_is_constant());
assert!(instruction.second_argument_is_constant());
} }
#[test] #[test]
@ -710,10 +714,7 @@ mod tests {
#[test] #[test]
fn jump() { fn jump() {
let mut instruction = Instruction::jump(4, true); let instruction = Instruction::jump(4, true);
instruction.set_first_argument_to_constant();
instruction.set_second_argument_to_constant();
assert_eq!(instruction.operation(), Operation::Jump); assert_eq!(instruction.operation(), Operation::Jump);
assert_eq!(instruction.first_argument(), 4); assert_eq!(instruction.first_argument(), 4);
@ -722,13 +723,10 @@ mod tests {
#[test] #[test]
fn r#return() { fn r#return() {
let mut instruction = Instruction::r#return(); let instruction = Instruction::r#return(4, 8);
instruction.set_first_argument_to_constant();
instruction.set_second_argument_to_constant();
assert_eq!(instruction.operation(), Operation::Return); assert_eq!(instruction.operation(), Operation::Return);
assert!(instruction.first_argument_is_constant()); assert_eq!(instruction.destination(), 4);
assert!(instruction.second_argument_is_constant()); assert_eq!(instruction.first_argument(), 8);
} }
} }

View File

@ -16,8 +16,9 @@ const SUBTRACT: u8 = 0b0000_1001;
const MULTIPLY: u8 = 0b0000_1010; const MULTIPLY: u8 = 0b0000_1010;
const DIVIDE: u8 = 0b0000_1011; const DIVIDE: u8 = 0b0000_1011;
const MODULO: u8 = 0b0000_1100; 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 EQUAL: u8 = 0b0000_1111;
const LESS: u8 = 0b0001_0000; const LESS: u8 = 0b0001_0000;
@ -51,8 +52,10 @@ pub enum Operation {
Multiply = MULTIPLY as isize, Multiply = MULTIPLY as isize,
Divide = DIVIDE as isize, Divide = DIVIDE as isize,
Modulo = MODULO 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 // Relational operations
Equal = EQUAL as isize, Equal = EQUAL as isize,
@ -77,8 +80,6 @@ impl Operation {
| Operation::Multiply | Operation::Multiply
| Operation::Divide | Operation::Divide
| Operation::Modulo | Operation::Modulo
| Operation::And
| Operation::Or
) )
} }
} }
@ -99,8 +100,8 @@ impl From<u8> for Operation {
MULTIPLY => Operation::Multiply, MULTIPLY => Operation::Multiply,
DIVIDE => Operation::Divide, DIVIDE => Operation::Divide,
MODULO => Operation::Modulo, MODULO => Operation::Modulo,
AND => Operation::And, TEST => Operation::Test,
OR => Operation::Or, TEST_SET => Operation::TestSet,
EQUAL => Operation::Equal, EQUAL => Operation::Equal,
LESS => Operation::Less, LESS => Operation::Less,
LESS_EQUAL => Operation::LessEqual, LESS_EQUAL => Operation::LessEqual,
@ -135,8 +136,8 @@ impl From<Operation> for u8 {
Operation::Multiply => MULTIPLY, Operation::Multiply => MULTIPLY,
Operation::Divide => DIVIDE, Operation::Divide => DIVIDE,
Operation::Modulo => MODULO, Operation::Modulo => MODULO,
Operation::And => AND, Operation::Test => TEST,
Operation::Or => OR, Operation::TestSet => TEST_SET,
Operation::Equal => EQUAL, Operation::Equal => EQUAL,
Operation::Less => LESS, Operation::Less => LESS,
Operation::LessEqual => LESS_EQUAL, Operation::LessEqual => LESS_EQUAL,
@ -164,8 +165,8 @@ impl Display for Operation {
Operation::Multiply => write!(f, "MULTIPLY"), Operation::Multiply => write!(f, "MULTIPLY"),
Operation::Divide => write!(f, "DIVIDE"), Operation::Divide => write!(f, "DIVIDE"),
Operation::Modulo => write!(f, "MODULO"), Operation::Modulo => write!(f, "MODULO"),
Operation::And => write!(f, "AND"), Operation::Test => write!(f, "TEST"),
Operation::Or => write!(f, "OR"), Operation::TestSet => write!(f, "TEST_SET"),
Operation::Equal => write!(f, "EQUAL"), Operation::Equal => write!(f, "EQUAL"),
Operation::Less => write!(f, "LESS"), Operation::Less => write!(f, "LESS"),
Operation::LessEqual => write!(f, "LESS_EQUAL"), Operation::LessEqual => write!(f, "LESS_EQUAL"),

View File

@ -18,7 +18,7 @@ pub fn parse(source: &str) -> Result<Chunk, DustError> {
while !parser.is_eof() { while !parser.is_eof() {
parser parser
.parse_statement() .parse_statement(true)
.map_err(|error| DustError::Parse { error, source })?; .map_err(|error| DustError::Parse { error, source })?;
} }
@ -143,31 +143,31 @@ impl<'src> Parser<'src> {
Ok(()) 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 { if let Token::Boolean(text) = self.current_token {
let position = self.current_position;
self.advance()?; self.advance()?;
let boolean = text.parse::<bool>().unwrap(); let boolean = text.parse::<bool>().unwrap();
if let Ok((last_instruction, _)) = self if let Ok((last_instruction, _)) =
.chunk self.chunk.get_previous(self.current_position).copied()
.get_last_instruction(self.current_position)
.copied()
{ {
let skip = last_instruction.operation() == Operation::Jump; let skip = last_instruction.operation() == Operation::Jump;
self.emit_instruction( self.emit_instruction(
Instruction::load_boolean(self.current_register, boolean, skip), Instruction::load_boolean(self.current_register, boolean, skip),
self.previous_position, position,
); );
if let Operation::LoadBoolean = last_instruction.operation() {
self.increment_register()?;
}
} else { } else {
self.emit_instruction( self.emit_instruction(
Instruction::load_boolean(self.current_register, boolean, false), Instruction::load_boolean(self.current_register, boolean, false),
self.previous_position, position,
); );
} }
} }
@ -175,7 +175,11 @@ impl<'src> Parser<'src> {
Ok(()) 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 { if let Token::Byte(text) = self.current_token {
self.advance()?; self.advance()?;
@ -192,7 +196,11 @@ impl<'src> Parser<'src> {
Ok(()) 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 { if let Token::Character(character) = self.current_token {
self.advance()?; self.advance()?;
@ -204,7 +212,11 @@ impl<'src> Parser<'src> {
Ok(()) 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 { if let Token::Float(text) = self.current_token {
self.advance()?; self.advance()?;
@ -222,7 +234,11 @@ impl<'src> Parser<'src> {
Ok(()) 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 { if let Token::Integer(text) = self.current_token {
self.advance()?; self.advance()?;
@ -240,7 +256,11 @@ impl<'src> Parser<'src> {
Ok(()) 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 { if let Token::String(text) = self.current_token {
self.advance()?; self.advance()?;
@ -252,13 +272,21 @@ impl<'src> Parser<'src> {
Ok(()) 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.allow(TokenKind::LeftParenthesis)?;
self.parse_expression()?; self.parse_expression()?;
self.expect(TokenKind::RightParenthesis) 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 = self.current_token;
let operator_position = self.current_position; let operator_position = self.current_position;
@ -308,89 +336,65 @@ impl<'src> Parser<'src> {
} }
fn parse_binary(&mut self) -> Result<(), ParseError> { 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) = let (left_instruction, left_position) =
self.chunk.pop_instruction(self.current_position)?; self.chunk.pop_instruction(self.current_position)?;
let mut push_back_left = false; let (push_back_left, left_is_constant, left) = handle_argument(self, &left_instruction)?;
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 operator = self.current_token; let operator = self.current_token;
let operator_position = self.current_position; let operator_position = self.current_position;
let rule = ParseRule::from(&operator.kind()); let rule = ParseRule::from(&operator.kind());
self.advance()?; let (mut instruction, push_args_first) = match operator.kind() {
self.parse(rule.precedence.increment())?; 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) = (and_test, false)
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()
} }
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 { return Err(ParseError::ExpectedTokenMultiple {
expected: &[ 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 { if left_is_constant {
instruction.set_first_argument_to_constant(); instruction.set_first_argument_to_constant();
} }
@ -417,38 +435,47 @@ impl<'src> Parser<'src> {
instruction.set_second_argument_to_constant(); instruction.set_second_argument_to_constant();
} }
if push_back_left { if push_args_first {
self.emit_instruction(left_instruction, left_position); 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 { if !push_args_first {
self.emit_instruction(right_instruction, right_position); 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 jump_distance = if left_is_constant { 1 } else { 2 };
let distance = if push_back_left && push_back_right {
3
} else if push_back_left || push_back_right {
2
} else {
1
};
self.emit_instruction(Instruction::jump(distance, true), operator_position); self.emit_instruction(instruction, operator_position);
} else { self.emit_instruction(Instruction::jump(jump_distance, true), operator_position);
self.increment_register()?;
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(()) Ok(())
} }
fn parse_variable(&mut self, allow_assignment: bool) -> Result<(), ParseError> { fn parse_variable(
self.parse_named_variable(allow_assignment) &mut self,
} allow_assignment: bool,
_allow_return: bool,
fn parse_named_variable(&mut self, allow_assignment: bool) -> Result<(), ParseError> { ) -> Result<(), ParseError> {
let token = self.current_token.to_owned(); let token = self.current_token.to_owned();
let start_position = self.current_position; let start_position = self.current_position;
let local_index = self.parse_identifier_from(token, start_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); previous_instruction.set_destination(register_index);
self.emit_instruction(previous_instruction, self.current_position); self.emit_instruction(previous_instruction, self.current_position);
return Ok(());
} }
} }
@ -491,11 +520,11 @@ impl<'src> Parser<'src> {
start_position, start_position,
); );
} else { } else {
self.increment_register()?;
self.emit_instruction( self.emit_instruction(
Instruction::get_local(self.current_register, local_index), Instruction::get_local(self.current_register, local_index),
self.previous_position, self.previous_position,
); );
self.increment_register()?;
} }
Ok(()) 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.advance()?;
self.chunk.begin_scope(); self.chunk.begin_scope();
while !self.allow(TokenKind::RightCurlyBrace)? && !self.is_eof() { while !self.allow(TokenKind::RightCurlyBrace)? && !self.is_eof() {
self.parse_statement()?; self.parse_statement(allow_return)?;
} }
self.chunk.end_scope(); self.chunk.end_scope();
@ -539,11 +572,16 @@ impl<'src> Parser<'src> {
Ok(()) 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; let start = self.current_position.0;
self.advance()?; self.advance()?;
let start_register = self.current_register;
let mut length = 0; let mut length = 0;
while !self.allow(TokenKind::RightSquareBrace)? && !self.is_eof() { while !self.allow(TokenKind::RightSquareBrace)? && !self.is_eof() {
@ -570,29 +608,39 @@ impl<'src> Parser<'src> {
let end = self.current_position.1; let end = self.current_position.1;
self.emit_instruction( self.emit_instruction(
Instruction::load_list(self.current_register, length), Instruction::load_list(self.current_register, start_register, length),
Span(start, end), Span(start, end),
); );
self.increment_register()?;
Ok(()) 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.advance()?;
self.parse_expression()?; self.parse_expression()?;
self.parse_block(allow_assignment)?;
self.parse_block(allow_assignment, false)?;
if self.allow(TokenKind::Else)? { 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(()) 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.advance()?;
self.parse_expression()?; self.parse_expression()?;
self.parse_block(allow_assignment)?; self.parse_block(allow_assignment, allow_return)?;
Ok(()) Ok(())
} }
@ -601,18 +649,25 @@ impl<'src> Parser<'src> {
self.parse(Precedence::None) 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 start = self.current_position.0;
let is_expression = match self.current_token { let is_expression = match self.current_token {
Token::Let => { Token::Let => {
self.parse_let_statement(true)?; self.parse_let_statement(true, allow_return)?;
false false
} }
Token::LeftCurlyBrace => { 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()?; self.parse_expression()?;
@ -620,18 +675,36 @@ impl<'src> Parser<'src> {
true 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)?; 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 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(()) 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 { if !allow_assignment {
return Err(ParseError::ExpectedExpression { return Err(ParseError::ExpectedExpression {
found: self.current_token.to_owned(), found: self.current_token.to_owned(),
@ -658,8 +731,7 @@ impl<'src> Parser<'src> {
self.expect(TokenKind::Equal)?; self.expect(TokenKind::Equal)?;
self.parse_expression()?; self.parse_expression()?;
let (previous_instruction, previous_position) = let (previous_instruction, previous_position) = *self.chunk.get_previous(position)?;
*self.chunk.get_last_instruction(position)?;
let register = previous_instruction.destination(); let register = previous_instruction.destination();
let local_index = let local_index =
self.chunk self.chunk
@ -671,7 +743,7 @@ impl<'src> Parser<'src> {
if let Operation::Equal = self if let Operation::Equal = self
.chunk .chunk
.get_last_instruction(self.current_position)? .get_previous(self.current_position)?
.0 .0
.operation() .operation()
{ {
@ -700,6 +772,7 @@ impl<'src> Parser<'src> {
fn parse(&mut self, precedence: Precedence) -> Result<(), ParseError> { fn parse(&mut self, precedence: Precedence) -> Result<(), ParseError> {
let allow_assignment = precedence < Precedence::Assignment; let allow_assignment = precedence < Precedence::Assignment;
let allow_return = precedence == Precedence::None;
if let Some(prefix_parser) = ParseRule::from(&self.current_token.kind()).prefix { if let Some(prefix_parser) = ParseRule::from(&self.current_token.kind()).prefix {
log::trace!( log::trace!(
@ -707,7 +780,7 @@ impl<'src> Parser<'src> {
self.current_token, 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()); 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>; type InfixFunction<'a> = fn(&mut Parser<'a>) -> Result<(), ParseError>;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -1057,7 +1130,14 @@ impl AnnotatedError for ParseError {
} => Some(format!("Expected \"{expected}\", found \"{found}\"")), } => Some(format!("Expected \"{expected}\", found \"{found}\"")),
Self::ExpectedTokenMultiple { Self::ExpectedTokenMultiple {
expected, found, .. 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, .. } => { Self::InvalidAssignmentTarget { found, .. } => {
Some(format!("Invalid assignment target, found \"{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::jump(1, true), Span(5, 7)),
(Instruction::load_constant(0, 2), Span(12, 13)), (Instruction::load_constant(0, 2), Span(12, 13)),
(Instruction::load_constant(1, 3), Span(23, 24)), (Instruction::load_constant(1, 3), Span(23, 24)),
(Instruction::r#return(), Span(0, 26)), (Instruction::r#return(1, 1), Span(0, 26)),
], ],
vec![ vec![
Value::integer(1), Value::integer(1),
@ -98,8 +98,8 @@ fn list_with_expression() {
Span(6, 7) Span(6, 7)
), ),
(Instruction::load_constant(2, 3), Span(11, 12)), (Instruction::load_constant(2, 3), Span(11, 12)),
(Instruction::load_list(3, 3), Span(0, 13)), (Instruction::load_list(3, 0, 3), Span(0, 13)),
(Instruction::r#return(), Span(0, 13)), (Instruction::r#return(3, 3), Span(0, 13)),
], ],
vec![ vec![
Value::integer(1), Value::integer(1),
@ -123,8 +123,8 @@ fn list() {
(Instruction::load_constant(0, 0), Span(1, 2)), (Instruction::load_constant(0, 0), Span(1, 2)),
(Instruction::load_constant(1, 1), Span(4, 5)), (Instruction::load_constant(1, 1), Span(4, 5)),
(Instruction::load_constant(2, 2), Span(7, 8)), (Instruction::load_constant(2, 2), Span(7, 8)),
(Instruction::load_list(3, 3), Span(0, 9)), (Instruction::load_list(3, 0, 3), Span(0, 9)),
(Instruction::r#return(), Span(0, 9)), (Instruction::r#return(3, 3), Span(0, 9)),
], ],
vec![Value::integer(1), Value::integer(2), Value::integer(3),], vec![Value::integer(1), Value::integer(2), Value::integer(3),],
vec![] vec![]
@ -217,7 +217,7 @@ fn parentheses_precedence() {
*Instruction::multiply(1, 0, 2).set_second_argument_to_constant(), *Instruction::multiply(1, 0, 2).set_second_argument_to_constant(),
Span(8, 9) 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![Value::integer(1), Value::integer(2), Value::integer(3)],
vec![] vec![]
@ -232,7 +232,7 @@ fn math_operator_precedence() {
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ vec![
( (
*Instruction::multiply(1, 2, 3) *Instruction::multiply(2, 2, 3)
.set_first_argument_to_constant() .set_first_argument_to_constant()
.set_second_argument_to_constant(), .set_second_argument_to_constant(),
Span(10, 11) Span(10, 11)
@ -244,11 +244,11 @@ fn math_operator_precedence() {
Span(2, 3) 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) Span(14, 15)
), ),
(Instruction::subtract(3, 0, 2), Span(6, 7)), (Instruction::subtract(1, 0, 3), Span(6, 7)),
(Instruction::r#return(), Span(0, 17)), (Instruction::r#return(1, 1), Span(0, 17)),
], ],
vec![ vec![
Value::integer(1), Value::integer(1),
@ -280,18 +280,16 @@ fn declare_local() {
#[test] #[test]
fn and() { fn and() {
assert_eq!( assert_eq!(
parse("1 && 2"), parse("true && false"),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ vec![
( (Instruction::load_boolean(0, true, false), Span(0, 4)),
*Instruction::and(0, 0, 1) (Instruction::test(0, true), Span(5, 7)),
.set_first_argument_to_constant() (Instruction::jump(1, true), Span(5, 7)),
.set_second_argument_to_constant(), (Instruction::load_boolean(1, false, false), Span(8, 13)),
Span(2, 4) (Instruction::r#return(1, 1), Span(0, 13)),
),
(Instruction::r#return(), Span(0, 6)),
], ],
vec![Value::integer(1), Value::integer(2)], vec![],
vec![] vec![]
)) ))
); );
@ -309,7 +307,7 @@ fn divide() {
.set_second_argument_to_constant(), .set_second_argument_to_constant(),
Span(2, 3) 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![Value::integer(1), Value::integer(2)],
vec![] vec![]
@ -329,7 +327,7 @@ fn multiply() {
.set_second_argument_to_constant(), .set_second_argument_to_constant(),
Span(2, 3) 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![Value::integer(1), Value::integer(2)],
vec![] vec![]
@ -349,7 +347,7 @@ fn add() {
.set_second_argument_to_constant(), .set_second_argument_to_constant(),
Span(2, 3) 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![Value::integer(1), Value::integer(2)],
vec![] vec![]
@ -369,7 +367,7 @@ fn subtract() {
.set_second_argument_to_constant(), .set_second_argument_to_constant(),
Span(2, 3) 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![Value::integer(1), Value::integer(2)],
vec![] vec![]
@ -384,7 +382,7 @@ fn constant() {
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ vec![
(Instruction::load_constant(0, 0), Span(0, 2)), (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![Value::integer(42)],
vec![] vec![]

View File

@ -186,21 +186,34 @@ impl Vm {
self.insert(remainder, instruction.destination(), position)?; self.insert(remainder, instruction.destination(), position)?;
} }
Operation::And => { Operation::Test => {
let (left, right) = take_constants_or_clone(self, instruction, position)?; let register = instruction.destination();
let and = left let test_value = instruction.second_argument_as_boolean();
.and(&right) let value = self.clone(register, position)?;
.map_err(|error| VmError::Value { error, 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 => { Operation::TestSet => {
let (left, right) = take_constants_or_clone(self, instruction, position)?; let to_register = instruction.destination();
let or = left let argument = instruction.first_argument();
.or(&right) let test_value = instruction.second_argument_as_boolean();
.map_err(|error| VmError::Value { error, position })?; 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 => { Operation::Equal => {
let (left, right) = take_constants_or_clone(self, instruction, position)?; let (left, right) = take_constants_or_clone(self, instruction, position)?;
@ -279,9 +292,9 @@ impl Vm {
self.ip = new_ip; self.ip = new_ip;
} }
Operation::Return => { 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> { fn clone(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
let index = index as usize; let index = index as usize;
@ -397,6 +424,10 @@ pub enum VmError {
index: usize, index: usize,
position: Span, position: Span,
}, },
ExpectedBoolean {
found: Value,
position: Span,
},
RegisterIndexOutOfBounds { RegisterIndexOutOfBounds {
position: Span, position: Span,
}, },
@ -437,6 +468,7 @@ impl AnnotatedError for VmError {
fn description(&self) -> &'static str { fn description(&self) -> &'static str {
match self { match self {
Self::EmptyRegister { .. } => "Empty register", Self::EmptyRegister { .. } => "Empty register",
Self::ExpectedBoolean { .. } => "Expected boolean",
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds", Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
Self::InvalidInstruction { .. } => "Invalid instruction", Self::InvalidInstruction { .. } => "Invalid instruction",
Self::StackOverflow { .. } => "Stack overflow", Self::StackOverflow { .. } => "Stack overflow",
@ -462,6 +494,7 @@ impl AnnotatedError for VmError {
fn position(&self) -> Span { fn position(&self) -> Span {
match self { match self {
Self::EmptyRegister { position, .. } => *position, Self::EmptyRegister { position, .. } => *position,
Self::ExpectedBoolean { position, .. } => *position,
Self::RegisterIndexOutOfBounds { position } => *position, Self::RegisterIndexOutOfBounds { position } => *position,
Self::InvalidInstruction { position, .. } => *position, Self::InvalidInstruction { position, .. } => *position,
Self::StackUnderflow { position } => *position, Self::StackUnderflow { position } => *position,