about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2021-01-03T15·26+0300
committertazjin <mail@tazj.in>2021-01-06T17·14+0000
commita89777b34f52ee5af51d93c66e1d9f92e830ca6a (patch)
tree819c7adf43acd684dc1f0fb87fddc653115beb1b
parent27a73171cf97c24c7785787eb7661405334329de (diff)
feat(tazjin/rlox): Parse if statements r/2059
Change-Id: I2352d75a3f02d65a5a2d04fb2cc4daa50f11ca1e
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2321
Tested-by: BuildkiteCI
Reviewed-by: tazjin <mail@tazj.in>
-rw-r--r--users/tazjin/rlox/src/errors.rs1
-rw-r--r--users/tazjin/rlox/src/interpreter.rs1
-rw-r--r--users/tazjin/rlox/src/parser.rs44
3 files changed, 45 insertions, 1 deletions
diff --git a/users/tazjin/rlox/src/errors.rs b/users/tazjin/rlox/src/errors.rs
index 10fc0a352990..64dc7a92072f 100644
--- a/users/tazjin/rlox/src/errors.rs
+++ b/users/tazjin/rlox/src/errors.rs
@@ -7,6 +7,7 @@ pub enum ErrorKind {
     ExpectedSemicolon,
     ExpectedClosingBrace,
     ExpectedVariableName,
+    ExpectedToken(&'static str),
     TypeError(String),
     UndefinedVariable(String),
     InternalError(String),
diff --git a/users/tazjin/rlox/src/interpreter.rs b/users/tazjin/rlox/src/interpreter.rs
index b650a4ee6826..3645e884ddf9 100644
--- a/users/tazjin/rlox/src/interpreter.rs
+++ b/users/tazjin/rlox/src/interpreter.rs
@@ -127,6 +127,7 @@ impl Interpreter {
             }
             Statement::Var(var) => return self.interpret_var(var),
             Statement::Block(block) => return self.interpret_block(block),
+            Statement::If(_) => unimplemented!(),
         }
 
         Ok(())
diff --git a/users/tazjin/rlox/src/parser.rs b/users/tazjin/rlox/src/parser.rs
index 94ad2f4c474b..4be6a6afcc54 100644
--- a/users/tazjin/rlox/src/parser.rs
+++ b/users/tazjin/rlox/src/parser.rs
@@ -62,6 +62,13 @@ pub struct Var<'a> {
     pub initialiser: Option<Expr<'a>>,
 }
 
+#[derive(Debug)]
+pub struct If<'a> {
+    pub condition: Expr<'a>,
+    pub then_branch: Box<Statement<'a>>,
+    pub else_branch: Option<Box<Statement<'a>>>,
+}
+
 pub type Block<'a> = Vec<Statement<'a>>;
 
 #[derive(Debug)]
@@ -70,6 +77,7 @@ pub enum Statement<'a> {
     Print(Expr<'a>),
     Var(Var<'a>),
     Block(Block<'a>),
+    If(If<'a>),
 }
 
 // Parser
@@ -81,9 +89,15 @@ declaration    → varDecl
                | statement ;
 
 statement      → exprStmt
-               | printStmt ;
+               | ifStmt
+               | printStmt
+               | block ;
 
 exprStmt       → expression ";" ;
+
+ifStmt         → "if" "(" expression ")" statement
+               ( "else" statement )? ;
+
 printStmt      → "print" expression ";" ;
 
 expression     → assignment ;
@@ -146,6 +160,8 @@ impl<'a> Parser<'a> {
             self.print_statement()
         } else if self.match_token(&[TokenKind::LeftBrace]) {
             self.block_statement()
+        } else if self.match_token(&[TokenKind::If]) {
+            self.if_statement()
         } else {
             self.expr_statement()
         }
@@ -169,6 +185,32 @@ impl<'a> Parser<'a> {
         Ok(Statement::Block(block))
     }
 
+    fn if_statement(&mut self) -> StmtResult<'a> {
+        self.consume(
+            &TokenKind::LeftParen,
+            ErrorKind::ExpectedToken("Expected '(' after 'if'"),
+        )?;
+        let condition = self.expression()?;
+        self.consume(
+            &TokenKind::RightParen,
+            ErrorKind::ExpectedToken("Expected ')' after condition"),
+        )?;
+
+        let then_branch = Box::new(self.statement()?);
+
+        let mut stmt = If {
+            condition,
+            then_branch,
+            else_branch: Option::None,
+        };
+
+        if self.match_token(&[TokenKind::Else]) {
+            stmt.else_branch = Some(Box::new(self.statement()?));
+        }
+
+        Ok(Statement::If(stmt))
+    }
+
     fn expr_statement(&mut self) -> StmtResult<'a> {
         let expr = self.expression()?;
         self.consume(&TokenKind::Semicolon, ErrorKind::ExpectedSemicolon)?;