about summary refs log tree commit diff
path: root/users/tazjin
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
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')
-rw-r--r--users/tazjin/rlox/src/errors.rs1
-rw-r--r--users/tazjin/rlox/src/interpreter.rs42
-rw-r--r--users/tazjin/rlox/src/parser.rs65
3 files changed, 73 insertions, 35 deletions
diff --git a/users/tazjin/rlox/src/errors.rs b/users/tazjin/rlox/src/errors.rs
index ae7e0515a025..9598893aa352 100644
--- a/users/tazjin/rlox/src/errors.rs
+++ b/users/tazjin/rlox/src/errors.rs
@@ -4,6 +4,7 @@ pub enum ErrorKind {
     UnterminatedString,
     UnmatchedParens,
     ExpectedExpression(String),
+    ExpectedSemicolon,
     TypeError(String),
 }
 
diff --git a/users/tazjin/rlox/src/interpreter.rs b/users/tazjin/rlox/src/interpreter.rs
index 6c411ded5e11..c68101cf68a6 100644
--- a/users/tazjin/rlox/src/interpreter.rs
+++ b/users/tazjin/rlox/src/interpreter.rs
@@ -1,33 +1,25 @@
 use crate::errors::{report, Error, ErrorKind};
-use crate::parser::{self, Expr, Literal};
-use crate::scanner::{self, Token, TokenKind};
+use crate::parser::{self, Expr, Literal, Program, Statement};
+use crate::scanner::{self, TokenKind};
 
 // Run some Lox code and print it to stdout
 pub fn run(code: &str) {
     let chars: Vec<char> = code.chars().collect();
 
     match scanner::scan(&chars) {
-        Ok(tokens) => {
-            print_tokens(&tokens);
-            match parser::parse(tokens) {
-                Ok(expr) => {
-                    println!("Expression:\n{:?}", expr);
-                    println!("Result: {:?}", eval(&expr));
+        Ok(tokens) => match parser::parse(tokens) {
+            Ok(program) => {
+                println!("Program:\n{:?}", program);
+                if let Err(err) = run_program(&program) {
+                    println!("Error in program: {:?}", err);
                 }
-                Err(errors) => report_errors(errors),
             }
-        }
+            Err(errors) => report_errors(errors),
+        },
         Err(errors) => report_errors(errors),
     }
 }
 
-fn print_tokens<'a>(tokens: &Vec<Token<'a>>) {
-    println!("Tokens:");
-    for token in tokens {
-        println!("{:?}", token);
-    }
-}
-
 fn report_errors(errors: Vec<Error>) {
     for error in errors {
         report(&error);
@@ -111,3 +103,19 @@ fn eval<'a>(expr: &Expr<'a>) -> Result<Literal, Error> {
         Expr::Binary(binary) => eval_binary(binary),
     }
 }
+
+fn run_program<'a>(program: &Program<'a>) -> Result<(), Error> {
+    for stmt in program {
+        match stmt {
+            Statement::Expr(expr) => {
+                eval(expr)?;
+            }
+            Statement::Print(expr) => {
+                let result = eval(expr)?;
+                println!("{:?}", result)
+            }
+        }
+    }
+
+    Ok(())
+}
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)
+    }
 }