From 2880ba81ab76c5df0618c197058771d69dcbdb92 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Thu, 10 Feb 2022 13:13:15 +0100 Subject: [PATCH] Implement break & continue - Fix return propagation inside loops --- src/ast.rs | 2 ++ src/astoptimizer.rs | 1 + src/interpreter.rs | 29 +++++++++++++++++++++++------ src/lexer.rs | 2 ++ src/parser.rs | 16 ++++++++++++++++ src/token.rs | 12 ++++++++++++ 6 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 2443161..042e80e 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -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), diff --git a/src/astoptimizer.rs b/src/astoptimizer.rs index 5017083..71fe509 100644 --- a/src/astoptimizer.rs +++ b/src/astoptimizer.rs @@ -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 => (), } } } diff --git a/src/interpreter.rs b/src/interpreter.rs index c088370..0726ab2 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -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, } } diff --git a/src/lexer.rs b/src/lexer.rs index 9c592cc..2dd491d 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -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)], diff --git a/src/parser.rs b/src/parser.rs index 906ad6b..2bbc6ef 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -107,6 +107,22 @@ impl> Parser { /// Parse a single statement from the tokens. fn parse_stmt(&mut self) -> ResPE { 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] => { diff --git a/src/token.rs b/src/token.rs index add510c..0fadc77 100644 --- a/src/token.rs +++ b/src/token.rs @@ -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)*))