diff --git a/src/compile.rs b/src/compile.rs index 8dff552..b4b06cf 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -1,3 +1,5 @@ +use std::cell::OnceCell; + use crate::bytecode::*; use crate::ir; use crate::ty::internal::Const; @@ -19,7 +21,7 @@ pub fn compile(downgraded: ir::Downgraded) -> Program { .funcs .into_iter() .map(|func| Func { - env: None, + env: OnceCell::new(), param: func.param.into(), opcodes: Compiler::new().compile(*func.body), }) diff --git a/src/ty/internal/attrset.rs b/src/ty/internal/attrset.rs index 9b8fbe9..3d1b586 100644 --- a/src/ty/internal/attrset.rs +++ b/src/ty/internal/attrset.rs @@ -17,7 +17,7 @@ pub struct AttrSet { } impl AttrSet { - pub fn empty() -> AttrSet { + pub fn empty() -> Self { AttrSet { data: HashTrieMapSync::new_sync(), } diff --git a/src/ty/internal/func.rs b/src/ty/internal/func.rs index bc4feee..9c20b8c 100644 --- a/src/ty/internal/func.rs +++ b/src/ty/internal/func.rs @@ -1,3 +1,5 @@ +use std::cell::OnceCell; + use ecow::EcoString; use itertools::Itertools; use rpds::HashTrieMap; @@ -40,19 +42,19 @@ impl From for Param { #[derive(Debug, Clone)] pub struct Func { - pub env: Option, + pub env: OnceCell, pub param: Param, pub opcodes: OpCodes, } impl Func { - pub fn call(self, vm: &VM, arg: Value) -> Result { + pub fn call(&self, vm: &VM, arg: Value) -> Result { use Param::*; - let env = self.env.unwrap().released(); + let env = self.env.get().unwrap().clone().released(); - match self.param { - Ident(ident) => env.enter(HashTrieMap::new_sync().insert(ident.into(), arg)), + match &self.param { + Ident(ident) => env.enter(HashTrieMap::new_sync().insert(ident.clone().into(), arg)), Formals { formals, ellipsis, @@ -73,18 +75,18 @@ impl Func { for (formal, default) in formals { let arg = arg .select(formal.clone().into()) - .or_else(|| default.map(|idx| Value::Thunk(vm.get_thunk(idx)))) + .or_else(|| default.map(Value::ThunkRef)) .unwrap(); - new.insert_mut(formal.into(), arg); + new.insert_mut(formal.clone().into(), arg); } if let Some(alias) = alias { - new.insert_mut(alias.into(), Value::AttrSet(arg)); + new.insert_mut(alias.clone().into(), Value::AttrSet(arg)); } env.enter(new); } } - vm.eval(self.opcodes, env) + vm.eval(self.opcodes.clone(), env) } } diff --git a/src/ty/internal/mod.rs b/src/ty/internal/mod.rs index 6666be6..862b08c 100644 --- a/src/ty/internal/mod.rs +++ b/src/ty/internal/mod.rs @@ -101,48 +101,52 @@ impl PartialEq for Thunk { pub enum Value { Const(Const), Thunk(Thunk), + ThunkRef(usize), AttrSet(AttrSet), RecAttrSet(RecAttrSet), List(List), Catchable(c::Catchable), PrimOp(PrimOp), PartialPrimOp(PartialPrimOp), - Func(Func), + Func(usize), } #[derive(Debug, IsVariant, Unwrap, Clone, PartialEq)] -pub enum ValueAsRef<'a> { - Const(&'a Const), - Thunk(&'a Thunk), - AttrSet(&'a AttrSet), - RecAttrSet(&'a RecAttrSet), - List(&'a List), - Catchable(&'a c::Catchable), - PrimOp(&'a PrimOp), - PartialPrimOp(&'a PartialPrimOp), - Func(&'a Func), +pub enum ValueAsRef<'v> { + Const(&'v Const), + Thunk(&'v Thunk), + ThunkRef(&'v usize), + AttrSet(&'v AttrSet), + RecAttrSet(&'v RecAttrSet), + List(&'v List), + Catchable(&'v c::Catchable), + PrimOp(&'v PrimOp), + PartialPrimOp(&'v PartialPrimOp), + Func(&'v usize), } #[derive(Debug, IsVariant, Unwrap, PartialEq)] -pub enum ValueAsMut<'a> { - Const(&'a mut Const), - Thunk(&'a mut Thunk), - AttrSet(&'a mut AttrSet), - RecAttrSet(&'a mut RecAttrSet), - List(&'a mut List), - Catchable(&'a mut c::Catchable), - PrimOp(&'a mut PrimOp), - PartialPrimOp(&'a mut PartialPrimOp), - Func(&'a mut Func), +pub enum ValueAsMut<'v> { + Const(&'v mut Const), + Thunk(&'v Thunk), + ThunkRef(&'v usize), + AttrSet(&'v mut AttrSet), + RecAttrSet(&'v mut RecAttrSet), + List(&'v mut List), + Catchable(&'v mut c::Catchable), + PrimOp(&'v mut PrimOp), + PartialPrimOp(&'v mut PartialPrimOp), + Func(&'v usize), } impl Value { - pub fn as_ref(&self) -> ValueAsRef<'_> { + pub fn as_ref(&self) -> ValueAsRef { use Value::*; use ValueAsRef as R; match self { Const(x) => R::Const(x), Thunk(x) => R::Thunk(x), + ThunkRef(x) => R::ThunkRef(x), AttrSet(x) => R::AttrSet(x), RecAttrSet(x) => R::RecAttrSet(x), List(x) => R::List(x), @@ -153,12 +157,13 @@ impl Value { } } - pub fn as_mut(&mut self) -> ValueAsMut<'_> { + pub fn as_mut(&mut self) -> ValueAsMut { use Value::*; use ValueAsMut as M; match self { Const(x) => M::Const(x), Thunk(x) => M::Thunk(x), + ThunkRef(x) => M::ThunkRef(x), AttrSet(x) => M::AttrSet(x), RecAttrSet(x) => M::RecAttrSet(x), List(x) => M::List(x), @@ -177,6 +182,7 @@ impl Value { match self { Const(_) => todo!(), Thunk(_) => "thunk", + ThunkRef(_) => "thunk", AttrSet(_) => "set", RecAttrSet(_) => "set", List(_) => "list", @@ -210,7 +216,7 @@ impl Value { PartialPrimOp(func) => { return Ok(func.call(vm, [arg].into_iter().chain(iter).collect())); } - Func(func) => func.call(vm, arg)?, + Func(func) => vm.get_func(func).call(vm, arg)?, _ => todo!(), } } @@ -361,22 +367,19 @@ impl Value { } pub fn select(&mut self, sym: Symbol) -> &mut Self { - if let Value::AttrSet(attrs) = self { - let val = attrs.select(sym.clone()).unwrap_or_else(|| { + let val = match self { + Value::AttrSet(attrs) => attrs.select(sym.clone()).unwrap_or_else(|| { Value::Catchable(c::Catchable::new(Some(format!("{sym:?} not found")))) - }); - *self = val; - } else if let Value::RecAttrSet(attrs) = self { - let val = attrs.select(sym.clone()).unwrap_or_else(|| { + }), + Value::RecAttrSet(attrs) => attrs.select(sym.clone()).unwrap_or_else(|| { Value::Catchable(c::Catchable::new(Some(format!("{sym:?} not found")))) - }); - *self = val; - } else { - *self = Value::Catchable(Catchable::new(Some(format!( + }), + _ => Value::Catchable(Catchable::new(Some(format!( "cannot select from {:?}", self.typename() - )))) - } + )))), + }; + *self = val; self } @@ -426,7 +429,7 @@ impl Value { match self { Value::Thunk(thunk) => { let mut value = thunk.force(vm)?; - value.force_deep(vm)?; + let _ = value.force_deep(vm)?; *self = value; } Value::List(list) => list.force_deep(vm)?, @@ -447,6 +450,7 @@ impl ToPublic for Value { Value::Catchable(catchable) => p::Value::Catchable(catchable), Value::Const(cnst) => p::Value::Const(cnst.into()), Value::Thunk(_) => p::Value::Thunk, + Value::ThunkRef(_) => p::Value::Thunk, Value::PrimOp(primop) => p::Value::PrimOp(primop.name), Value::PartialPrimOp(primop) => p::Value::PartialPrimOp(primop.name), Value::Func(_) => p::Value::Func, diff --git a/src/vm/mod.rs b/src/vm/mod.rs index cd5d3db..fcb77d2 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -37,12 +37,12 @@ impl VM { VM { thunks, funcs } } - pub fn get_thunk(&self, idx: usize) -> Thunk { - self.thunks[idx].clone() + pub fn get_thunk(&self, idx: usize) -> &Thunk { + &self.thunks[idx] } - pub fn get_func(&self, idx: usize) -> Func { - self.funcs[idx].clone() + pub fn get_func(&self, idx: usize) -> &Func { + &self.funcs[idx] } pub fn eval(&self, opcodes: OpCodes, env: Arc) -> Result { @@ -101,9 +101,8 @@ impl VM { stack.push(func.call(self, args)?)?; } OpCode::Func { idx } => { - let mut func = self.get_func(idx); - func.env = Some(env.captured()); - stack.push(Value::Func(func))?; + self.get_func(idx).env.get_or_init(|| env.captured()); + stack.push(Value::Func(idx))?; } OpCode::UnOp { op } => { use UnOp::*;