Implement break & continue

- Fix return propagation inside loops
This commit is contained in:
Daniel M 2022-02-10 13:13:15 +01:00
parent 4e92a416ed
commit 2880ba81ab
6 changed files with 56 additions and 6 deletions

View File

@ -129,6 +129,8 @@ pub struct FunDecl {
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Statement {
Return(Expression),
Break,
Continue,
Declaration(Sid, usize, Expression),
FunDeclare(FunDecl),
Expr(Expression),

View File

@ -43,6 +43,7 @@ impl SimpleAstOptimizer {
Statement::Declaration(_, _, expr) => Self::optimize_expr(expr),
Statement::FunDeclare(_) => (),
Statement::Return(expr) => Self::optimize_expr(expr),
Statement::Break | Statement::Continue => (),
}
}
}

View File

@ -48,6 +48,8 @@ pub enum Value {
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum BlockExit {
Normal,
Break,
Continue,
Return(Value),
}
@ -139,6 +141,9 @@ impl Interpreter {
for stmt in prog {
match stmt {
Statement::Break => return Ok(BlockExit::Break),
Statement::Continue => return Ok(BlockExit::Continue),
Statement::Return(expr) => {
let val = self.resolve_expr(expr)?;
@ -156,9 +161,10 @@ impl Interpreter {
}
Statement::Block(block) => match self.run_block(block)? {
BlockExit::Return(val) => {
// Propagate return, continue and break
be @ (BlockExit::Return(_) | BlockExit::Continue | BlockExit::Break) => {
self.vartable.truncate(framepointer);
return Ok(BlockExit::Return(val));
return Ok(be);
}
_ => (),
},
@ -170,7 +176,16 @@ impl Interpreter {
break;
}
self.run_block(&looop.body)?;
let be = self.run_block(&looop.body)?;
match be {
// Propagate return
be @ BlockExit::Return(_) => {
self.vartable.truncate(framepointer);
return Ok(be);
}
BlockExit::Break => break,
BlockExit::Continue | BlockExit::Normal => (),
}
if let Some(adv) = &looop.advancement {
self.resolve_expr(&adv)?;
@ -198,10 +213,12 @@ impl Interpreter {
} else {
self.run_block(body_true)?
};
match exit {
BlockExit::Return(val) => {
// Propagate return, continue and break
be @ (BlockExit::Return(_) | BlockExit::Continue | BlockExit::Break) => {
self.vartable.truncate(framepointer);
return Ok(BlockExit::Return(val));
return Ok(be);
}
_ => (),
}
@ -269,7 +286,7 @@ impl Interpreter {
&Rc::clone(&self.funtable.get(*fun_stackpos).unwrap().body),
expected_num_args,
)? {
BlockExit::Normal => Value::Void,
BlockExit::Normal | BlockExit::Continue | BlockExit::Break => Value::Void,
BlockExit::Return(val) => val,
}
}

View File

@ -202,6 +202,8 @@ impl<'a> Lexer<'a> {
"else" => T![else],
"fun" => T![fun],
"return" => T![return],
"break" => T![break],
"continue" => T![continue],
// If it doesn't match a keyword, it is a normal identifier
_ => T![ident(ident)],

View File

@ -107,6 +107,22 @@ impl<T: Iterator<Item = Token>> Parser<T> {
/// Parse a single statement from the tokens.
fn parse_stmt(&mut self) -> ResPE<Statement> {
let stmt = match self.peek() {
T![break] => {
self.next();
validate_next!(self, T![;], ";");
Statement::Break
}
T![continue] => {
self.next();
validate_next!(self, T![;], ";");
Statement::Continue
}
T![loop] => Statement::Loop(self.parse_loop()?),
T![print] => {

View File

@ -18,6 +18,10 @@ pub enum Keyword {
Fun,
/// Return keyword ("return")
Return,
/// Break keyword ("break")
Break,
/// Continue keyword ("continue")
Continue,
}
/// Literal values
@ -217,6 +221,14 @@ macro_rules! T {
crate::token::Token::Keyword(crate::token::Keyword::Return)
};
[break] => {
crate::token::Token::Keyword(crate::token::Keyword::Break)
};
[continue] => {
crate::token::Token::Keyword(crate::token::Keyword::Continue)
};
// Literals
[i64($($val:tt)*)] => {
crate::token::Token::Literal(crate::token::Literal::I64($($val)*))