about summary refs log tree commit diff
path: root/users/tazjin/rlox/src/parser.rs
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2020-12-20T22·55+0100
committertazjin <mail@tazj.in>2020-12-22T10·13+0000
commit75ae25daa9e7686bca60b518d9cf442bcfba3bf7 (patch)
treead9fa978d10be144010fd41b26b272f3e0ad0fd5 /users/tazjin/rlox/src/parser.rs
parentc3bbb861b8a97f7ea0df51f56bacf14145350a42 (diff)
feat(tazjin/rlox): Add support for statements r/2024
First part of
https://craftinginterpreters.com/statements-and-state.html

Supports print statements, as well as evaluation for the sake of
it (i.e. future side-effects).

Change-Id: Ic6653b568f98d6cfe3f297615b7113c0ba1d9a70
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2287
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.rs65
1 files changed, 47 insertions, 18 deletions
diff --git a/users/tazjin/rlox/src/parser.rs b/users/tazjin/rlox/src/parser.rs
index 611a99f01c22..d7155fff10a7 100644
--- a/users/tazjin/rlox/src/parser.rs
+++ b/users/tazjin/rlox/src/parser.rs
@@ -42,9 +42,25 @@ pub enum Expr<'a> {
     Unary(Unary<'a>),
 }
 
+#[derive(Debug)]
+pub enum Statement<'a> {
+    Expr(Expr<'a>),
+    Print(Expr<'a>),
+}
+
+pub type Program<'a> = Vec<Statement<'a>>;
+
 // Parser
 
 /*
+program        → statement* EOF ;
+
+statement      → exprStmt
+               | printStmt ;
+
+exprStmt       → expression ";" ;
+printStmt      → "print" expression ";" ;
+
 expression     → equality ;
 equality       → comparison ( ( "!=" | "==" ) comparison )* ;
 comparison     → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
@@ -62,10 +78,31 @@ struct Parser<'a> {
 }
 
 type ExprResult<'a> = Result<Expr<'a>, Error>;
+type StmtResult<'a> = Result<Statement<'a>, Error>;
 
 impl<'a> Parser<'a> {
     // recursive-descent parser functions
 
+    fn statement(&mut self) -> StmtResult<'a> {
+        if self.match_token(&[TokenKind::Print]) {
+            self.print_statement()
+        } else {
+            self.expr_statement()
+        }
+    }
+
+    fn print_statement(&mut self) -> StmtResult<'a> {
+        let expr = self.expression()?;
+        self.consume(&TokenKind::Semicolon, ErrorKind::ExpectedSemicolon)?;
+        Ok(Statement::Print(expr))
+    }
+
+    fn expr_statement(&mut self) -> StmtResult<'a> {
+        let expr = self.expression()?;
+        self.consume(&TokenKind::Semicolon, ErrorKind::ExpectedSemicolon)?;
+        Ok(Statement::Expr(expr))
+    }
+
     fn expression(&mut self) -> ExprResult<'a> {
         self.equality()
     }
@@ -231,34 +268,26 @@ impl<'a> Parser<'a> {
     }
 }
 
-pub fn parse<'a>(tokens: Vec<Token<'a>>) -> Result<Expr<'a>, Vec<Error>> {
+pub fn parse<'a>(tokens: Vec<Token<'a>>) -> Result<Program<'a>, Vec<Error>> {
     let mut parser = Parser { tokens, current: 0 };
+    let mut program: Program<'a> = vec![];
     let mut errors: Vec<Error> = vec![];
 
     while !parser.is_at_end() {
-        match parser.expression() {
+        match parser.statement() {
             Err(err) => {
                 errors.push(err);
                 parser.synchronise();
             }
-            Ok(expr) => {
-                if !parser.is_at_end() {
-                    // TODO(tazjin): This isn't a functional language
-                    // - multiple statements should be allowed, at
-                    // some point.
-                    let current = &parser.tokens[parser.current];
-                    errors.push(Error {
-                        line: current.line,
-                        kind: ErrorKind::UnexpectedChar(current.lexeme[0]),
-                    });
-                }
-
-                if errors.is_empty() {
-                    return Ok(expr);
-                }
+            Ok(stmt) => {
+                program.push(stmt);
             }
         }
     }
 
-    return Err(errors);
+    if errors.is_empty() {
+        Ok(program)
+    } else {
+        Err(errors)
+    }
 }