about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2021-01-16T13·35+0300
committertazjin <mail@tazj.in>2021-01-16T13·46+0000
commitda7e99a4ff129ca3b0ca23ed008d8c5ea29a54dd (patch)
tree3fa2683eb03683796a587cf7433ac4af07cc4cd9
parent29335a8b63ba62bd83019eb193baba7c5b656da3 (diff)
feat(tazjin/rlox): Use variable depth for env lookup r/2114
Finishes the binding implementation from https://craftinginterpreters.com/resolving-and-binding.html

Change-Id: I1e2c1f4139d9e77ce0b99e38db26edd4cdb56ad2
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2404
Reviewed-by: tazjin <mail@tazj.in>
Tested-by: BuildkiteCI
-rw-r--r--users/tazjin/rlox/src/interpreter.rs38
-rw-r--r--users/tazjin/rlox/src/interpreter/tests.rs5
2 files changed, 31 insertions, 12 deletions
diff --git a/users/tazjin/rlox/src/interpreter.rs b/users/tazjin/rlox/src/interpreter.rs
index 6c61288d5a..01ea217f82 100644
--- a/users/tazjin/rlox/src/interpreter.rs
+++ b/users/tazjin/rlox/src/interpreter.rs
@@ -106,23 +106,33 @@ impl Environment {
         Ok(())
     }
 
-    fn get(&self, name: &parser::Variable) -> Result<Value, Error> {
-        let ident = identifier_str(&name.name)?;
+    fn get(&self, ident: &str, line: usize, depth: usize) -> Result<Value, Error> {
+        println!("looking up {} at depth {}", ident, depth);
+        if depth > 0 {
+            match &self.enclosing {
+                None => {
+                    return Err(Error {
+                        line,
+                        kind: ErrorKind::InternalError(format!(
+                            "invalid depth {} for {}",
+                            depth, ident
+                        )),
+                    })
+                }
+                Some(parent) => {
+                    let env = parent.read().expect("fatal: environment lock poisoned");
+                    return env.get(ident, line, depth - 1);
+                }
+            }
+        }
 
         self.values
             .get(ident)
             .map(Clone::clone)
             .ok_or_else(|| Error {
-                line: name.name.line,
+                line,
                 kind: ErrorKind::UndefinedVariable(ident.into()),
             })
-            .or_else(|err| {
-                if let Some(parent) = &self.enclosing {
-                    parent.read().unwrap().get(name)
-                } else {
-                    Err(err)
-                }
-            })
     }
 
     fn assign(&mut self, name: &scanner::Token, value: Value) -> Result<(), Error> {
@@ -198,10 +208,16 @@ impl Interpreter {
     }
 
     fn get_var(&mut self, var: &parser::Variable) -> Result<Value, Error> {
+        let ident = identifier_str(&var.name)?;
+        let depth = var.depth.ok_or_else(|| Error {
+            line: var.name.line,
+            kind: ErrorKind::UndefinedVariable(ident.into()),
+        })?;
+
         self.env
             .read()
             .expect("environment lock is poisoned")
-            .get(var)
+            .get(ident, var.name.line, depth)
     }
 
     // Interpreter itself
diff --git a/users/tazjin/rlox/src/interpreter/tests.rs b/users/tazjin/rlox/src/interpreter/tests.rs
index 875116593e..4698583dfc 100644
--- a/users/tazjin/rlox/src/interpreter/tests.rs
+++ b/users/tazjin/rlox/src/interpreter/tests.rs
@@ -1,10 +1,13 @@
 use super::*;
+use crate::resolver;
 
 /// Evaluate a code snippet, returning a value.
 fn parse_eval(code: &str) -> Value {
     let chars: Vec<char> = code.chars().collect();
     let tokens = scanner::scan(&chars).expect("could not scan code");
-    let program = parser::parse(tokens).expect("could not parse code");
+    let mut program = parser::parse(tokens).expect("could not parse code");
+    program = resolver::resolve(program).expect("could not resolve code");
+
     Interpreter::create()
         .interpret(&program)
         .expect("could not eval code")