diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 9c9e41f..8fa79a7 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -399,10 +399,10 @@ impl<'a> ChunkDisassembler<'a> { let bytecode = u32::from(instruction); let instruction_display = if let Some(info) = info_option { - format!("{index:<5} {bytecode:<8X} {operation:15} {info:30} {position:8}") + format!("{index:<5} {bytecode:<08X} {operation:15} {info:30} {position:8}") } else { format!( - "{index:<5} {bytecode:<8X} {operation:15} {:30} {position:8}", + "{index:<5} {bytecode:<08X} {operation:15} {:30} {position:8}", " " ) }; diff --git a/dust-lang/src/parser/mod.rs b/dust-lang/src/parser/mod.rs index 667c914..082684c 100644 --- a/dust-lang/src/parser/mod.rs +++ b/dust-lang/src/parser/mod.rs @@ -477,9 +477,31 @@ impl<'src> Parser<'src> { } fn parse_comparison_binary(&mut self) -> Result<(), ParseError> { + let (previous_booleans, equal_argument) = if let [Some(Operation::LoadBoolean), Some(Operation::LoadBoolean), Some(Operation::Jump), Some(Operation::Equal)] = + self.chunk.get_last_n_operations() + { + self.increment_register()?; + + let load_booleans = ( + self.chunk.pop_instruction(self.current_position)?, + self.chunk.pop_instruction(self.current_position)?, + ); + + let (jump, jump_position) = self.chunk.pop_instruction(self.current_position)?; + let (equal, equal_position) = self.chunk.pop_instruction(self.current_position)?; + let equal_argument = equal.second_argument(); + let is_constant = equal.second_argument_is_constant(); + + self.emit_instruction(equal, equal_position); + self.emit_instruction(jump, jump_position); + + (Some(load_booleans), Some((equal_argument, is_constant))) + } else { + (None, None) + }; + let (left_instruction, left_position) = self.chunk.pop_instruction(self.current_position)?; - let (push_back_left, left_is_constant, left) = self.handle_binary_argument(&left_instruction)?; @@ -514,13 +536,18 @@ impl<'src> Parser<'src> { let (right_instruction, right_position) = self.chunk.pop_instruction(self.current_position)?; - let (push_back_right, right_is_constant, right) = self.handle_binary_argument(&right_instruction)?; instruction.set_second_argument(right); - if left_is_constant { + if let Some((equal_argument, is_constant)) = equal_argument { + instruction.set_first_argument(equal_argument); + + if is_constant { + instruction.set_first_argument_to_constant(); + } + } else if left_is_constant { instruction.set_first_argument_to_constant(); } @@ -528,17 +555,6 @@ impl<'src> Parser<'src> { instruction.set_second_argument_to_constant(); } - self.emit_instruction(instruction, operator_position); - self.emit_instruction(Instruction::jump(1, true), operator_position); - self.emit_instruction( - Instruction::load_boolean(self.current_register, true, true), - operator_position, - ); - self.emit_instruction( - Instruction::load_boolean(self.current_register, false, false), - operator_position, - ); - if push_back_left { self.emit_instruction(left_instruction, left_position); } @@ -547,6 +563,25 @@ impl<'src> Parser<'src> { self.emit_instruction(right_instruction, right_position); } + self.emit_instruction(instruction, operator_position); + self.emit_instruction(Instruction::jump(1, true), operator_position); + + if let Some(((left_boolean, left_position), (right_boolean, right_position))) = + previous_booleans + { + self.emit_instruction(right_boolean, right_position); + self.emit_instruction(left_boolean, left_position); + } else { + self.emit_instruction( + Instruction::load_boolean(self.current_register, true, true), + operator_position, + ); + self.emit_instruction( + Instruction::load_boolean(self.current_register, false, false), + operator_position, + ); + } + Ok(()) } diff --git a/dust-lang/src/parser/tests.rs b/dust-lang/src/parser/tests.rs index 7ca953b..87c4092 100644 --- a/dust-lang/src/parser/tests.rs +++ b/dust-lang/src/parser/tests.rs @@ -2,6 +2,37 @@ use crate::Local; use super::*; +#[test] +fn equality_chain() { + let source = "1 == 2 == 3;"; + + 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(2, 4) + ), + (Instruction::jump(1, true), Span(2, 4)), + ( + *Instruction::equal(true, 1, 2) + .set_first_argument_to_constant() + .set_second_argument_to_constant(), + Span(7, 9) + ), + (Instruction::jump(1, true), Span(7, 9)), + (Instruction::load_boolean(0, true, true), Span(2, 4)), + (Instruction::load_boolean(0, false, false), Span(2, 4)), + ], + vec![Value::integer(1), Value::integer(2), Value::integer(3)], + vec![] + )), + ); +} + #[test] fn not() { let source = "!true"; diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index dbdef8a..e474171 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -42,7 +42,7 @@ impl Vm { vm: &mut Vm, instruction: Instruction, position: Span, - ) -> Result<(Value, Value), VmError> { + ) -> Result<(Value, &Value), VmError> { let left = if instruction.first_argument_is_constant() { vm.chunk .take_constant(instruction.first_argument(), position)? @@ -51,9 +51,9 @@ impl Vm { }; let right = if instruction.second_argument_is_constant() { vm.chunk - .take_constant(instruction.second_argument(), position)? + .get_constant(instruction.second_argument(), position)? } else { - vm.clone(instruction.second_argument(), position)? + vm.get(instruction.second_argument(), position)? }; Ok((left, right)) @@ -151,7 +151,7 @@ impl Vm { Operation::Add => { let (left, right) = take_constants_or_clone(self, instruction, position)?; let sum = left - .add(&right) + .add(right) .map_err(|error| VmError::Value { error, position })?; self.insert(sum, instruction.destination(), position)?; @@ -159,7 +159,7 @@ impl Vm { Operation::Subtract => { let (left, right) = take_constants_or_clone(self, instruction, position)?; let difference = left - .subtract(&right) + .subtract(right) .map_err(|error| VmError::Value { error, position })?; self.insert(difference, instruction.destination(), position)?; @@ -167,7 +167,7 @@ impl Vm { Operation::Multiply => { let (left, right) = take_constants_or_clone(self, instruction, position)?; let product = left - .multiply(&right) + .multiply(right) .map_err(|error| VmError::Value { error, position })?; self.insert(product, instruction.destination(), position)?; @@ -175,7 +175,7 @@ impl Vm { Operation::Divide => { let (left, right) = take_constants_or_clone(self, instruction, position)?; let quotient = left - .divide(&right) + .divide(right) .map_err(|error| VmError::Value { error, position })?; self.insert(quotient, instruction.destination(), position)?; @@ -183,7 +183,7 @@ impl Vm { Operation::Modulo => { let (left, right) = take_constants_or_clone(self, instruction, position)?; let remainder = left - .modulo(&right) + .modulo(right) .map_err(|error| VmError::Value { error, position })?; self.insert(remainder, instruction.destination(), position)?; @@ -220,7 +220,7 @@ impl Vm { Operation::Equal => { let (left, right) = take_constants_or_clone(self, instruction, position)?; let equal = left - .equal(&right) + .equal(right) .map_err(|error| VmError::Value { error, position })?; let compare_to = instruction.destination_as_boolean(); @@ -233,7 +233,7 @@ impl Vm { Operation::Less => { let (left, right) = take_constants_or_clone(self, instruction, position)?; let less = left - .less_than(&right) + .less_than(right) .map_err(|error| VmError::Value { error, position })?; let compare_to = instruction.destination() != 0; @@ -246,7 +246,7 @@ impl Vm { Operation::LessEqual => { let (left, right) = take_constants_or_clone(self, instruction, position)?; let less_equal = left - .less_than_or_equal(&right) + .less_than_or_equal(right) .map_err(|error| VmError::Value { error, position })?; let compare_to = instruction.destination() != 0; @@ -342,6 +342,20 @@ impl Vm { } } + fn get(&self, index: u8, position: Span) -> Result<&Value, VmError> { + let index = index as usize; + + if let Some(register) = self.register_stack.get(index) { + let value = register + .as_ref() + .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;