about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2020-12-30T21·59+0300
committertazjin <mail@tazj.in>2020-12-31T11·19+0000
commit78355d3c0b91e5b952be6244f23c14a2d596d05f (patch)
tree2969bc154f787c3fdd30cff4df1c483af720ca60
parent4a86a06466f88e2e4c7f4627809eb80d2acbc68c (diff)
feat(tazjin/rlox): Add global variable support in interpreter r/2031
Change-Id: I4134cf78dc3934a517ad0c848ae1d3729abaf882
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2297
Tested-by: BuildkiteCI
Reviewed-by: tazjin <mail@tazj.in>
-rw-r--r--users/tazjin/rlox/src/errors.rs2
-rw-r--r--users/tazjin/rlox/src/interpreter.rs211
2 files changed, 137 insertions, 76 deletions
diff --git a/users/tazjin/rlox/src/errors.rs b/users/tazjin/rlox/src/errors.rs
index 03a24312411c..2c29aa4c2f0a 100644
--- a/users/tazjin/rlox/src/errors.rs
+++ b/users/tazjin/rlox/src/errors.rs
@@ -7,6 +7,8 @@ pub enum ErrorKind {
     ExpectedSemicolon,
     ExpectedVariableName,
     TypeError(String),
+    UndefinedVariable(String),
+    InternalError(String),
 }
 
 #[derive(Debug)]
diff --git a/users/tazjin/rlox/src/interpreter.rs b/users/tazjin/rlox/src/interpreter.rs
index 99df23d725dd..5b03883b6c02 100644
--- a/users/tazjin/rlox/src/interpreter.rs
+++ b/users/tazjin/rlox/src/interpreter.rs
@@ -1,6 +1,7 @@
 use crate::errors::{report, Error, ErrorKind};
 use crate::parser::{self, Declaration, Expr, Literal, Program, Statement};
 use crate::scanner::{self, TokenKind};
