1
0

Begin fixing control flow

This commit is contained in:
Jeff 2024-11-06 14:46:23 -05:00
parent 78840cf3e7
commit 87f597624a
4 changed files with 149 additions and 119 deletions

View File

@ -39,7 +39,7 @@ impl<'chunk> Optimizer<'chunk> {
} }
fn optimize_comparison(&mut self) { fn optimize_comparison(&mut self) {
log::trace!("Optimizing comparison"); log::debug!("Optimizing comparison");
let first_loader_register = { let first_loader_register = {
let first_loader = &mut self.instructions[2].0; let first_loader = &mut self.instructions[2].0;

View File

@ -252,19 +252,6 @@ impl<'src> Parser<'src> {
Some(operations) Some(operations)
} }
fn get_last_jump_mut(&mut self) -> Option<&mut Instruction> {
self.chunk
.instructions_mut()
.iter_mut()
.find_map(|(instruction, _)| {
if let Operation::Jump = instruction.operation() {
Some(instruction)
} else {
None
}
})
}
fn get_last_jumpable_mut(&mut self) -> Option<&mut Instruction> { fn get_last_jumpable_mut(&mut self) -> Option<&mut Instruction> {
self.chunk self.chunk
.instructions_mut() .instructions_mut()
@ -901,12 +888,18 @@ impl<'src> Parser<'src> {
} }
} }
fn parse_block(&mut self, allowed: Allowed) -> Result<(), ParseError> { fn parse_block(&mut self, _: Allowed) -> Result<(), ParseError> {
self.advance()?; self.advance()?;
self.chunk.begin_scope(); self.chunk.begin_scope();
while !self.allow(Token::RightCurlyBrace)? && !self.is_eof() { while !self.allow(Token::RightCurlyBrace)? && !self.is_eof() {
self.parse(Precedence::None, allowed)?; self.parse(
Precedence::None,
Allowed {
assignment: true,
explicit_return: true,
},
)?;
} }
self.chunk.end_scope(); self.chunk.end_scope();
@ -962,28 +955,18 @@ impl<'src> Parser<'src> {
Operation::LoadBoolean, Operation::LoadBoolean,
Operation::Jump, Operation::Jump,
Operation::Equal | Operation::Less | Operation::LessEqual Operation::Equal | Operation::Less | Operation::LessEqual
],) ])
) { ) {
self.chunk.instructions_mut().pop(); self.chunk.instructions_mut().pop();
self.chunk.instructions_mut().pop(); self.chunk.instructions_mut().pop();
self.chunk.instructions_mut().pop();
} }
let block_allowed = Allowed { let if_block_start = self.chunk.len();
assignment: allowed.assignment, let if_block_start_position = self.current_position;
explicit_return: allowed.explicit_return,
};
if let Token::LeftCurlyBrace = self.current_token { if let Token::LeftCurlyBrace = self.current_token {
let block_start = self.chunk.len(); self.parse_block(allowed)?;
self.parse_block(block_allowed)?;
let block_end = self.chunk.len();
let jump_distance = (block_end - block_start) as u8;
if let Some(jump) = self.get_last_jump_mut() {
jump.set_b(jump_distance);
}
} else { } else {
return Err(ParseError::ExpectedToken { return Err(ParseError::ExpectedToken {
expected: TokenKind::LeftCurlyBrace, expected: TokenKind::LeftCurlyBrace,
@ -992,6 +975,9 @@ impl<'src> Parser<'src> {
}); });
} }
let if_block_end = self.chunk.len();
let mut if_block_distance = (if_block_end - if_block_start) as u8;
let if_block_is_expression = self let if_block_is_expression = self
.chunk .chunk
.instructions() .instructions()
@ -1006,92 +992,84 @@ impl<'src> Parser<'src> {
}) })
.unwrap_or(false); .unwrap_or(false);
let if_last_register = self.next_register().saturating_sub(1);
if let Token::Else = self.current_token { if let Token::Else = self.current_token {
let else_start = self.chunk.len(); self.advance()?;
let if_last_register = self.next_register().saturating_sub(1);
self.parse_else(allowed, block_allowed)?; if let Token::LeftCurlyBrace = self.current_token {
self.parse_block(allowed)?;
if self.chunk.len() >= 4 {
let possible_comparison_statement = {
let start = self.chunk.len() - 4;
&mut self.chunk.instructions_mut()[start..]
};
optimize(possible_comparison_statement);
}
let else_last_register = self.next_register().saturating_sub(1);
let else_end = self.chunk.len();
let jump_distance = (else_end - else_start) as u8;
if if_last_register < else_last_register {
self.emit_instruction(
Instruction::r#move(else_last_register, if_last_register),
self.current_position,
);
}
self.previous_is_expression = if_block_is_expression && self.previous_is_expression;
if jump_distance == 1 {
if let Some(skippable) = self.get_last_jumpable_mut() {
skippable.set_c_to_boolean(true);
} else {
self.chunk.instructions_mut().insert(
else_start,
(
Instruction::jump(jump_distance, true),
self.current_position,
),
);
}
} else { } else {
self.chunk.instructions_mut().insert( return Err(ParseError::ExpectedTokenMultiple {
else_start, expected: &[TokenKind::If, TokenKind::LeftCurlyBrace],
( found: self.current_token.to_owned(),
Instruction::jump(jump_distance, true), position: self.current_position,
self.current_position, });
),
);
} }
} else { } else {
self.previous_is_expression = false; self.previous_is_expression = false;
} }
Ok(()) self.previous_is_expression = if_block_is_expression && self.previous_is_expression;
}
fn parse_else(&mut self, allowed: Allowed, block_allowed: Allowed) -> Result<(), ParseError> { let else_block_end = self.chunk.len();
self.advance()?; let else_block_distance = (else_block_end - if_block_end) as u8;
let if_block_end = self.chunk.len(); match else_block_distance {
0 => {}
1 => {
if let Some(skippable) = self.get_last_jumpable_mut() {
skippable.set_c_to_boolean(true);
} else {
if_block_distance += 1;
if let Token::If = self.current_token { self.chunk.instructions_mut().insert(
self.parse_if(allowed)?; if_block_end,
} else if let Token::LeftCurlyBrace = self.current_token { (
self.parse_block(block_allowed)?; Instruction::jump(else_block_distance, true),
self.current_position,
let else_end = self.chunk.len(); ),
);
if else_end - if_block_end > 1 { }
let jump_distance = (else_end - if_block_end) as u8; }
2.. => {
if_block_distance += 1;
self.chunk.instructions_mut().insert( self.chunk.instructions_mut().insert(
if_block_end, if_block_end,
( (
Instruction::jump(jump_distance, true), Instruction::jump(else_block_distance, true),
self.current_position, self.current_position,
), ),
); );
} }
} else { }
return Err(ParseError::ExpectedTokenMultiple {
expected: &[TokenKind::If, TokenKind::LeftCurlyBrace], self.chunk.instructions_mut().insert(
found: self.current_token.to_owned(), if_block_start,
position: self.current_position, (
}); Instruction::jump(if_block_distance, true),
if_block_start_position,
),
);
if self.chunk.len() >= 4 {
let possible_comparison_statement = {
let start = self.chunk.len() - 4;
&mut self.chunk.instructions_mut()[start..]
};
optimize(possible_comparison_statement);
}
let else_last_register = self.next_register().saturating_sub(1);
if if_last_register < else_last_register {
self.emit_instruction(
Instruction::r#move(else_last_register, if_last_register),
self.current_position,
);
} }
Ok(()) Ok(())
@ -1229,7 +1207,7 @@ impl<'src> Parser<'src> {
) )
} }
fn parse_return(&mut self, allowed: Allowed) -> Result<(), ParseError> { fn parse_return_statement(&mut self, allowed: Allowed) -> Result<(), ParseError> {
if !allowed.explicit_return { if !allowed.explicit_return {
return Err(ParseError::UnexpectedReturn { return Err(ParseError::UnexpectedReturn {
position: self.current_position, position: self.current_position,
@ -1260,9 +1238,7 @@ impl<'src> Parser<'src> {
} }
fn parse_implicit_return(&mut self) -> Result<(), ParseError> { fn parse_implicit_return(&mut self) -> Result<(), ParseError> {
let has_semicolon = self.allow(Token::Semicolon)?; if self.allow(Token::Semicolon)? {
if has_semicolon {
self.emit_instruction(Instruction::r#return(false), self.current_position); self.emit_instruction(Instruction::r#return(false), self.current_position);
} else { } else {
self.emit_instruction( self.emit_instruction(
@ -1816,7 +1792,7 @@ impl From<&Token<'_>> for ParseRule<'_> {
precedence: Precedence::Assignment, precedence: Precedence::Assignment,
}, },
Token::Return => ParseRule { Token::Return => ParseRule {
prefix: Some(Parser::parse_return), prefix: Some(Parser::parse_return_statement),
infix: None, infix: None,
precedence: Precedence::None, precedence: Precedence::None,
}, },

View File

@ -15,7 +15,7 @@ fn equality_assignment_long() {
.set_c_is_constant(), .set_c_is_constant(),
Span(13, 15) Span(13, 15)
), ),
(Instruction::jump(1, true), Span(13, 15)), (Instruction::jump(1, true), Span(18, 19)),
(Instruction::load_boolean(0, true, true), Span(20, 24)), (Instruction::load_boolean(0, true, true), Span(20, 24)),
(Instruction::load_boolean(0, false, false), Span(34, 39)), (Instruction::load_boolean(0, false, false), Span(34, 39)),
(Instruction::define_local(0, 0, false), Span(4, 5)), (Instruction::define_local(0, 0, false), Span(4, 5)),
@ -61,7 +61,65 @@ fn equality_assignment_short() {
} }
#[test] #[test]
fn if_else_assigment() { fn if_else_assigment_false() {
let source = r#"
let a = if 4 == 3 {
1; 2; 3; 4;
panic()
} else {
1; 2; 3; 4;
42
};
a"#;
assert_eq!(
parse(source),
Ok(Chunk::with_data(
None,
vec![
(
*Instruction::equal(true, 0, 1)
.set_b_is_constant()
.set_c_is_constant(),
Span(22, 24)
),
(Instruction::jump(6, true), Span(27, 28)),
(Instruction::load_constant(0, 2, false), Span(41, 42)),
(Instruction::load_constant(1, 3, false), Span(44, 45)),
(Instruction::load_constant(2, 1, false), Span(47, 48)),
(Instruction::load_constant(3, 0, false), Span(50, 51)),
(
Instruction::call_native(4, NativeFunction::Panic, 0),
Span(65, 72)
),
(Instruction::jump(5, true), Span(138, 139)),
(Instruction::load_constant(5, 2, false), Span(102, 103)),
(Instruction::load_constant(6, 3, false), Span(105, 106)),
(Instruction::load_constant(7, 1, false), Span(108, 109)),
(Instruction::load_constant(8, 0, false), Span(111, 112)),
(Instruction::load_constant(9, 4, false), Span(126, 128)),
(Instruction::r#move(9, 4), Span(138, 139)),
(Instruction::define_local(9, 0, false), Span(13, 14)),
(Instruction::get_local(10, 0), Span(148, 149)),
(Instruction::r#return(true), Span(149, 149)),
],
vec![
Value::integer(4),
Value::integer(3),
Value::integer(1),
Value::integer(2),
Value::integer(42),
Value::string("a")
],
vec![Local::new(5, None, false, Scope::default(), 0)]
)),
);
assert_eq!(run(source), Ok(Some(Value::integer(42))));
}
#[test]
fn if_else_assigment_true() {
let source = r#" let source = r#"
let a = if 4 == 4 { let a = if 4 == 4 {
1; 2; 3; 4; 1; 2; 3; 4;
@ -83,13 +141,12 @@ fn if_else_assigment() {
.set_c_is_constant(), .set_c_is_constant(),
Span(22, 24) Span(22, 24)
), ),
(Instruction::jump(5, true), Span(22, 24)), (Instruction::jump(6, true), Span(27, 28)),
(Instruction::load_constant(0, 1, false), Span(41, 42)), (Instruction::load_constant(0, 1, false), Span(41, 42)),
(Instruction::load_constant(1, 2, false), Span(44, 45)), (Instruction::load_constant(1, 2, false), Span(44, 45)),
(Instruction::load_constant(2, 3, false), Span(47, 48)), (Instruction::load_constant(2, 3, false), Span(47, 48)),
(Instruction::load_constant(3, 0, false), Span(50, 51)), (Instruction::load_constant(3, 0, false), Span(50, 51)),
(Instruction::load_constant(4, 4, false), Span(65, 67)), (Instruction::load_constant(4, 4, false), Span(65, 67)),
(Instruction::jump(6, true), Span(138, 139)),
(Instruction::jump(5, true), Span(138, 139)), (Instruction::jump(5, true), Span(138, 139)),
(Instruction::load_constant(5, 1, false), Span(97, 98)), (Instruction::load_constant(5, 1, false), Span(97, 98)),
(Instruction::load_constant(6, 2, false), Span(100, 101)), (Instruction::load_constant(6, 2, false), Span(100, 101)),
@ -139,12 +196,11 @@ fn if_else_complex() {
.set_c_is_constant(), .set_c_is_constant(),
Span(14, 16) Span(14, 16)
), ),
(Instruction::jump(5, true), Span(14, 16)), (Instruction::jump(5, true), Span(19, 20)),
(Instruction::load_constant(0, 0, false), Span(33, 34)), (Instruction::load_constant(0, 0, false), Span(33, 34)),
(Instruction::load_constant(1, 1, false), Span(36, 37)), (Instruction::load_constant(1, 1, false), Span(36, 37)),
(Instruction::load_constant(2, 2, false), Span(39, 40)), (Instruction::load_constant(2, 2, false), Span(39, 40)),
(Instruction::load_constant(3, 3, false), Span(42, 43)), (Instruction::load_constant(3, 3, false), Span(42, 43)),
(Instruction::jump(5, true), Span(95, 95)),
(Instruction::jump(4, true), Span(95, 95)), (Instruction::jump(4, true), Span(95, 95)),
(Instruction::load_constant(4, 0, false), Span(74, 75)), (Instruction::load_constant(4, 0, false), Span(74, 75)),
(Instruction::load_constant(5, 1, false), Span(77, 78)), (Instruction::load_constant(5, 1, false), Span(77, 78)),
@ -251,7 +307,7 @@ fn if_else_false() {
.set_c_is_constant(), .set_c_is_constant(),
Span(5, 7) Span(5, 7)
), ),
(Instruction::jump(1, true), Span(5, 7)), (Instruction::jump(1, true), Span(10, 11)),
( (
Instruction::call_native(0, NativeFunction::Panic, 0), Instruction::call_native(0, NativeFunction::Panic, 0),
Span(12, 19) Span(12, 19)
@ -283,7 +339,7 @@ fn if_else_true() {
.set_c_is_constant(), .set_c_is_constant(),
Span(5, 7) Span(5, 7)
), ),
(Instruction::jump(1, true), Span(5, 7)), (Instruction::jump(1, true), Span(10, 11)),
(Instruction::load_constant(0, 1, true), Span(12, 14)), (Instruction::load_constant(0, 1, true), Span(12, 14)),
( (
Instruction::call_native(1, NativeFunction::Panic, 0), Instruction::call_native(1, NativeFunction::Panic, 0),
@ -315,7 +371,7 @@ fn if_false() {
.set_c_is_constant(), .set_c_is_constant(),
Span(5, 7) Span(5, 7)
), ),
(Instruction::jump(1, true), Span(5, 7)), (Instruction::jump(1, true), Span(10, 11)),
(Instruction::load_constant(0, 1, false), Span(12, 13)), (Instruction::load_constant(0, 1, false), Span(12, 13)),
(Instruction::r#return(false), Span(15, 15)) (Instruction::r#return(false), Span(15, 15))
], ],
@ -342,7 +398,7 @@ fn if_true() {
.set_c_is_constant(), .set_c_is_constant(),
Span(5, 7) Span(5, 7)
), ),
(Instruction::jump(1, true), Span(5, 7)), (Instruction::jump(1, true), Span(10, 11)),
(Instruction::load_constant(0, 1, false), Span(12, 13)), (Instruction::load_constant(0, 1, false), Span(12, 13)),
(Instruction::r#return(false), Span(15, 15)) (Instruction::r#return(false), Span(15, 15))
], ],

View File

@ -1,10 +1,8 @@
fn fib (n: int) -> int { fn fib (n: int) -> int {
if n <= 0 { 0 } if n <= 0 { return 0 }
if n == 1 { if n == 1 { return 1 }
1
} else { fib(n - 1) + fib(n - 2)
fib(n - 1) + fib(n - 2)
}
} }
fib(10) fib(10)