diff --git a/src/ty/internal/func.rs b/src/ty/internal/func.rs
index defc542..69c16c0 100644
--- a/src/ty/internal/func.rs
+++ b/src/ty/internal/func.rs
@@ -1,8 +1,11 @@
use ecow::EcoString;
+use rpds::HashTrieMap;
use crate::bytecode::{OpCodes, ThunkIdx};
use crate::ty::internal::Value;
+use crate::vm::{LockedEnv, VM};
+
#[derive(Debug, Clone)]
pub enum Param {
Ident(EcoString),
@@ -15,20 +18,31 @@ pub enum Param {
#[derive(Debug, Clone)]
pub struct Func {
+ pub env: LockedEnv,
pub param: Option,
pub opcodes: OpCodes,
}
impl Func {
- pub fn new(opcodes: OpCodes) -> Func {
+ pub fn new(env: LockedEnv, opcodes: OpCodes) -> Func {
Func {
- param: None,
+ env,
opcodes,
+ param: None,
}
}
- pub fn call(self, _arg: Value) -> Value {
- todo!()
+ pub fn call(self, vm: &VM, arg: Value) -> Value {
+ use Param::*;
+
+ let mut env = self.env.unlocked();
+
+ match self.param.unwrap() {
+ Ident(ident) => env.enter(HashTrieMap::new_sync().insert(ident.into(), arg)),
+ Formals { .. } => todo!()
+ }
+
+ vm.eval(self.opcodes, &mut env).unwrap()
}
pub fn push_ident_param(&mut self, param: EcoString) {
diff --git a/src/ty/internal/mod.rs b/src/ty/internal/mod.rs
index a995da9..1c2ec15 100644
--- a/src/ty/internal/mod.rs
+++ b/src/ty/internal/mod.rs
@@ -109,7 +109,7 @@ impl Value {
}
}
- pub fn call(self, args: Vec) -> Value {
+ pub fn call(self, vm: &VM, args: Vec) -> Value {
use Value::*;
match self {
PrimOp(func) => func.call(args),
@@ -120,7 +120,7 @@ impl Value {
func = match func {
PrimOp(func) => return func.call([arg].into_iter().chain(iter).collect()),
PartialPrimOp(func) => return func.call([arg].into_iter().chain(iter).collect()),
- Func(func) => func.call(arg),
+ Func(func) => func.call(vm, arg),
_ => todo!()
}
}
diff --git a/src/vm/env.rs b/src/vm/env.rs
index 9bb21b4..a132f12 100644
--- a/src/vm/env.rs
+++ b/src/vm/env.rs
@@ -8,6 +8,11 @@ pub struct Env {
map: HashTrieMapSync,
}
+#[derive(Debug, Clone)]
+pub struct LockedEnv {
+ map: HashTrieMapSync
+}
+
impl Env {
pub fn empty() -> Env {
Env {
@@ -16,20 +21,19 @@ impl Env {
}
}
- pub fn lookup(&self, symbol: Symbol) -> Value {
- if let Some(value) = self.map.get(&symbol) {
- value.clone()
- } else {
- let last = self.last.as_ref().unwrap();
- last.lookup(symbol)
- }
+ pub fn lookup(&self, symbol: Symbol) -> Option {
+ self.map.get(&symbol).cloned()
}
pub fn insert(&mut self, symbol: Symbol, value: Value) {
self.map.insert_mut(symbol, value);
}
- pub fn enter(&mut self, map: HashTrieMapSync) {
+ pub fn enter(&mut self, new: HashTrieMapSync) {
+ let mut map = self.map.clone();
+ for (k, v) in new.iter() {
+ map.insert_mut(k.clone(), v.clone());
+ }
let last = std::mem::replace(self, Env { last: None, map });
self.last = Some(Box::new(last));
}
@@ -37,6 +41,23 @@ impl Env {
pub fn leave(&mut self) {
let last = std::mem::replace(&mut self.last, None).unwrap();
let _ = std::mem::replace(&mut self.last, last.last);
- self.map = last.map.clone();
+ self.map = last.map;
+ }
+
+ pub fn locked(&self) -> LockedEnv {
+ LockedEnv { map: self.map.clone() }
+ }
+}
+
+impl LockedEnv {
+ pub fn lookup(&self, symbol: Symbol) -> Option {
+ self.map.get(&symbol).cloned()
+ }
+
+ pub fn unlocked(self) -> Env {
+ Env {
+ map: self.map,
+ last: None
+ }
}
}
diff --git a/src/vm/mod.rs b/src/vm/mod.rs
index cebf8f7..f79ba37 100644
--- a/src/vm/mod.rs
+++ b/src/vm/mod.rs
@@ -6,5 +6,5 @@ mod vmthunk;
#[cfg(test)]
mod test;
-pub use env::Env;
+pub use env::{Env, LockedEnv};
pub use vm::VM;
diff --git a/src/vm/test.rs b/src/vm/test.rs
index 174b325..4691c7d 100644
--- a/src/vm/test.rs
+++ b/src/vm/test.rs
@@ -171,4 +171,6 @@ fn test_let() {
#[test]
fn test_func() {
test_expr("(x: x) 1", int!(1));
+ test_expr("(x: x) (x: x) 1", int!(1));
+ test_expr("(x: y: x + y) 1 1", int!(2));
}
diff --git a/src/vm/vm.rs b/src/vm/vm.rs
index 3dca267..290b2b4 100644
--- a/src/vm/vm.rs
+++ b/src/vm/vm.rs
@@ -1,11 +1,9 @@
-use std::ops::Index;
-
-use anyhow::Result;
+use anyhow::{Result, anyhow};
use crate::builtins::env;
-use crate::bytecode::{self, Program, OpCode, OpCodes, Thunks, UnOp, BinOp};
-use crate::ty::internal::*;
+use crate::bytecode::{self, BinOp, OpCode, OpCodes, Program, Thunks, UnOp};
use crate::ty::common::Symbol;
+use crate::ty::internal::*;
use crate::ty::public as p;
use super::env::Env;
@@ -81,19 +79,31 @@ impl VM {
args.insert(0, stack.pop()?);
}
let func = stack.pop()?;
- stack.push(func.call(args))?;
+ stack.push(func.call(self, args))?;
}
OpCode::Func { idx } => {
- stack.push(Value::Func(Func::new(self.thunks[idx].unwrap_code())))?;
+ stack.push(Value::Func(Func::new(env.locked(), self.thunks[idx].unwrap_code())))?;
}
OpCode::PushIdentParam { sym } => {
- stack.tos_mut()?.as_mut().unwrap_func().push_ident_param(sym);
+ stack
+ .tos_mut()?
+ .as_mut()
+ .unwrap_func()
+ .push_ident_param(sym);
}
OpCode::PushFormalParam { sym } => {
- stack.tos_mut()?.as_mut().unwrap_func().push_formal_param(sym);
+ stack
+ .tos_mut()?
+ .as_mut()
+ .unwrap_func()
+ .push_formal_param(sym);
}
OpCode::PushDefaultParam { idx } => {
- stack.tos_mut()?.as_mut().unwrap_func().push_default_param(idx);
+ stack
+ .tos_mut()?
+ .as_mut()
+ .unwrap_func()
+ .push_default_param(idx);
}
OpCode::SetEllipsis => {
stack.tos_mut()?.as_mut().unwrap_func().set_ellipsis();
@@ -184,7 +194,10 @@ impl VM {
stack.tos_mut()?.has_attr(sym);
}
OpCode::LookUp { sym } => {
- stack.push(env.lookup(Symbol::new(sym)))?;
+ stack.push(
+ env.lookup(Symbol::new(sym.clone()))
+ .ok_or(anyhow!(r#""{sym}" not found"#))?,
+ )?;
}
OpCode::EnterEnv => {
env.enter(stack.pop()?.unwrap_attr_set().to_data());