about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2021-01-06T22·57+0300
committertazjin <mail@tazj.in>2021-01-06T23·02+0000
commit0b4e280a0791cea82f3d076c5e6ddebfaed9d704 (patch)
tree2345b7aef19c750c2bb9123721a47c4763bc6aae
parentd47e55ba4db53445a48cdf634e10e823b1c115da (diff)
feat(tazjin/rlox): Implement for loops via desugaring to while r/2063
Change-Id: I31a93efcc8e0c2bcb8549e2a2c05bb58d2dc74ca
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2326
Reviewed-by: tazjin <mail@tazj.in>
Tested-by: BuildkiteCI
-rw-r--r--users/tazjin/rlox/src/parser.rs62
1 files changed, 62 insertions, 0 deletions
diff --git a/users/tazjin/rlox/src/parser.rs b/users/tazjin/rlox/src/parser.rs
index 62296ca02c..fd1f7c9ea8 100644
--- a/users/tazjin/rlox/src/parser.rs
+++ b/users/tazjin/rlox/src/parser.rs
@@ -104,11 +104,16 @@ declaration    → varDecl
                | statement ;
 
 statement      → exprStmt
+               | forStmt
                | ifStmt
                | printStmt
                | whileStmt
                | block ;
 
+forStmt        → "for" "(" ( varDecl | exprStmt | ";" )
+                 expression? ";"
+                 expression? ")" statement ;
+
 whileStmt      → "while" "(" expression ")" statement ;
 
 exprStmt       → expression ";" ;
@@ -184,6 +189,8 @@ impl<'a> Parser<'a> {
             self.if_statement()
         } else if self.match_token(&[TokenKind::While]) {
             self.while_statement()
+        } else if self.match_token(&[TokenKind::For]) {
+            self.for_statement()
         } else {
             self.expr_statement()
         }
@@ -252,6 +259,61 @@ impl<'a> Parser<'a> {
         }))
     }
 
+    fn for_statement(&mut self) -> StmtResult<'a> {
+        // Parsing of clauses ...
+        self.consume(
+            &TokenKind::LeftParen,
+            ErrorKind::ExpectedToken("Expected '(' after 'for'"),
+        )?;
+
+        let initialiser = if self.match_token(&[TokenKind::Semicolon]) {
+            None
+        } else if self.match_token(&[TokenKind::Var]) {
+            Some(self.var_declaration()?)
+        } else {
+            Some(self.expr_statement()?)
+        };
+
+        let condition = if self.check_token(&TokenKind::Semicolon) {
+            // unspecified condition => infinite loop
+            Expr::Literal(Literal::Boolean(true))
+        } else {
+            self.expression()?
+        };
+
+        self.consume(&TokenKind::Semicolon, ErrorKind::ExpectedSemicolon)?;
+
+        let increment = if self.check_token(&TokenKind::RightParen) {
+            None
+        } else {
+            Some(self.expression()?)
+        };
+
+        self.consume(
+            &TokenKind::RightParen,
+            ErrorKind::ExpectedToken("Expected ')' after for clauses"),
+        )?;
+
+        let mut body = self.statement()?;
+
+        // ... desugaring to while
+
+        if let Some(inc) = increment {
+            body = Statement::Block(vec![body, Statement::Expr(inc)]);
+        }
+
+        body = Statement::While(While {
+            condition,
+            body: Box::new(body),
+        });
+
+        if let Some(init) = initialiser {
+            body = Statement::Block(vec![init, body]);
+        }
+
+        Ok(body)
+    }
+
     fn expr_statement(&mut self) -> StmtResult<'a> {
         let expr = self.expression()?;
         self.consume(&TokenKind::Semicolon, ErrorKind::ExpectedSemicolon)?;