From b692fd89c3b369f0a67b3b4670e46ea979b72fe9 Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 3 Dec 2024 18:24:26 -0500 Subject: [PATCH] Add boolean chain short-circuiting --- dust-lang/src/compiler.rs | 29 ++++++++++++++++++++---- dust-lang/tests/logic_and_and.rs | 39 ++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 dust-lang/tests/logic_and_and.rs diff --git a/dust-lang/src/compiler.rs b/dust-lang/src/compiler.rs index 5a34c95..56d9360 100644 --- a/dust-lang/src/compiler.rs +++ b/dust-lang/src/compiler.rs @@ -866,7 +866,21 @@ impl<'src> Compiler<'src> { } fn parse_logical_binary(&mut self) -> Result<(), CompileError> { + let is_logic_chain = matches!( + self.get_last_operations(), + Some([Operation::Test, Operation::Jump, _]) + ); let (left_instruction, left_type, left_position) = self.pop_last_instruction()?; + let jump_index = self.instructions.len().saturating_sub(1); + let mut jump_distance = if is_logic_chain { + self.instructions.pop().map_or(0, |(jump, _, _)| { + let Jump { offset, .. } = Jump::from(&jump); + + offset + }) + } else { + 0 + }; if !left_instruction.yields_value() { return Err(CompileError::ExpectedExpression { @@ -903,16 +917,21 @@ impl<'src> Compiler<'src> { argument, test_value: test_boolean, }); - let jump = Instruction::from(Jump { - offset: 1, - is_positive: true, - }); self.advance()?; self.emit_instruction(test, Type::None, operator_position); - self.emit_instruction(jump, Type::None, operator_position); + self.emit_instruction(Instruction::jump(1, true), Type::None, operator_position); self.parse_sub_expression(&rule.precedence)?; + if is_logic_chain { + let expression_length = self.instructions.len() - jump_index - 1; + jump_distance += expression_length as u16; + let jump = Instruction::jump(jump_distance, true); + + self.instructions + .insert(jump_index, (jump, Type::None, operator_position)); + } + Ok(()) } diff --git a/dust-lang/tests/logic_and_and.rs b/dust-lang/tests/logic_and_and.rs new file mode 100644 index 0000000..afb51f2 --- /dev/null +++ b/dust-lang/tests/logic_and_and.rs @@ -0,0 +1,39 @@ +use dust_lang::*; + +#[test] +fn true_and_true_and_true() { + let source = "true && true && true"; + + assert_eq!( + compile(source), + Ok(Chunk::with_data( + None, + FunctionType { + type_parameters: None, + value_parameters: None, + return_type: Box::new(Type::Boolean), + }, + vec![ + ( + Instruction::load_boolean(Destination::Register(0), true, false), + Span(0, 4) + ), + (Instruction::test(Argument::Register(0), true), Span(5, 7)), + (Instruction::jump(1, true), Span(5, 7)), + ( + Instruction::load_boolean(Destination::Register(1), true, false), + Span(8, 12) + ), + (Instruction::test(Argument::Register(1), true), Span(13, 15)), + (Instruction::jump(1, true), Span(13, 15)), + ( + Instruction::load_boolean(Destination::Register(2), true, false), + Span(16, 20) + ), + (Instruction::r#return(true), Span(20, 20)), + ], + vec![], + vec![] + )) + ); +}