about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2021-01-06T17·35+0300
committertazjin <mail@tazj.in>2021-01-06T23·02+0000
commit0a0335ae6cd957f48086a41832cef794998f7271 (patch)
tree54e59e6c3b88989f7d913aa7aaf8e4cb3a750a69
parent93122a212e1f27cb0a941b11e342458fef97ed25 (diff)
feat(tazjin/rlox): Parse & interpret logical operators r/2061
Change-Id: I1a7d0eda61f7f077b820dc0d2c2516e204966962
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2324
Reviewed-by: tazjin <mail@tazj.in>
Tested-by: BuildkiteCI
-rw-r--r--users/tazjin/rlox/src/interpreter.rs15
-rw-r--r--users/tazjin/rlox/src/parser.rs42
2 files changed, 55 insertions, 2 deletions
diff --git a/users/tazjin/rlox/src/interpreter.rs b/users/tazjin/rlox/src/interpreter.rs
index cfef5548ec..738542b6b8 100644
--- a/users/tazjin/rlox/src/interpreter.rs
+++ b/users/tazjin/rlox/src/interpreter.rs
@@ -180,6 +180,7 @@ impl Interpreter {
             Expr::Unary(unary) => self.eval_unary(unary),
             Expr::Binary(binary) => self.eval_binary(binary),
             Expr::Variable(var) => self.get_var(var),
+            Expr::Logical(log) => self.eval_logical(log),
         }
     }
 
@@ -249,6 +250,20 @@ impl Interpreter {
         self.assign_var(&assign.name, value.clone())?;
         Ok(value)
     }
+
+    fn eval_logical<'a>(&mut self, logical: &parser::Logical<'a>) -> Result<Literal, Error> {
+        let left = eval_truthy(&self.eval(&logical.left)?);
+        let right = eval_truthy(&self.eval(&logical.right)?);
+
+        match &logical.operator.kind {
+            TokenKind::And => Ok(Literal::Boolean(left && right)),
+            TokenKind::Or => Ok(Literal::Boolean(left || right)),
+            kind => Err(Error {
+                line: logical.operator.line,
+                kind: ErrorKind::InternalError(format!("Invalid logical operator: {:?}", kind)),
+            }),
+        }
+    }
 }
 
 // Interpreter functions not dependent on interpreter-state.
diff --git a/users/tazjin/rlox/src/parser.rs b/users/tazjin/rlox/src/parser.rs
index 4be6a6afcc..ac99f19cb2 100644
--- a/users/tazjin/rlox/src/parser.rs
+++ b/users/tazjin/rlox/src/parser.rs
@@ -24,6 +24,13 @@ pub struct Binary<'a> {
 }
 
 #[derive(Debug)]
+pub struct Logical<'a> {
+    pub left: Box<Expr<'a>>,
+    pub operator: Token<'a>,
+    pub right: Box<Expr<'a>>,
+}
+
+#[derive(Debug)]
 pub struct Grouping<'a>(pub Box<Expr<'a>>);
 
 #[derive(Debug, Clone, PartialEq)]
@@ -52,6 +59,7 @@ pub enum Expr<'a> {
     Literal(Literal),
     Unary(Unary<'a>),
     Variable(Variable<'a>),
+    Logical(Logical<'a>),
 }
 
 // Variable assignment. Not to be confused with `Variable`, which is
@@ -102,7 +110,9 @@ printStmt      → "print" expression ";" ;
 
 expression     → assignment ;
 assignment     → IDENTIFIER "=" assignment
-               | equality ;
+               | logic_or ;
+logic_or       → logic_and ( "or" logic_and )* ;
+logic_and      → equality ( "and" equality )* ;
 equality       → comparison ( ( "!=" | "==" ) comparison )* ;
 comparison     → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
 term           → factor ( ( "-" | "+" ) factor )* ;
@@ -222,7 +232,7 @@ impl<'a> Parser<'a> {
     }
 
     fn assignment(&mut self) -> ExprResult<'a> {
-        let expr = self.equality()?;
+        let expr = self.logic_or()?;
 
         if self.match_token(&[TokenKind::Equal]) {
             let equals = self.previous().clone();
@@ -244,6 +254,34 @@ impl<'a> Parser<'a> {
         Ok(expr)
     }
 
+    fn logic_or(&mut self) -> ExprResult<'a> {
+        let mut expr = self.logic_and()?;
+
+        while self.match_token(&[TokenKind::Or]) {
+            expr = Expr::Logical(Logical {
+                left: Box::new(expr),
+                operator: self.previous().clone(),
+                right: Box::new(self.logic_and()?),
+            })
+        }
+
+        Ok(expr)
+    }
+
+    fn logic_and(&mut self) -> ExprResult<'a> {
+        let mut expr = self.equality()?;
+
+        while self.match_token(&[TokenKind::And]) {
+            expr = Expr::Logical(Logical {
+                left: Box::new(expr),
+                operator: self.previous().clone(),
+                right: Box::new(self.equality()?),
+            })
+        }
+
+        Ok(expr)
+    }
+
     fn equality(&mut self) -> ExprResult<'a> {
         self.binary_operator(
             &[TokenKind::BangEqual, TokenKind::EqualEqual],