From da7e99a4ff129ca3b0ca23ed008d8c5ea29a54dd Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 16 Jan 2021 16:35:47 +0300 Subject: feat(tazjin/rlox): Use variable depth for env lookup 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 Tested-by: BuildkiteCI --- users/tazjin/rlox/src/interpreter.rs | 38 +++++++++++++++++++++--------- users/tazjin/rlox/src/interpreter/tests.rs | 5 +++- 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 { - let ident = identifier_str(&name.name)?; + fn get(&self, ident: &str, line: usize, depth: usize) -> Result { + 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 { + 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 = 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") -- cgit 1.4.1