about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2021-01-14T15·03+0300
committertazjin <mail@tazj.in>2021-01-14T15·19+0000
commit56d8fa97ed936aacb66b13bb4a8c849a9797db93 (patch)
tree550c7fcd0598d33b9a68eb2ea1d21173b546326e
parentfe97398fd9d1e20ad4a953e27d080721b949865a (diff)
feat(tazjin/rlox): Implement calling user-defined functions r/2105
This slightly jiggles around interpret_block to let callers pass in an
environment.

Change-Id: I03112a38be0e8696242d8eae8d41da8c2cc66b48
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2392
Reviewed-by: tazjin <mail@tazj.in>
Tested-by: BuildkiteCI
-rw-r--r--users/tazjin/rlox/src/interpreter.rs27
1 files changed, 20 insertions, 7 deletions
diff --git a/users/tazjin/rlox/src/interpreter.rs b/users/tazjin/rlox/src/interpreter.rs
index bd907542fb5e..ef188b797fa8 100644
--- a/users/tazjin/rlox/src/interpreter.rs
+++ b/users/tazjin/rlox/src/interpreter.rs
@@ -25,14 +25,23 @@ impl<'a> Callable<'a> {
     fn arity(&self) -> usize {
         match self {
             Callable::Builtin(builtin) => builtin.arity(),
-            _ => unimplemented!(),
+            Callable::Function(func) => func.params.len(),
         }
     }
 
-    fn call(&self, args: Vec<Value>) -> Result<Value<'a>, Error> {
+    fn call(&self, lox: &mut Interpreter<'a>, args: Vec<Value<'a>>) -> Result<Value<'a>, Error> {
         match self {
             Callable::Builtin(builtin) => builtin.call(args),
-            _ => unimplemented!(),
+
+            Callable::Function(func) => {
+                let mut fn_env: Environment<'a> = Default::default();
+
+                for (param, value) in func.params.iter().zip(args.into_iter()) {
+                    fn_env.define(param, value)?;
+                }
+
+                lox.interpret_block(Rc::new(RwLock::new(fn_env)), &func.body)
+            },
         }
     }
 }
@@ -208,7 +217,7 @@ impl<'a> Interpreter<'a> {
                 Value::Literal(Literal::String(output))
             }
             Statement::Var(var) => return self.interpret_var(var),
-            Statement::Block(block) => return self.interpret_block(block),
+            Statement::Block(block) => return self.interpret_block(Default::default(), block),
             Statement::If(if_stmt) => return self.interpret_if(if_stmt),
             Statement::While(while_stmt) => return self.interpret_while(while_stmt),
             Statement::Function(_) => unimplemented!(),
@@ -227,13 +236,17 @@ impl<'a> Interpreter<'a> {
         Ok(value)
     }
 
-    fn interpret_block(&mut self, block: &parser::Block<'a>) -> Result<Value<'a>, Error> {
+    fn interpret_block(
+        &mut self,
+        env: Rc<RwLock<Environment<'a>>>,
+        block: &parser::Block<'a>,
+    ) -> Result<Value<'a>, Error> {
         // Initialise a new environment and point it at the parent
         // (this is a bit tedious because we need to wrap it in and
         // out of the Rc).
         //
         // TODO(tazjin): Refactor this to use Rc on the interpreter itself.
-        let previous = std::mem::replace(&mut self.env, Default::default());
+        let previous = std::mem::replace(&mut self.env, env);
         self.set_enclosing(previous.clone());
 
         let result = self.interpret(block);
@@ -388,7 +401,7 @@ impl<'a> Interpreter<'a> {
             });
         }
 
-        callable.call(args)
+        callable.call(self, args)
     }
 }