From be77d64c39eae18ecfea0cac8faed2e312c73e0c Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 19 Sep 2024 11:41:18 -0400 Subject: [PATCH] Pass all but one test --- dust-lang/src/chunk.rs | 21 +- dust-lang/src/instruction.rs | 108 +++++------ dust-lang/src/operation.rs | 25 +-- dust-lang/src/parser/mod.rs | 356 +++++++++++++++++++++------------- dust-lang/src/parser/tests.rs | 46 +++-- dust-lang/src/vm.rs | 61 ++++-- 6 files changed, 372 insertions(+), 245 deletions(-) diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index da8ef71..a87cbb2 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -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 { + 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; diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index c59d98a..4694f83 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -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); } } diff --git a/dust-lang/src/operation.rs b/dust-lang/src/operation.rs index b71629f..7418d8d 100644 --- a/dust-lang/src/operation.rs +++ b/dust-lang/src/operation.rs @@ -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 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 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"), diff --git a/dust-lang/src/parser/mod.rs b/dust-lang/src/parser/mod.rs index 95e4db2..3deafc1 100644 --- a/dust-lang/src/parser/mod.rs +++ b/dust-lang/src/parser/mod.rs @@ -18,7 +18,7 @@ pub fn parse(source: &str) -> Result { 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::().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::(); + + Some(format!("Expected one of {expected}, found \"{found}\"")) + } Self::InvalidAssignmentTarget { found, .. } => { Some(format!("Invalid assignment target, found \"{found}\"")) } diff --git a/dust-lang/src/parser/tests.rs b/dust-lang/src/parser/tests.rs index aa5b33c..7dd4a2b 100644 --- a/dust-lang/src/parser/tests.rs +++ b/dust-lang/src/parser/tests.rs @@ -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![] diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index a5f71a8..ac9726e 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -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 { + 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 { 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,