diff options
author | Vincent Ambo <mail@tazj.in> | 2021-01-06T22·57+0300 |
---|---|---|
committer | tazjin <mail@tazj.in> | 2021-01-06T23·02+0000 |
commit | 0b4e280a0791cea82f3d076c5e6ddebfaed9d704 (patch) | |
tree | 2345b7aef19c750c2bb9123721a47c4763bc6aae /users/tazjin/rlox/src/parser.rs | |
parent | d47e55ba4db53445a48cdf634e10e823b1c115da (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
Diffstat (limited to 'users/tazjin/rlox/src/parser.rs')
-rw-r--r-- | users/tazjin/rlox/src/parser.rs | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/users/tazjin/rlox/src/parser.rs b/users/tazjin/rlox/src/parser.rs index 62296ca02c3b..fd1f7c9ea825 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)?; |