1
0

Add test for equality chain

This commit is contained in:
Jeff 2024-09-23 08:57:49 -04:00
parent 1d0bafa4a3
commit d68c789ea8
4 changed files with 107 additions and 27 deletions

View File

@ -399,10 +399,10 @@ impl<'a> ChunkDisassembler<'a> {
let bytecode = u32::from(instruction); let bytecode = u32::from(instruction);
let instruction_display = if let Some(info) = info_option { 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 { } else {
format!( format!(
"{index:<5} {bytecode:<8X} {operation:15} {:30} {position:8}", "{index:<5} {bytecode:<08X} {operation:15} {:30} {position:8}",
" " " "
) )
}; };

View File

@ -477,9 +477,31 @@ impl<'src> Parser<'src> {
} }
fn parse_comparison_binary(&mut self) -> Result<(), ParseError> { 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) = let (left_instruction, left_position) =
self.chunk.pop_instruction(self.current_position)?; self.chunk.pop_instruction(self.current_position)?;
let (push_back_left, left_is_constant, left) = let (push_back_left, left_is_constant, left) =
self.handle_binary_argument(&left_instruction)?; self.handle_binary_argument(&left_instruction)?;
@ -514,13 +536,18 @@ impl<'src> Parser<'src> {
let (right_instruction, right_position) = let (right_instruction, right_position) =
self.chunk.pop_instruction(self.current_position)?; self.chunk.pop_instruction(self.current_position)?;
let (push_back_right, right_is_constant, right) = let (push_back_right, right_is_constant, right) =
self.handle_binary_argument(&right_instruction)?; self.handle_binary_argument(&right_instruction)?;
instruction.set_second_argument(right); 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(); instruction.set_first_argument_to_constant();
} }
@ -528,8 +555,23 @@ impl<'src> Parser<'src> {
instruction.set_second_argument_to_constant(); instruction.set_second_argument_to_constant();
} }
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); self.emit_instruction(instruction, operator_position);
self.emit_instruction(Instruction::jump(1, true), 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( self.emit_instruction(
Instruction::load_boolean(self.current_register, true, true), Instruction::load_boolean(self.current_register, true, true),
operator_position, operator_position,
@ -538,13 +580,6 @@ impl<'src> Parser<'src> {
Instruction::load_boolean(self.current_register, false, false), Instruction::load_boolean(self.current_register, false, false),
operator_position, operator_position,
); );
if push_back_left {
self.emit_instruction(left_instruction, left_position);
}
if push_back_right {
self.emit_instruction(right_instruction, right_position);
} }
Ok(()) Ok(())

View File

@ -2,6 +2,37 @@ use crate::Local;
use super::*; 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] #[test]
fn not() { fn not() {
let source = "!true"; let source = "!true";

View File

@ -42,7 +42,7 @@ impl Vm {
vm: &mut Vm, vm: &mut Vm,
instruction: Instruction, instruction: Instruction,
position: Span, position: Span,
) -> Result<(Value, Value), VmError> { ) -> Result<(Value, &Value), VmError> {
let left = if instruction.first_argument_is_constant() { let left = if instruction.first_argument_is_constant() {
vm.chunk vm.chunk
.take_constant(instruction.first_argument(), position)? .take_constant(instruction.first_argument(), position)?
@ -51,9 +51,9 @@ impl Vm {
}; };
let right = if instruction.second_argument_is_constant() { let right = if instruction.second_argument_is_constant() {
vm.chunk vm.chunk
.take_constant(instruction.second_argument(), position)? .get_constant(instruction.second_argument(), position)?
} else { } else {
vm.clone(instruction.second_argument(), position)? vm.get(instruction.second_argument(), position)?
}; };
Ok((left, right)) Ok((left, right))
@ -151,7 +151,7 @@ impl Vm {
Operation::Add => { Operation::Add => {
let (left, right) = take_constants_or_clone(self, instruction, position)?; let (left, right) = take_constants_or_clone(self, instruction, position)?;
let sum = left let sum = left
.add(&right) .add(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.insert(sum, instruction.destination(), position)?; self.insert(sum, instruction.destination(), position)?;
@ -159,7 +159,7 @@ impl Vm {
Operation::Subtract => { Operation::Subtract => {
let (left, right) = take_constants_or_clone(self, instruction, position)?; let (left, right) = take_constants_or_clone(self, instruction, position)?;
let difference = left let difference = left
.subtract(&right) .subtract(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.insert(difference, instruction.destination(), position)?; self.insert(difference, instruction.destination(), position)?;
@ -167,7 +167,7 @@ impl Vm {
Operation::Multiply => { Operation::Multiply => {
let (left, right) = take_constants_or_clone(self, instruction, position)?; let (left, right) = take_constants_or_clone(self, instruction, position)?;
let product = left let product = left
.multiply(&right) .multiply(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.insert(product, instruction.destination(), position)?; self.insert(product, instruction.destination(), position)?;
@ -175,7 +175,7 @@ impl Vm {
Operation::Divide => { Operation::Divide => {
let (left, right) = take_constants_or_clone(self, instruction, position)?; let (left, right) = take_constants_or_clone(self, instruction, position)?;
let quotient = left let quotient = left
.divide(&right) .divide(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.insert(quotient, instruction.destination(), position)?; self.insert(quotient, instruction.destination(), position)?;
@ -183,7 +183,7 @@ impl Vm {
Operation::Modulo => { Operation::Modulo => {
let (left, right) = take_constants_or_clone(self, instruction, position)?; let (left, right) = take_constants_or_clone(self, instruction, position)?;
let remainder = left let remainder = left
.modulo(&right) .modulo(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.insert(remainder, instruction.destination(), position)?; self.insert(remainder, instruction.destination(), position)?;
@ -220,7 +220,7 @@ impl Vm {
Operation::Equal => { Operation::Equal => {
let (left, right) = take_constants_or_clone(self, instruction, position)?; let (left, right) = take_constants_or_clone(self, instruction, position)?;
let equal = left let equal = left
.equal(&right) .equal(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
let compare_to = instruction.destination_as_boolean(); let compare_to = instruction.destination_as_boolean();
@ -233,7 +233,7 @@ impl Vm {
Operation::Less => { Operation::Less => {
let (left, right) = take_constants_or_clone(self, instruction, position)?; let (left, right) = take_constants_or_clone(self, instruction, position)?;
let less = left let less = left
.less_than(&right) .less_than(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
let compare_to = instruction.destination() != 0; let compare_to = instruction.destination() != 0;
@ -246,7 +246,7 @@ impl Vm {
Operation::LessEqual => { Operation::LessEqual => {
let (left, right) = take_constants_or_clone(self, instruction, position)?; let (left, right) = take_constants_or_clone(self, instruction, position)?;
let less_equal = left let less_equal = left
.less_than_or_equal(&right) .less_than_or_equal(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
let compare_to = instruction.destination() != 0; 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<Value, VmError> { fn clone(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
let index = index as usize; let index = index as usize;