about summary refs log tree commit diff
path: root/src/interpret
diff options
context:
space:
mode:
Diffstat (limited to 'src/interpret')
-rw-r--r--src/interpret/scope.rs44
-rw-r--r--src/interpret/value.rs66
-rw-r--r--src/interpret/walker.rs49
3 files changed, 134 insertions, 25 deletions
diff --git a/src/interpret/scope.rs b/src/interpret/scope.rs
index 478b24d..0db34c2 100644
--- a/src/interpret/scope.rs
+++ b/src/interpret/scope.rs
@@ -3,17 +3,37 @@ use crate::parse::ast::nodes::Identifier;
 use std::{cell::RefCell, collections::HashMap, rc::Rc};
 use thiserror::Error;
 
-pub struct Scope {
-    scopes: Vec<HashMap<Identifier, AssignedValue>>,
-}
+type InnerScope = HashMap<Identifier, AssignedValue>;
+
+#[derive(Debug, Clone)]
+pub struct Scope(Rc<RefCell<InnerScope>>);
 
 impl Scope {
+    fn new() -> Self {
+        Scope(Rc::new(RefCell::new(HashMap::new())))
+    }
+
+    fn get(&self, ident: &str) -> Option<AssignedValue> {
+        self.0.borrow().get(ident).cloned()
+    }
+
+    fn insert(&self, ident: Identifier, value: AssignedValue) {
+        self.0.borrow_mut().insert(ident, value);
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct ScopeChain {
+    scopes: Vec<Scope>,
+}
+
+impl ScopeChain {
     pub fn new() -> Self {
-        Scope { scopes: Vec::new() }
+        ScopeChain { scopes: Vec::new() }
     }
 
     pub fn nest(&mut self) {
-        self.scopes.push(HashMap::new());
+        self.scopes.push(Scope::new());
     }
 
     pub fn unnest(&mut self) {
@@ -70,9 +90,21 @@ impl Scope {
         }
         Err(ScopeError::UnknownIdentifier(ident.to_string()))
     }
+
+    // In contrast to set_var sets the var in the most inner scope so that it's always found first.
+    // This is used when setting parameters.
+    pub fn set_var_shadowed(&mut self, ident: &str, value: Value) {
+        let inner_scope = self
+            .scopes
+            .last_mut()
+            .expect("Tried accessing scope after last frame is gone.");
+
+        // Can shadowed values be constant? No idea!
+        inner_scope.insert(ident.to_string(), AssignedValue::Mutable(value));
+    }
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 enum AssignedValue {
     Constant(Value),
     Mutable(Value),
diff --git a/src/interpret/value.rs b/src/interpret/value.rs
index f923742..dd8ad54 100644
--- a/src/interpret/value.rs
+++ b/src/interpret/value.rs
@@ -1,7 +1,12 @@
-use crate::parse::ast::nodes::FnNode;
+use crate::parse::ast::{expression::Expression, nodes::FnNode};
 use std::{cell::RefCell, fmt::Display, rc::Rc};
 use thiserror::Error;
 
+use super::{
+    scope::ScopeChain,
+    walker::{Walker, WalkerError},
+};
+
 type ReferenceOnCopy<T> = Rc<RefCell<T>>;
 
 #[derive(Clone, Debug)]
@@ -11,7 +16,7 @@ pub enum Value {
     Int(i64),
     Bool(bool),
     Array(ReferenceOnCopy<Vec<Value>>),
-    Fn(ReferenceOnCopy<FnNode>),
+    Fn(ReferenceOnCopy<FnValue>),
     Void,
 }
 
@@ -214,6 +219,53 @@ impl Value {
             x => Err(OperationError::ArrayType(x.clone())),
         }
     }
+
+    pub fn call(&self, arguments: Vec<Value>) -> Result<Value, WalkerError> {
+        let called = match self {
+            Value::Fn(i) => i,
+            i => {
+                return Err(WalkerError::OperationError(OperationError::CallableType(
+                    i.clone(),
+                )))
+            }
+        }
+        .borrow();
+
+        // FIXME: Currently closures are able to re-assign values from the upper scopes.
+        // This is good behaviour, until a closure re-assigns a value that was declared after
+        // the closure even existed.
+        // Minimal reproducible example:
+        // ```rh
+        // closure = fn { y = 10; };
+        // y = 1;
+        // closure();
+        // print y;
+        // ```
+        // Expected: 1
+        // Actual: 10
+        let mut scope = called.scope.clone();
+        scope.nest();
+
+        let parameters = &called.node.header.parameters;
+
+        if parameters.len() != arguments.len() {
+            return Err(WalkerError::OperationError(
+                OperationError::WrongArgumentCount(parameters.len(), arguments.len()),
+            ));
+        }
+
+        for (argument, parameter) in arguments.into_iter().zip(parameters.iter()) {
+            scope.set_var_shadowed(&parameter.identifier, argument);
+        }
+
+        // Yes, we create a new walker for every function call,
+        // it's *way* easier that way.
+        let mut walker = Walker::new_with_scope(scope);
+        let result =
+            walker.walk_expression(&Expression::Block(Box::new(called.node.body.clone())))?;
+
+        Ok(result)
+    }
 }
 
 impl Value {
@@ -254,6 +306,12 @@ impl Display for Value {
     }
 }
 
+#[derive(Clone, Debug)]
+pub struct FnValue {
+    pub node: FnNode,
+    pub scope: ScopeChain,
+}
+
 #[derive(Error, Debug)]
 pub enum OperationError {
     #[error("Can't add value '{0}' of type '{}' to value '{1}' of type '{}'.", .0.type_name(), .1.type_name())]
@@ -276,4 +334,8 @@ pub enum OperationError {
     ArrayType(Value),
     #[error("Array index '{index}' out of range for array of length '{length}'.")]
     ArrayIndexOutOfRange { index: i64, length: usize },
+    #[error("Can't call value '{0}' of type '{}'.", .0.type_name())]
+    CallableType(Value),
+    #[error("Function expects {0} arguments, but {1} were given.")]
+    WrongArgumentCount(usize, usize),
 }
diff --git a/src/interpret/walker.rs b/src/interpret/walker.rs
index 68dc388..3feed6f 100644
--- a/src/interpret/walker.rs
+++ b/src/interpret/walker.rs
@@ -1,29 +1,38 @@
 use std::{cell::RefCell, rc::Rc};
 
-use crate::parse::ast::{
-    expression::Expression,
-    nodes::{BinaryOperator as BinOp, BlockNode, SimpleLiteral, StrPart, UnaryOperator as UnOp},
-    statement::Statement,
-    Program,
+use crate::{
+    interpret::value::FnValue,
+    parse::ast::{
+        expression::Expression,
+        nodes::{
+            BinaryOperator as BinOp, BlockNode, SimpleLiteral, StrPart, UnaryOperator as UnOp,
+        },
+        statement::Statement,
+        Program,
+    },
 };
 use thiserror::Error;
 
 use super::{
-    scope::{Scope, ScopeError},
+    scope::{ScopeChain, ScopeError},
     value::{OperationError, Value},
 };
 
 pub struct Walker {
-    scope: Scope,
+    scope: ScopeChain,
 }
 
 impl Walker {
     pub fn new() -> Self {
         Walker {
-            scope: Scope::new(),
+            scope: ScopeChain::new(),
         }
     }
 
+    pub fn new_with_scope(scope: ScopeChain) -> Self {
+        Walker { scope }
+    }
+
     pub fn walk(&mut self, program: &Program) {
         self.scope.nest();
         for statement in program.statements.iter() {
@@ -118,14 +127,20 @@ impl Walker {
                 }
                 .map_err(WalkerError::OperationError)
             }
-            Expression::Call(_) => todo!("Calls not implemented yet."),
+            Expression::Call(node) => {
+                let called = self.walk_expression(&node.called)?;
+
+                let mut argument_values = Vec::new();
+                for argument_node in node.arguments.iter() {
+                    argument_values.push(self.walk_expression(argument_node)?);
+                }
+
+                called.call(argument_values)
+            }
             Expression::ArrayAccess(node) => {
                 let array = self.walk_expression(&node.array)?;
                 let index = self.walk_expression(&node.index)?;
-                array
-                    .subscript(index)
-                    .map(|v| v.clone())
-                    .map_err(WalkerError::OperationError)
+                array.subscript(index).map_err(WalkerError::OperationError)
             }
             Expression::MemberAccess(_) => todo!("Structures not implemented yet."),
             Expression::Group(node) => self.walk_expression(node),
@@ -160,10 +175,10 @@ impl Walker {
 
                 Ok(Value::Str(buffer))
             }
-            Expression::FnLiteral(fn_node) => {
-                let node = fn_node.as_ref().clone();
-                Ok(Value::Fn(Rc::new(RefCell::new(node))))
-            }
+            Expression::FnLiteral(node) => Ok(Value::Fn(Rc::new(RefCell::new(FnValue {
+                node: node.as_ref().clone(),
+                scope: self.scope.clone(),
+            })))),
             Expression::If(if_node) => {
                 for conditional in &if_node.conditionals {
                     if let Value::Bool(bool) = self.walk_expression(&conditional.condition)? {