about summary refs log tree commit diff
path: root/users
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2021-02-27T14·38+0200
committertazjin <mail@tazj.in>2021-02-27T19·22+0000
commit1d3d9d32e34c72c84ff72af4583f83d7105bdb98 (patch)
tree98a133b47c9cf06d7fb1b50197c6b370efd5cd9d /users
parentb13a6736ddf87e9689a207d5980dd28b9aa83dd4 (diff)
feat(tazjin/rlox): Set up precedence parsing scaffolding r/2239
Defines a new precedence levels enum which can be used to restrict the
parser precedence in any given location. As an example, unary
expressions and grouping are implemented, as these have a different
precedence from e.g. expression()

Change-Id: I91f299fc77530f76c3aba717f638985428104ee5
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2558
Reviewed-by: tazjin <mail@tazj.in>
Tested-by: BuildkiteCI
Diffstat (limited to 'users')
-rw-r--r--users/tazjin/rlox/src/bytecode/compiler/mod.rs (renamed from users/tazjin/rlox/src/bytecode/compiler.rs)79
1 files changed, 67 insertions, 12 deletions
diff --git a/users/tazjin/rlox/src/bytecode/compiler.rs b/users/tazjin/rlox/src/bytecode/compiler/mod.rs
index ab5a970ffd12..813cf2e26df0 100644
--- a/users/tazjin/rlox/src/bytecode/compiler.rs
+++ b/users/tazjin/rlox/src/bytecode/compiler/mod.rs
@@ -1,25 +1,44 @@
 use super::chunk::Chunk;
 use super::errors::{Error, ErrorKind, LoxResult};
 use super::opcode::OpCode;
-use crate::scanner;
+use super::value::Value;
+use crate::scanner::{self, Token, TokenKind};
 
-struct Compiler<T: Iterator<Item = scanner::Token>> {
+#[cfg(test)]
+mod tests;
+
+struct Compiler<T: Iterator<Item = Token>> {
     tokens: T,
     chunk: Chunk,
     panic: bool,
     errors: Vec<Error>,
 
     // TODO(tazjin): Restructure so that these don't need to be Option?
-    current: Option<scanner::Token>,
-    previous: Option<scanner::Token>,
+    current: Option<Token>,
+    previous: Option<Token>,
+}
+
+#[derive(Debug, PartialEq, PartialOrd)]
+enum Precedence {
+    None,
+    Assignment, // =
+    Or,         // or
+    And,        // and
+    Equality,   // == !=
+    Comparison, // < > <= >=
+    Term,       // + -
+    Factor,     // * /
+    Unary,      // ! -
+    Call,       // . ()
+    Primary,
 }
 
-impl<T: Iterator<Item = scanner::Token>> Compiler<T> {
+impl<T: Iterator<Item = Token>> Compiler<T> {
     fn compile(&mut self) -> LoxResult<()> {
         self.advance();
-        self.expression();
+        self.expression()?;
         self.consume(
-            &scanner::TokenKind::Eof,
+            &TokenKind::Eof,
             ErrorKind::ExpectedToken("Expected end of expression"),
         )?;
 
@@ -31,13 +50,44 @@ impl<T: Iterator<Item = scanner::Token>> Compiler<T> {
         self.current = self.tokens.next();
     }
 
-    fn expression(&mut self) {
-        unimplemented!()
+    fn expression(&mut self) -> LoxResult<()> {
+        self.parse_precedence(Precedence::Assignment)
+    }
+
+    // TODO(tazjin): Assumption is that we have access to the previous
+    // token wherever this ends up invoked. True?
+    fn number(&mut self, num: f64) {
+        self.emit_constant(num);
+    }
+
+    fn grouping(&mut self, num: f64) -> LoxResult<()> {
+        self.expression()?;
+        self.consume(
+            &TokenKind::RightParen,
+            ErrorKind::ExpectedToken("Expected ')' after expression"),
+        )
+    }
+
+    fn unary(&mut self, kind: &TokenKind) -> LoxResult<()> {
+        // Compile the operand
+        self.parse_precedence(Precedence::Unary)?;
+
+        // Emit operator instruction
+        match kind {
+            TokenKind::Minus => self.emit_op(OpCode::OpNegate),
+            _ => unreachable!("only called for unary operator tokens"),
+        }
+
+        Ok(())
+    }
+
+    fn parse_precedence(&mut self, precedence: Precedence) -> LoxResult<()> {
+        unimplemented!("what goes here?")
     }
 
     fn consume(
         &mut self,
-        expected: &scanner::TokenKind,
+        expected: &TokenKind,
         err: ErrorKind,
     ) -> LoxResult<()> {
         unimplemented!()
@@ -57,13 +107,18 @@ impl<T: Iterator<Item = scanner::Token>> Compiler<T> {
         self.current_chunk().add_op(op, line);
     }
 
-    fn previous(&self) -> &scanner::Token {
+    fn emit_constant(&mut self, val: Value) {
+        let idx = self.chunk.add_constant(val);
+        self.emit_op(OpCode::OpConstant(idx));
+    }
+
+    fn previous(&self) -> &Token {
         self.previous
             .as_ref()
             .expect("invalid internal compiler state: missing previous token")
     }
 
-    fn error_at(&mut self, token: &scanner::Token, kind: ErrorKind) {
+    fn error_at(&mut self, token: &Token, kind: ErrorKind) {
         if self.panic {
             return;
         }