Add test for equality chain
This commit is contained in:
parent
1d0bafa4a3
commit
d68c789ea8
@ -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}",
|
||||||
" "
|
" "
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -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,17 +555,6 @@ impl<'src> Parser<'src> {
|
|||||||
instruction.set_second_argument_to_constant();
|
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 {
|
if push_back_left {
|
||||||
self.emit_instruction(left_instruction, left_position);
|
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(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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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";
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user