+use std::collections::HashMap;
 
 // Run some Lox code and print it to stdout
 pub fn run(code: &str) {
@@ -9,8 +10,9 @@ pub fn run(code: &str) {
     match scanner::scan(&chars) {
         Ok(tokens) => match parser::parse(tokens) {
             Ok(program) => {
+                let mut interpreter = Interpreter::default();
                 println!("Program:\n{:?}", program);
-                if let Err(err) = run_program(&program) {
+                if let Err(err) = interpreter.interpret(&program) {
                     println!("Error in program: {:?}", err);
                 }
             }
@@ -28,104 +30,161 @@ fn report_errors(errors: Vec<Error>) {
 
 // Tree-walk interpreter
 
-fn eval_truthy(lit: &Literal) -> bool {
-    match lit {
-        Literal::Nil => false,
-        Literal::Boolean(b) => *b,
-        _ => true,
-    }
+#[derive(Debug, Default)]
+struct Environment {
+    values: HashMap<String, Literal>,
 }
 
-fn eval_unary<'a>(expr: &parser::Unary<'a>) -> Result<Literal, Error> {
-    let right = eval(&*expr.right)?;
+impl Environment {
+    fn define(&mut self, name: &str, value: Literal) {
+        self.values.insert(name.into(), value);
+    }
 
-    match (&expr.operator.kind, right) {
-        (TokenKind::Minus, Literal::Number(num)) => Ok(Literal::Number(-num)),
-        (TokenKind::Bang, right) => Ok(Literal::Boolean(!eval_truthy(&right))),
+    fn get(&self, name: &parser::Variable) -> Result<Literal, Error> {
+        if let TokenKind::Identifier(ident) = &name.0.kind {
+            return self
+                .values
+                .get(ident)
+                .map(Clone::clone)
+                .ok_or_else(|| Error {
+                    line: name.0.line,
+                    kind: ErrorKind::UndefinedVariable(ident.into()),
+                });
+        }
 
-        (op, right) => Err(Error {
-            line: expr.operator.line,
-            kind: ErrorKind::TypeError(format!(
-                "Operator '{:?}' can not be called with argument '{:?}'",
-                op, right
-            )),
-        }),
+        Err(Error {
+            line: name.0.line,
+            kind: ErrorKind::InternalError("unexpected identifier kind".into()),
+        })
     }
 }
 
-fn eval_binary<'a>(expr: &parser::Binary<'a>) -> Result<Literal, Error> {
-    let left = eval(&*expr.left)?;
-    let right = eval(&*expr.right)?;
+#[derive(Debug, Default)]
+struct Interpreter {
+    globals: Environment,
+}
 
-    let result = match (&expr.operator.kind, left, right) {
-        // Numeric
-        (TokenKind::Minus, Literal::Number(l), Literal::Number(r)) => Literal::Number(l - r),
-        (TokenKind::Slash, Literal::Number(l), Literal::Number(r)) => Literal::Number(l / r),
-        (TokenKind::Star, Literal::Number(l), Literal::Number(r)) => Literal::Number(l * r),
-        (TokenKind::Plus, Literal::Number(l), Literal::Number(r)) => Literal::Number(l + r),
+impl Interpreter {
+    fn interpret_stmt<'a>(&self, stmt: &Statement<'a>) -> Result<(), Error> {
+        match stmt {
+            Statement::Expr(expr) => {
+                self.eval(expr)?;
+            }
+            Statement::Print(expr) => {
+                let result = self.eval(expr)?;
+                println!("{:?}", result)
+            }
+        }
 
-        // Strings
-        (TokenKind::Plus, Literal::String(l), Literal::String(r)) => {
-            Literal::String(format!("{}{}", l, r))
+        Ok(())
+    }
+
+    fn interpret_var<'a>(&mut self, var: &parser::Var<'a>) -> Result<(), Error> {
+        if let TokenKind::Identifier(ident) = &var.name.kind {
+            let init = var.initialiser.as_ref().ok_or_else(|| Error {
+                line: var.name.line,
+                kind: ErrorKind::InternalError("missing variable initialiser".into()),
+            })?;
+
+            self.globals.define(ident, self.eval(init)?);
+            return Ok(());
         }
 
-        // Comparators (on numbers only?)
-        (TokenKind::Greater, Literal::Number(l), Literal::Number(r)) => Literal::Boolean(l > r),
-        (TokenKind::GreaterEqual, Literal::Number(l), Literal::Number(r)) => {
-            Literal::Boolean(l >= r)
+        Err(Error {
+            line: var.name.line,
+            kind: ErrorKind::InternalError("unexpected identifier kind".into()),
+        })
+    }
+
+    pub fn interpret<'a>(&mut self, program: &Program<'a>) -> Result<(), Error> {
+        for decl in program {
+            match decl {
+                Declaration::Stmt(stmt) => self.interpret_stmt(stmt)?,
+                Declaration::Var(var) => self.interpret_var(var)?,
+            }
         }
-        (TokenKind::Less, Literal::Number(l), Literal::Number(r)) => Literal::Boolean(l < r),
-        (TokenKind::LessEqual, Literal::Number(l), Literal::Number(r)) => Literal::Boolean(l <= r),
 
-        // Equality
-        (TokenKind::Equal, l, r) => Literal::Boolean(l == r),
-        (TokenKind::BangEqual, l, r) => Literal::Boolean(l != r),
+        Ok(())
+    }
+
+    fn eval<'a>(&self, expr: &Expr<'a>) -> Result<Literal, Error> {
+        match expr {
+            Expr::Literal(lit) => Ok(lit.clone()),
+            Expr::Grouping(grouping) => self.eval(&*grouping.0),
+            Expr::Unary(unary) => self.eval_unary(unary),
+            Expr::Binary(binary) => self.eval_binary(binary),
+            Expr::Variable(var) => self.globals.get(var),
+        }
+    }
+
+    fn eval_unary<'a>(&self, expr: &parser::Unary<'a>) -> Result<Literal, Error> {
+        let right = self.eval(&*expr.right)?;
 
-        (op, left, right) => {
-            return Err(Error {
+        match (&expr.operator.kind, right) {
+            (TokenKind::Minus, Literal::Number(num)) => Ok(Literal::Number(-num)),
+            (TokenKind::Bang, right) => Ok(Literal::Boolean(!eval_truthy(&right))),
+
+            (op, right) => Err(Error {
                 line: expr.operator.line,
                 kind: ErrorKind::TypeError(format!(
-                    "Operator '{:?}' can not be called with arguments '({:?}, {:?})'",
-                    op, left, right
+                    "Operator '{:?}' can not be called with argument '{:?}'",
+                    op, right
                 )),
-            })
+            }),
         }
-    };
+    }
 
-    Ok(result)
-}
+    fn eval_binary<'a>(&self, expr: &parser::Binary<'a>) -> Result<Literal, Error> {
+        let left = self.eval(&*expr.left)?;
+        let right = self.eval(&*expr.right)?;
 
-fn eval<'a>(expr: &Expr<'a>) -> Result<Literal, Error> {
-    match expr {
-        Expr::Literal(lit) => Ok(lit.clone()),
-        Expr::Grouping(grouping) => eval(&*grouping.0),
-        Expr::Unary(unary) => eval_unary(unary),
-        Expr::Binary(binary) => eval_binary(binary),
-        Expr::Variable(_) => unimplemented!(),
-    }
-}
+        let result = match (&expr.operator.kind, left, right) {
+            // Numeric
+            (TokenKind::Minus, Literal::Number(l), Literal::Number(r)) => Literal::Number(l - r),
+            (TokenKind::Slash, Literal::Number(l), Literal::Number(r)) => Literal::Number(l / r),
+            (TokenKind::Star, Literal::Number(l), Literal::Number(r)) => Literal::Number(l * r),
+            (TokenKind::Plus, Literal::Number(l), Literal::Number(r)) => Literal::Number(l + r),
 
-fn run_stmt<'a>(stmt: &Statement<'a>) -> Result<(), Error> {
-    match stmt {
-        Statement::Expr(expr) => {
-            eval(expr)?;
-        }
-        Statement::Print(expr) => {
-            let result = eval(expr)?;
-            println!("{:?}", result)
-        }
-    }
+            // Strings
+            (TokenKind::Plus, Literal::String(l), Literal::String(r)) => {
+                Literal::String(format!("{}{}", l, r))
+            }
 
-    Ok(())
-}
+            // Comparators (on numbers only?)
+            (TokenKind::Greater, Literal::Number(l), Literal::Number(r)) => Literal::Boolean(l > r),
+            (TokenKind::GreaterEqual, Literal::Number(l), Literal::Number(r)) => {
+                Literal::Boolean(l >= r)
+            }
+            (TokenKind::Less, Literal::Number(l), Literal::Number(r)) => Literal::Boolean(l < r),
+            (TokenKind::LessEqual, Literal::Number(l), Literal::Number(r)) => {
+                Literal::Boolean(l <= r)
+            }
 
-fn run_program<'a>(program: &Program<'a>) -> Result<(), Error> {
-    for decl in program {
-        match decl {
-            Declaration::Stmt(stmt) => run_stmt(stmt)?,
-            Declaration::Var(_var) => unimplemented!(),
-        }
+            // Equality
+            (TokenKind::Equal, l, r) => Literal::Boolean(l == r),
+            (TokenKind::BangEqual, l, r) => Literal::Boolean(l != r),
+
+            (op, left, right) => {
+                return Err(Error {
+                    line: expr.operator.line,
+                    kind: ErrorKind::TypeError(format!(
+                        "Operator '{:?}' can not be called with arguments '({:?}, {:?})'",
+                        op, left, right
+                    )),
+                })
+            }
+        };
+
+        Ok(result)
     }
+}
+
+// Interpreter functions not dependent on interpreter-state.
 
-    Ok(())
+fn eval_truthy(lit: &Literal) -> bool {
+    match lit {
+        Literal::Nil => false,
+        Literal::Boolean(b) => *b,
+        _ => true,
+    }
 }