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,17 +148,24 @@ impl<'src> Parser<'src> {
_allow_assignment: bool, _allow_assignment: bool,
_allow_return: bool, _allow_return: bool,
) -> Result<(), ParseError> { ) -> Result<(), ParseError> {
if let Token::Boolean(text) = self.current_token { 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,
});
};
let position = self.current_position; let position = self.current_position;
let boolean = text.parse::<bool>().unwrap(); let boolean = boolean_text.parse::<bool>().unwrap();
self.advance()?; self.advance()?;
let previous_operations = self.chunk.get_last_n_operations::<2>(); let previous_operations = self.chunk.get_last_n_operations::<2>();
if let [Some(Operation::LoadBoolean), Some(Operation::LoadBoolean)] = if let [Some(Operation::LoadBoolean), Some(Operation::LoadBoolean)] = previous_operations {
previous_operations
{
let (second_boolean, second_position) = let (second_boolean, second_position) =
self.chunk.pop_instruction(self.current_position)?; self.chunk.pop_instruction(self.current_position)?;
let (first_boolean, first_position) = let (first_boolean, first_position) =
@ -196,7 +203,7 @@ impl<'src> Parser<'src> {
Instruction::load_boolean(self.current_register, boolean, skip), Instruction::load_boolean(self.current_register, boolean, skip),
position, position,
); );
} self.increment_register()?;
Ok(()) Ok(())
} }
@ -330,11 +337,7 @@ impl<'src> Parser<'src> {
(false, true, previous_instruction.first_argument()) (false, true, previous_instruction.first_argument())
} }
Operation::LoadBoolean => { Operation::LoadBoolean => (true, false, previous_instruction.destination()),
self.increment_register()?;
(true, false, previous_instruction.destination())
}
Operation::Close => { Operation::Close => {
return Err(ParseError::ExpectedExpression { return Err(ParseError::ExpectedExpression {
found: self.previous_token.to_owned(), found: self.previous_token.to_owned(),
@ -391,6 +394,7 @@ impl<'src> Parser<'src> {
is_constant = true; is_constant = true;
push_back = true; push_back = true;
self.decrement_register()?;
instruction.destination() instruction.destination()
} }
Operation::Close => { Operation::Close => {
@ -552,13 +556,18 @@ impl<'src> Parser<'src> {
let (push_back_left, left_is_constant, _) = let (push_back_left, left_is_constant, _) =
self.handle_binary_argument(&left_instruction)?; self.handle_binary_argument(&left_instruction)?;
if let Operation::LoadBoolean = left_instruction.operation() {
self.increment_register()?;
}
let operator = self.current_token; let operator = self.current_token;
let operator_position = self.current_position; let operator_position = self.current_position;
let rule = ParseRule::from(&operator.kind()); let rule = ParseRule::from(&operator.kind());
let test_register = self.current_register.saturating_sub(1);
let mut instruction = match operator.kind() { let mut instruction = match operator.kind() {
TokenKind::DoubleAmpersand => Instruction::test(self.current_register, true), TokenKind::DoubleAmpersand => Instruction::test(test_register, true),
TokenKind::DoublePipe => Instruction::test(self.current_register, false), TokenKind::DoublePipe => Instruction::test(test_register, false),
_ => { _ => {
return Err(ParseError::ExpectedTokenMultiple { return Err(ParseError::ExpectedTokenMultiple {
expected: &[TokenKind::DoubleAmpersand, TokenKind::DoublePipe], 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.advance()?;
self.parse(rule.precedence.increment())?; self.parse(rule.precedence.increment())?;
@ -580,6 +585,12 @@ impl<'src> Parser<'src> {
let (push_back_right, right_is_constant, _) = let (push_back_right, right_is_constant, _) =
self.handle_binary_argument(&right_instruction)?; 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 { if left_is_constant {
instruction.set_first_argument_to_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(right_instruction, right_position);
} }
if let Some(register) = emit_move_to {
self.emit_instruction( self.emit_instruction(
Instruction::r#move(self.current_register, self.current_register - 1), Instruction::r#move(register, test_register),
operator_position, operator_position,
); );
}
Ok(()) Ok(())
} }
@ -780,7 +793,19 @@ impl<'src> Parser<'src> {
self.emit_instruction(second_load_boolean, second_position); 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 jump if the last instruction was a LoadBoolean operation. A LoadBoolean can
// skip the following instruction, so a jump is unnecessary. // skip the following instruction, so a jump is unnecessary.
} else { } 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] #[test]
fn divide() { fn divide() {
assert_eq!( assert_eq!(