Refine binary parsing
This commit is contained in:
parent
2c485cf046
commit
1d0bafa4a3
@ -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 {
|
||||||
|
@ -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!(
|
||||||
|
Loading…
Reference in New Issue
Block a user