From ca3ec46bdc6de6dd137644282d0069e02046f954 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= <tseeker@nocternity.net>
Date: Mon, 2 Jan 2023 19:50:55 +0100
Subject: [PATCH] Interpreter - Data structure for functions

---
 src/interpreter/functions.rs | 65 ++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/src/interpreter/functions.rs b/src/interpreter/functions.rs
index e69de29..682eaa9 100644
--- a/src/interpreter/functions.rs
+++ b/src/interpreter/functions.rs
@@ -0,0 +1,65 @@
+use itertools::izip;
+
+use crate::{
+    ast,
+    errors::InterpreterError,
+    interpreter::{Environment, Interpretable},
+    tokens::Token,
+};
+
+use super::{Callable, EnvironmentRef, Value};
+
+/// A function implemented in the Lox-ish language.
+#[derive(Debug)]
+pub(crate) struct Function {
+    name: Token,
+    params: Vec<Token>,
+    body: Vec<ast::StmtNode>,
+}
+
+impl From<&ast::StmtNode> for Function {
+    fn from(node: &ast::StmtNode) -> Self {
+        if let ast::StmtNode::FunDecl { name, params, body } = node {
+            Self {
+                name: name.clone(),
+                params: params.clone(),
+                body: body.clone(),
+            }
+        } else {
+            panic!("initializing Function from non-function statement");
+        }
+    }
+}
+
+impl Callable for Function {
+    fn arity(&self) -> usize {
+        self.params.len()
+    }
+
+    fn call(
+        &self,
+        environment: &EnvironmentRef,
+        arguments: Vec<Value>,
+    ) -> Result<Value, InterpreterError> {
+        assert_eq!(arguments.len(), self.arity());
+        let param_env = Environment::create_child(environment);
+        for (arg, value) in izip!(self.params.iter(), arguments.into_iter()) {
+            param_env.borrow_mut().define(&arg, Some(value));
+        }
+
+        let child = Environment::create_child(&param_env);
+        for stmt in self.body.iter() {
+            let result = stmt.interpret(&child)?;
+            if result.is_flow_control() {
+                panic!("unexpected flow control");
+            }
+        }
+        Ok(Value::Nil)
+    }
+}
+
+impl ToString for Function {
+    fn to_string(&self) -> String {
+        format!("<fun {}>", self.name.lexeme)
+    }
+}