From bf4f3193022195560f26904630c683ea1d76b57e Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 18 Sep 2024 22:00:24 -0400 Subject: [PATCH] Add and pass tests --- dust-lang/src/chunk.rs | 9 +++-- dust-lang/src/instruction.rs | 16 ++++---- dust-lang/src/parser/mod.rs | 75 +++++++++++++++++++++-------------- dust-lang/src/parser/tests.rs | 50 +++++++++++++++++++++++ 4 files changed, 109 insertions(+), 41 deletions(-) diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 4f38524..da8ef71 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -379,11 +379,12 @@ impl<'a> ChunkDisassembler<'a> { .as_ref() .map(|value| value.to_string()) .unwrap_or("empty".to_string()); - let elipsis = if value_display.len() > 9 { "..." } else { "" }; - let constant_display = if value_display.len() > 9 { - format!("{index:<5} {value_display:^7.7}{elipsis}") + let trucated_length = 8; + let with_elipsis = trucated_length - 3; + let constant_display = if value_display.len() > with_elipsis { + format!("{index:<5} {value_display:. { let destination = self.destination(); - let first_index = destination - self.first_argument(); + let first_index = destination - (self.first_argument() - 1); let last_index = destination - 1; format!("R{} = [R{}..=R{}]", destination, first_index, last_index) @@ -390,43 +390,43 @@ impl Instruction { let destination = self.destination(); let (first_argument, second_argument) = format_arguments(); - format!("R({destination}) = {first_argument} + {second_argument}",) + format!("R{destination} = {first_argument} + {second_argument}",) } Operation::Subtract => { let destination = self.destination(); let (first_argument, second_argument) = format_arguments(); - format!("R({destination}) = {first_argument} - {second_argument}",) + format!("R{destination} = {first_argument} - {second_argument}",) } Operation::Multiply => { let destination = self.destination(); let (first_argument, second_argument) = format_arguments(); - format!("R({destination}) = {first_argument} * {second_argument}",) + format!("R{destination} = {first_argument} * {second_argument}",) } Operation::Divide => { let destination = self.destination(); let (first_argument, second_argument) = format_arguments(); - format!("R({destination}) = {first_argument} / {second_argument}",) + format!("R{destination} = {first_argument} / {second_argument}",) } Operation::Modulo => { let destination = self.destination(); let (first_argument, second_argument) = format_arguments(); - format!("R({destination}) = {first_argument} % {second_argument}",) + format!("R{destination} = {first_argument} % {second_argument}",) } Operation::And => { let destination = self.destination(); let (first_argument, second_argument) = format_arguments(); - format!("R({destination}) = {first_argument} && {second_argument}",) + format!("R{destination} = {first_argument} && {second_argument}",) } Operation::Or => { let destination = self.destination(); let (first_argument, second_argument) = format_arguments(); - format!("R({destination}) = {first_argument} || {second_argument}",) + format!("R{destination} = {first_argument} || {second_argument}",) } Operation::Equal => { let comparison_symbol = if self.destination_as_boolean() { diff --git a/dust-lang/src/parser/mod.rs b/dust-lang/src/parser/mod.rs index e0920fa..cdfa649 100644 --- a/dust-lang/src/parser/mod.rs +++ b/dust-lang/src/parser/mod.rs @@ -149,26 +149,26 @@ impl<'src> Parser<'src> { let boolean = text.parse::().unwrap(); - if let Ok((last_instruction, _)) = - self.chunk.get_last_instruction(self.current_position) + if let Ok((last_instruction, _)) = self + .chunk + .get_last_instruction(self.current_position) + .copied() { let skip = last_instruction.operation() == Operation::Jump; - let destination = if let Operation::LoadBoolean = last_instruction.operation() { - last_instruction.destination() - } else { - self.current_register - }; self.emit_instruction( - Instruction::load_boolean(destination, boolean, skip), + Instruction::load_boolean(self.current_register, boolean, skip), self.previous_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, ); - self.increment_register()?; } } @@ -313,25 +313,20 @@ impl<'src> Parser<'src> { let mut push_back_left = false; let mut left_is_constant = false; let left = match left_instruction.operation() { - Operation::LoadConstant => { + Operation::LoadConstant | Operation::GetLocal => { log::trace!( "Condensing {} to binary expression", left_instruction.operation() ); - left_is_constant = true; + left_is_constant = matches!(left_instruction.operation(), Operation::LoadConstant); self.decrement_register()?; left_instruction.first_argument() } - Operation::GetLocal => { - log::trace!( - "Condensing {} to binary expression", - left_instruction.operation() - ); - + Operation::LoadBoolean => { self.decrement_register()?; - left_instruction.first_argument() + left_instruction.destination() } Operation::Close => { return Err(ParseError::ExpectedExpression { @@ -358,25 +353,21 @@ impl<'src> Parser<'src> { let mut push_back_right = false; let mut right_is_constant = false; let right = match right_instruction.operation() { - Operation::LoadConstant => { + Operation::LoadConstant | Operation::GetLocal => { log::trace!( "Condensing {} to binary expression", right_instruction.operation() ); - right_is_constant = true; + right_is_constant = + matches!(right_instruction.operation(), Operation::LoadConstant); self.decrement_register()?; right_instruction.first_argument() } - Operation::GetLocal => { - log::trace!( - "Condensing {} to binary expression", - right_instruction.operation() - ); - + Operation::LoadBoolean => { self.decrement_register()?; - right_instruction.first_argument() + right_instruction.destination() } Operation::Close => { return Err(ParseError::ExpectedExpression { @@ -665,10 +656,36 @@ impl<'src> Parser<'src> { self.expect(TokenKind::Equal)?; self.parse_expression()?; - let register = self.chunk.get_last_instruction(position)?.0.destination(); + let (previous_instruction, previous_position) = + *self.chunk.get_last_instruction(position)?; + let register = previous_instruction.destination(); let local_index = self.chunk - .declare_local(identifier, is_mutable, register, self.current_position)?; + .declare_local(identifier, is_mutable, register, previous_position)?; + + // Optimize for assignment to a comparison + if let Operation::Jump = previous_instruction.operation() { + let (jump, jump_position) = self.chunk.pop_instruction(self.current_position)?; + + if let Operation::Equal = self + .chunk + .get_last_instruction(self.current_position)? + .0 + .operation() + { + self.emit_instruction(jump, jump_position); + self.emit_instruction( + Instruction::load_boolean(self.current_register, true, true), + self.current_position, + ); + self.emit_instruction( + Instruction::load_boolean(self.current_register, false, false), + self.current_position, + ); + } else { + self.emit_instruction(jump, jump_position); + } + } self.emit_instruction( Instruction::define_local(register, local_index, is_mutable), diff --git a/dust-lang/src/parser/tests.rs b/dust-lang/src/parser/tests.rs index d08ca85..aa5b33c 100644 --- a/dust-lang/src/parser/tests.rs +++ b/dust-lang/src/parser/tests.rs @@ -2,6 +2,56 @@ use crate::Local; use super::*; +#[test] +fn equality_assignment_long() { + let source = "let a = if 4 == 4 { true } else { false };"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + vec![ + ( + *Instruction::equal(true, 0, 1) + .set_first_argument_to_constant() + .set_second_argument_to_constant(), + Span(13, 15) + ), + (Instruction::jump(1, true), Span(13, 15)), + (Instruction::load_boolean(0, true, true), Span(20, 24)), + (Instruction::load_boolean(0, false, false), Span(34, 39)), + (Instruction::define_local(0, 0, false), Span(4, 5)), + ], + vec![Value::integer(4), Value::integer(4),], + vec![Local::new(Identifier::new("a"), false, 0, Some(0)),] + )), + ); +} + +#[test] +fn equality_assignment_short() { + let source = "let a = 4 == 4;"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + vec![ + ( + *Instruction::equal(true, 0, 1) + .set_first_argument_to_constant() + .set_second_argument_to_constant(), + Span(10, 12) + ), + (Instruction::jump(1, true), Span(10, 12)), + (Instruction::load_boolean(0, true, true), Span(14, 15)), + (Instruction::load_boolean(0, false, false), Span(14, 15)), + (Instruction::define_local(0, 0, false), Span(4, 5)), + ], + vec![Value::integer(4), Value::integer(4),], + vec![Local::new(Identifier::new("a"), false, 0, Some(0)),] + )), + ); +} + #[test] fn if_else_expression() { let source = "if 1 == 1 { 2 } else { 3 }";