aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2021-11-14 22:31:34 +0100
committerMel <einebeere@gmail.com>2021-11-14 22:31:34 +0100
commit2a3ab5c161ac98cb3c6326173e5ed78089a9ed68 (patch)
tree2a25e0017f6b90f4296e1d9f5d10980a345e1373
parent89fcc9a395f2aa9744d507d79d29aa117b89fa7e (diff)
downloadrabbithole-2a3ab5c161ac98cb3c6326173e5ed78089a9ed68.tar.zst
rabbithole-2a3ab5c161ac98cb3c6326173e5ed78089a9ed68.zip
Function calling and the ensuing scope shenanigans
-rw-r--r--src/interpret/scope.rs44
-rw-r--r--src/interpret/value.rs66
-rw-r--r--src/interpret/walker.rs49
-rw-r--r--src/parse/parser.rs11
4 files changed, 139 insertions, 31 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)? {
diff --git a/src/parse/parser.rs b/src/parse/parser.rs
index 5a09a8f..ee83293 100644
--- a/src/parse/parser.rs
+++ b/src/parse/parser.rs
@@ -217,10 +217,10 @@ impl<T: Iterator<Item = Token>> Parser<T> {
while let Some(token) = consume_if!(self, GroupOpen | ArrayOpen | Dot) {
match token.variant {
- GroupOpen => loop {
+ GroupOpen => {
let mut arguments = Vec::new();
- loop {
+ while consume_if!(self, GroupClose).is_none() {
arguments.push(self.expression()?);
if consume_if!(self, Comma).is_none() {
@@ -232,8 +232,8 @@ impl<T: Iterator<Item = Token>> Parser<T> {
left = Expression::Call(Box::new(CallNode {
called: left,
arguments,
- }))
- },
+ }));
+ }
ArrayOpen => {
let index = self.expression()?;
consume!(self, ArrayClose)?;
@@ -335,10 +335,9 @@ impl<T: Iterator<Item = Token>> Parser<T> {
fn function(&mut self) -> Result<FnNode> {
consume!(self, KeywordFn)?;
- let token = self.tokens.next().expect("Expected function header.");
let header = {
- let has_self_receiver = if let KeywordSelf = token.variant {
+ let has_self_receiver = if consume_if!(self, KeywordSelf).is_some() {
consume_if!(self, Comma);
true
} else {