1
0

Refine binary parsing

This commit is contained in:
Jeff 2024-09-23 06:42:41 -04:00
parent 2c485cf046
commit 1d0bafa4a3
2 changed files with 102 additions and 54 deletions

View File

@ -148,56 +148,63 @@ impl<'src> Parser<'src> {
_allow_assignment: bool,
_allow_return: bool,
) -> Result<(), ParseError> {
if let Token::Boolean(text) = self.current_token {
let position = self.current_position;
let boolean = text.parse::<bool>().unwrap();
let boolean_text = if let Token::Boolean(text) = self.current_token {
text
} else {
return Err(ParseError::ExpectedToken {
expected: TokenKind::Boolean,
found: self.current_token.to_owned(),
position: self.current_position,
});
};
self.advance()?;
let position = self.current_position;
let boolean = boolean_text.parse::<bool>().unwrap();
let previous_operations = self.chunk.get_last_n_operations::<2>();
self.advance()?;
if let [Some(Operation::LoadBoolean), Some(Operation::LoadBoolean)] =
previous_operations
{
let (second_boolean, second_position) =
self.chunk.pop_instruction(self.current_position)?;
let (first_boolean, first_position) =
self.chunk.pop_instruction(self.current_position)?;
let previous_operations = self.chunk.get_last_n_operations::<2>();
if first_boolean.first_argument_as_boolean() == boolean {
let skip = first_boolean.second_argument_as_boolean();
if let [Some(Operation::LoadBoolean), Some(Operation::LoadBoolean)] = previous_operations {
let (second_boolean, second_position) =
self.chunk.pop_instruction(self.current_position)?;
let (first_boolean, first_position) =
self.chunk.pop_instruction(self.current_position)?;
self.emit_instruction(
Instruction::load_boolean(self.current_register, boolean, skip),
position,
);
if first_boolean.first_argument_as_boolean() == boolean {
let skip = first_boolean.second_argument_as_boolean();
return Ok(());
}
self.emit_instruction(
Instruction::load_boolean(self.current_register, boolean, skip),
position,
);
if second_boolean.first_argument_as_boolean() == boolean {
let skip = second_boolean.second_argument_as_boolean();
self.emit_instruction(
Instruction::load_boolean(self.current_register, boolean, skip),
position,
);
return Ok(());
}
self.emit_instruction(first_boolean, first_position);
self.emit_instruction(second_boolean, second_position);
return Ok(());
}
let skip = previous_operations[0] == Some(Operation::Jump);
if second_boolean.first_argument_as_boolean() == boolean {
let skip = second_boolean.second_argument_as_boolean();
self.emit_instruction(
Instruction::load_boolean(self.current_register, boolean, skip),
position,
);
self.emit_instruction(
Instruction::load_boolean(self.current_register, boolean, skip),
position,
);
return Ok(());
}
self.emit_instruction(first_boolean, first_position);
self.emit_instruction(second_boolean, second_position);
}
let skip = previous_operations[0] == Some(Operation::Jump);
self.emit_instruction(
Instruction::load_boolean(self.current_register, boolean, skip),
position,
);
self.increment_register()?;
Ok(())
}
@ -330,11 +337,7 @@ impl<'src> Parser<'src> {
(false, true, previous_instruction.first_argument())
}
Operation::LoadBoolean => {
self.increment_register()?;
(true, false, previous_instruction.destination())
}
Operation::LoadBoolean => (true, false, previous_instruction.destination()),
Operation::Close => {
return Err(ParseError::ExpectedExpression {
found: self.previous_token.to_owned(),
@ -391,6 +394,7 @@ impl<'src> Parser<'src> {
is_constant = true;
push_back = true;
self.decrement_register()?;
instruction.destination()
}
Operation::Close => {
@ -552,13 +556,18 @@ impl<'src> Parser<'src> {
let (push_back_left, left_is_constant, _) =
self.handle_binary_argument(&left_instruction)?;
if let Operation::LoadBoolean = left_instruction.operation() {
self.increment_register()?;
}
let operator = self.current_token;
let operator_position = self.current_position;
let rule = ParseRule::from(&operator.kind());
let test_register = self.current_register.saturating_sub(1);
let mut instruction = match operator.kind() {
TokenKind::DoubleAmpersand => Instruction::test(self.current_register, true),
TokenKind::DoublePipe => Instruction::test(self.current_register, false),
TokenKind::DoubleAmpersand => Instruction::test(test_register, true),
TokenKind::DoublePipe => Instruction::test(test_register, false),
_ => {
return Err(ParseError::ExpectedTokenMultiple {
expected: &[TokenKind::DoubleAmpersand, TokenKind::DoublePipe],
@ -568,10 +577,6 @@ impl<'src> Parser<'src> {
}
};
if let Operation::LoadBoolean = left_instruction.operation() {
self.increment_register()?;
}
self.advance()?;
self.parse(rule.precedence.increment())?;
@ -580,6 +585,12 @@ impl<'src> Parser<'src> {
let (push_back_right, right_is_constant, _) =
self.handle_binary_argument(&right_instruction)?;
let emit_move_to = if self.current_register != test_register {
Some(self.current_register)
} else {
None
};
if left_is_constant {
instruction.set_first_argument_to_constant();
}
@ -603,10 +614,12 @@ impl<'src> Parser<'src> {
self.emit_instruction(right_instruction, right_position);
}
self.emit_instruction(
Instruction::r#move(self.current_register, self.current_register - 1),
operator_position,
);
if let Some(register) = emit_move_to {
self.emit_instruction(
Instruction::r#move(register, test_register),
operator_position,
);
}
Ok(())
}
@ -780,7 +793,19 @@ impl<'src> Parser<'src> {
self.emit_instruction(second_load_boolean, second_position);
}
if let Some(Operation::LoadBoolean) = self.chunk.get_last_operation() {
if let [Some(Operation::LoadBoolean), Some(Operation::LoadBoolean)] =
self.chunk.get_last_n_operations()
{
// Do not emit a jump if the last two instructions were LoadBoolean operations. However,
// we need to set them to the same destination register and decrement the register count.
let (mut second_load_boolean, second_position) =
self.chunk.pop_instruction(self.current_position)?;
let (first_load_boolean, _) = self.chunk.get_previous().unwrap();
second_load_boolean.set_destination(first_load_boolean.destination());
self.emit_instruction(second_load_boolean, second_position);
} else if let Some(Operation::LoadBoolean) = self.chunk.get_last_operation() {
// Skip the jump if the last instruction was a LoadBoolean operation. A LoadBoolean can
// skip the following instruction, so a jump is unnecessary.
} else {

View File

@ -497,6 +497,29 @@ fn and() {
);
}
#[test]
fn variable_and() {
assert_eq!(
parse("let a = true; let b = false; a && b"),
Ok(Chunk::with_data(
vec![
(Instruction::load_boolean(0, true, false), Span(8, 12)),
(Instruction::define_local(0, 0, false), Span(4, 5)),
(Instruction::load_boolean(1, false, false), Span(22, 27)),
(Instruction::define_local(1, 1, false), Span(18, 19)),
(Instruction::test(1, true), Span(31, 33)),
(Instruction::jump(1, true), Span(31, 33)),
(Instruction::r#move(2, 1), Span(31, 33)),
],
vec![],
vec![
Local::new(Identifier::new("a"), false, 0, Some(0)),
Local::new(Identifier::new("b"), false, 0, Some(1)),
]
))
);
}
#[test]
fn divide() {
assert_eq!(