diff --git a/Cargo.lock b/Cargo.lock index d8385d7..047f162 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -267,6 +267,15 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "lru" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f8cc7106155f10bdf99a6f379688f543ad6596a415375b36a59a054ceda1198" +dependencies = [ + "hashbrown 0.15.3", +] + [[package]] name = "memchr" version = "2.7.4" @@ -312,6 +321,7 @@ dependencies = [ "hashbrown 0.15.3", "inkwell", "itertools", + "lru", "priority-queue", "regex", "replace_with", diff --git a/Cargo.toml b/Cargo.toml index cb24a38..dab0a9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ ecow = "0.2" regex = "1.11" hashbrown = "0.15" priority-queue = "2.5" +lru = "0.14" replace_with = "0.1" inkwell = { version = "0.6.0", features = ["llvm18-1"] } diff --git a/src/bin/eval.rs b/src/bin/eval.rs index dc6776c..80c5fd2 100644 --- a/src/bin/eval.rs +++ b/src/bin/eval.rs @@ -2,10 +2,10 @@ use std::process::exit; use itertools::Itertools; +use nixjit::engine::eval; use nixjit::error::Error; use nixjit::error::Result; use nixjit::ir::downgrade; -use nixjit::engine::eval; fn main() -> Result<()> { let mut args = std::env::args(); diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index f1a282f..3b17c92 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -1,7 +1,7 @@ use ecow::EcoString; use hashbrown::HashMap; -use crate::ir::{DowngradeContext, Ir, Const}; +use crate::ir::{Const, DowngradeContext, Ir}; pub fn ir_env(ctx: &mut DowngradeContext) -> HashMap { let mut map = HashMap::new(); diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 1a9cadb..005aaf9 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -1,52 +1,63 @@ -use std::sync::RwLock; use std::rc::Rc; use hashbrown::HashSet; +use lru::LruCache; use priority_queue::PriorityQueue; use crate::env::VmEnv; +use crate::error::Result; use crate::eval::Evaluate; use crate::ir::{Downgraded, Ir}; -use crate::ty::public::Value; use crate::ty::internal as i; -use crate::error::Result; +use crate::ty::public::Value; #[cfg(test)] mod test; +type ThunkIdx = usize; +type EnvIdx = usize; + pub struct Engine { - thunks: Box<[RwLock]>, - func_offset: usize, - tasks: PriorityQueue + thunks: Box<[Ir]>, + pub func_offset: usize, + tasks: PriorityQueue, + lru: LruCache<(ThunkIdx, EnvIdx), i::Value>, } pub fn eval(downgraded: Downgraded) -> Result { let mut engine = Engine::new(downgraded.thunks, downgraded.func_offset); - engine.eval(downgraded.top_level, &VmEnv::new(vec![])).map(|mut val| Ok(val.force(&engine)?.to_public(&engine, &mut HashSet::new())))? + engine + .eval(downgraded.top_level, &mut VmEnv::new()) + .map(|mut val| { + Ok(val + .force(&mut engine)? + .to_public(&engine, &mut HashSet::new())) + })? } impl Engine { pub fn new(thunks: Box<[Ir]>, func_offset: usize) -> Self { Self { - thunks: thunks.into_iter().map(Thunk::new).map(RwLock::new).collect(), + lru: LruCache::new(thunks.len().clamp(1, usize::MAX).try_into().unwrap()), + thunks, func_offset, - tasks: PriorityQueue::new() + tasks: PriorityQueue::new(), } } - pub fn eval(&mut self, expr: Ir, env: &VmEnv) -> Result { + pub fn eval(&mut self, expr: Ir, env: &mut VmEnv) -> Result { expr.eval(self, env) } - pub fn eval_thunk(&self, idx: usize, env: &VmEnv) -> Result { - todo!() + pub fn eval_thunk(&mut self, idx: usize, env: &mut VmEnv) -> Result { + self.thunks[idx].clone().eval(self, env) } } enum Thunk { Expr(Ir), Compiling, - Compiled(fn(Rc) -> Value) + Compiled(fn(Rc) -> Value), } impl Thunk { @@ -57,5 +68,5 @@ impl Thunk { #[derive(Hash, PartialEq, Eq)] struct CompileTask { - idx: usize + idx: usize, } diff --git a/src/engine/test.rs b/src/engine/test.rs index bea9a0f..ce79b44 100644 --- a/src/engine/test.rs +++ b/src/engine/test.rs @@ -221,4 +221,3 @@ fn bench_fib(b: &mut Bencher) { black_box(()) }) } - diff --git a/src/env.rs b/src/env.rs index dd7f9cb..131efb2 100644 --- a/src/env.rs +++ b/src/env.rs @@ -1,33 +1,34 @@ -use std::hash::Hash; -use std::rc::Rc; +use std::cell::RefCell; +use std::rc::{Rc, Weak}; use ecow::EcoString; use hashbrown::HashMap; -use crate::ty::internal::Value; +use crate::stack::Stack; +use crate::ty::internal::{EnvRef, Value}; -pub struct Env { - let_: Rc>, - with: Rc>, - args: Rc>, +#[derive(Clone)] +pub struct VmEnv { + let_: Rc, 1000>>>, + with: Rc, + args: Rc>>, + new: bool, + pub id: usize, } -pub struct LetEnv { - map: Vec, - last: Option>>, +#[derive(Clone)] +pub struct VmEnvWeak { + let_: Weak, 1000>>>, + with: Weak, + args: Weak>>, + new: bool, + pub id: usize, } -pub type VmEnv = Env; - #[derive(Default, Clone)] -pub struct With { - map: Option>>, - last: Option>>, -} - -enum LetNode { - Let(Rc>), - MultiArg(Rc>), +pub struct With { + map: Option>>, + last: Option>, } #[derive(Clone, Copy)] @@ -37,27 +38,30 @@ pub enum Type { With, } -impl Env { - pub fn new(map: Vec) -> Self { +impl VmEnv { + pub fn new() -> Self { Self { - let_: LetEnv::new(map), + let_: Rc::default(), with: With { map: None, last: None, - }.into(), - args: Vec::new().into(), + } + .into(), + args: Rc::default(), + new: false, + id: 0, } } - pub fn lookup_arg(&self, level: usize) -> &V { - &self.args[self.args.len() - level - 1] + pub fn lookup_let(&self, level: usize, idx: usize) -> Value { + self.let_.borrow()[level][idx].clone() } - pub fn lookup_let(&self, level: usize, idx: usize) -> &V { - self.let_.lookup(level, idx) + pub fn lookup_arg(&self, level: usize) -> Value { + self.args.borrow()[level].clone() } - pub fn lookup_with(&self, symbol: &K) -> Option<&V> { + pub fn lookup_with(&self, symbol: &EcoString) -> Option { self.with.lookup(symbol) } @@ -65,69 +69,103 @@ impl Env { self.with.map.is_some() } - #[must_use] - pub fn enter_arg(&self, val: V) -> Self { - let mut args = self.args.clone(); - Rc::make_mut(&mut args).push(val); - Self { - let_: self.let_.clone(), - with: self.with.clone(), - args, + pub fn enter_let(&mut self, mut map: Vec) { + if Rc::strong_count(&self.let_) > 1 { + self.let_ = Rc::new_cyclic(|weak| { + let weak = VmEnvWeak { + let_: weak.clone(), + with: Rc::downgrade(&self.with), + args: Rc::downgrade(&self.args), + new: self.new, + id: self.id, + }; + map.iter_mut().for_each(|val| { + if let Value::Thunk(thunk) = val { + thunk.capture(EnvRef::Weak(weak.clone())); + } + }); + let new = self.let_.as_ref().clone(); + new.borrow_mut().push(map).unwrap(); + new + }) + } else { + let weak = self.downgrade(); + map.iter_mut().for_each(|val| { + if let Value::Thunk(thunk) = val { + thunk.capture(EnvRef::Weak(weak.clone())); + } + }); + let _ = self.let_.borrow_mut().push(map); } } - #[must_use] - pub fn enter_let(&self, map: Vec) -> Self { - Self { - let_: self.let_.clone().enter_let(map), - with: self.with.clone(), - args: self.args.clone(), + pub fn enter_arg(&mut self, val: Value) { + if Rc::strong_count(&self.args) > 1 { + self.args = Rc::new(self.args.as_ref().clone()); } + self.args.borrow_mut().push(val).unwrap() + } + + pub fn pop_let(&mut self) { + if Rc::strong_count(&self.let_) > 1 { + self.let_ = Rc::new(self.let_.as_ref().clone()); + } + self.let_.borrow_mut().pop(); + } + + pub fn pop_arg(&mut self) { + if Rc::strong_count(&self.args) > 1 { + self.args = Rc::new(self.args.as_ref().clone()); + } + self.args.borrow_mut().pop(); } #[must_use] - pub fn enter_with(&self, map: Rc>) -> Self { + pub fn enter_with(&self, map: Rc>) -> Self { Self { let_: self.let_.clone(), with: self.with.clone().enter(map), args: self.args.clone(), + new: self.new, + id: self.id + 1, + } + } + + pub fn downgrade(&self) -> VmEnvWeak { + VmEnvWeak { + let_: Rc::downgrade(&self.let_), + with: Rc::downgrade(&self.with), + args: Rc::downgrade(&self.args), + id: self.id, + new: self.new, } } } -impl LetEnv { - pub fn new(map: Vec) -> Rc { - Rc::new(Self { map, last: None }) - } - - pub fn lookup(&self, level: usize, idx: usize) -> &V { - let mut cur = self; - for _ in 0..level { - cur = cur.last.as_ref().unwrap(); - } - &cur.map[idx] - } - - pub fn enter_let(self: Rc, map: Vec) -> Rc { - Rc::new(Self { - map, - last: Some(self), - }) - } -} - -impl With { - pub fn lookup(&self, symbol: &K) -> Option<&V> { +impl With { + pub fn lookup(&self, symbol: &EcoString) -> Option { if let Some(val) = self.map.as_ref()?.get(symbol) { - return Some(val); + return Some(val.clone()); } self.last.as_ref().and_then(|env| env.lookup(symbol)) } - pub fn enter(self: Rc, map: Rc>) -> Rc { + pub fn enter(self: Rc, map: Rc>) -> Rc { Rc::new(Self { map: Some(map), last: Some(self), }) } } + +impl VmEnvWeak { + pub fn upgrade(&self) -> VmEnv { + VmEnv { + let_: self.let_.upgrade().unwrap(), + with: self.with.upgrade().unwrap(), + args: self.args.upgrade().unwrap(), + id: self.id, + new: self.new, + } + } +} diff --git a/src/eval/jit/compile.rs b/src/eval/jit/compile.rs index dea820a..cd3dd83 100644 --- a/src/eval/jit/compile.rs +++ b/src/eval/jit/compile.rs @@ -14,7 +14,7 @@ impl JITCompile for Attrs { } } -impl JITCompile for List { +impl JITCompile for List { fn compile<'gc>(self, ctx: &JITContext<'gc>) { todo!() } @@ -32,7 +32,7 @@ impl JITCompile for BinOp { } } -impl JITCompile for UnOp { +impl JITCompile for UnOp { fn compile<'gc>(self, ctx: &JITContext<'gc>) { todo!() } @@ -91,13 +91,13 @@ impl JITCompile for Const { todo!() } } - + impl JITCompile for String { fn compile<'gc>(self, ctx: &JITContext<'gc>) { todo!() } } - + impl JITCompile for Var { fn compile<'gc>(self, ctx: &JITContext<'gc>) { todo!() @@ -124,6 +124,6 @@ impl JITCompile for Thunk { impl JITCompile for Path { fn compile<'gc>(self, ctx: &JITContext<'gc>) { - todo!() + todo!() } } diff --git a/src/eval/jit/helpers.rs b/src/eval/jit/helpers.rs index 7e69069..10b0844 100644 --- a/src/eval/jit/helpers.rs +++ b/src/eval/jit/helpers.rs @@ -10,7 +10,7 @@ use inkwell::values::{BasicValueEnum, FunctionValue}; use crate::env::VmEnv; use crate::eval::Engine; -use super::{JITContext, JITValue, ValueTag, JITValueData}; +use super::{JITContext, JITValue, JITValueData, ValueTag}; pub struct Helpers<'ctx> { pub int_type: IntType<'ctx>, @@ -341,9 +341,7 @@ extern "C" fn helper_arg(idx: usize, env: *const VmEnv) -> JITValue { } extern "C" fn helper_lookup_let(level: usize, idx: usize, env: *const VmEnv) -> JITValue { - let env = unsafe { env.as_ref() }.unwrap(); - let val: JITValue = env.lookup_let(level, idx).clone().into(); - val + todo!() } extern "C" fn helper_lookup(sym: usize, env: *const VmEnv) -> JITValue { diff --git a/src/eval/jit/mod.rs b/src/eval/jit/mod.rs index f3ae1f0..5ac687f 100644 --- a/src/eval/jit/mod.rs +++ b/src/eval/jit/mod.rs @@ -1,5 +1,5 @@ -use std::ops::Deref; use std::marker::PhantomData; +use std::ops::Deref; use inkwell::OptimizationLevel; use inkwell::builder::Builder; @@ -10,8 +10,8 @@ use inkwell::module::Module; use crate::env::VmEnv; use crate::ty::internal::Value; -mod helpers; mod compile; +mod helpers; pub use compile::JITCompile; use helpers::Helpers; @@ -105,7 +105,6 @@ impl Deref for JITFunc<'_> { } } - pub struct JITContext<'ctx> { context: &'ctx Context, module: Module<'ctx>, diff --git a/src/eval/jit/test.rs b/src/eval/jit/test.rs index ade3c43..abd2ee5 100644 --- a/src/eval/jit/test.rs +++ b/src/eval/jit/test.rs @@ -8,8 +8,8 @@ use inkwell::context::Context; use ecow::EcoString; -use crate::ir::downgrade; use super::JITContext; +use crate::ir::downgrade; use crate::ty::common::Const; use crate::ty::public::*; diff --git a/src/eval/mod.rs b/src/eval/mod.rs index cbbc236..508c363 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -7,26 +7,34 @@ use crate::env::VmEnv; use crate::error::{Error, Result}; use crate::ir::{self, DynamicAttrPair}; use crate::ty::common::Const; -use crate::ty::internal::{AttrSet, List, Thunk, ThunkRef, Value}; +use crate::ty::internal::{AttrSet, EnvRef, List, ThunkRef, Value}; use crate::ty::public::Symbol; pub mod jit; pub trait Evaluate { - fn eval(self, engine: &Engine, env: &VmEnv) -> Result; + fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result; } impl Evaluate for ir::Attrs { - fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { let mut attrs = AttrSet::new( self.stcs .into_iter() - .map(|(k, v)| Ok((k, v.eval(engine, env)?))) + .map(|(k, v)| { + Ok((k, { + let mut val = v.eval(engine, env)?; + if let Value::Thunk(thunk) = &mut val { + thunk.capture(EnvRef::Strong(env.clone())); + } + val + })) + }) .collect::>()?, ); for DynamicAttrPair(k, v) in self.dyns { let mut k = k.eval(engine, env)?; - k.coerce_to_string(); + k.force(engine)?.coerce_to_string(); attrs.push_attr(k.unwrap_string(), v.eval(engine, env)?); } Value::AttrSet(attrs.into()).ok() @@ -34,11 +42,17 @@ impl Evaluate for ir::Attrs { } impl Evaluate for ir::List { - fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { Value::List(List::from( self.items .into_iter() - .map(|val| val.eval(engine, env)) + .map(|val| { + let mut val = val.eval(engine, env)?; + if let Value::Thunk(thunk) = &mut val { + thunk.capture(EnvRef::Strong(env.clone())); + } + val.ok() + }) .collect::>>()?, )) .ok() @@ -46,7 +60,7 @@ impl Evaluate for ir::List { } impl Evaluate for ir::HasAttr { - fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { use ir::Attr::*; let mut val = self.lhs.eval(engine, env)?; val.has_attr(self.rhs.into_iter().map(|attr| { @@ -55,7 +69,7 @@ impl Evaluate for ir::HasAttr { Strs(expr) => expr.eval(engine, env)?.unwrap_string(), Dynamic(expr) => { let mut val = expr.eval(engine, env)?; - val.coerce_to_string(); + val.force(engine)?.coerce_to_string(); val.unwrap_string() } }) @@ -65,10 +79,12 @@ impl Evaluate for ir::HasAttr { } impl Evaluate for ir::BinOp { - fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { use ir::BinOpKind::*; let mut lhs = self.lhs.eval(engine, env)?; let mut rhs = self.rhs.eval(engine, env)?; + lhs.force(engine)?; + rhs.force(engine)?; match self.kind { Add => lhs.add(rhs), Sub => { @@ -115,9 +131,10 @@ impl Evaluate for ir::BinOp { } impl Evaluate for ir::UnOp { - fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { use ir::UnOpKind::*; let mut rhs = self.rhs.eval(engine, env)?; + rhs.force(engine)?; match self.kind { Neg => rhs.neg(), Not => rhs.not(), @@ -127,43 +144,45 @@ impl Evaluate for ir::UnOp { } impl Evaluate for ir::Select { - fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { use ir::Attr::*; let mut val = self.expr.eval(engine, env)?; if let Some(default) = self.default { - val.select_with_default( + let default = default.eval(engine, env)?; + val.force(engine)?.select_with_default( self.attrpath.into_iter().map(|attr| { Ok(match attr { Str(ident) => ident, Strs(expr) => expr.eval(engine, env)?.unwrap_string(), Dynamic(expr) => { let mut val = expr.eval(engine, env)?; - val.coerce_to_string(); + val.force(engine)?.coerce_to_string(); val.unwrap_string() } }) }), - default.eval(engine, env)?, + default, )?; } else { - val.select(self.attrpath.into_iter().map(|attr| { - Ok(match attr { - Str(ident) => ident, - Strs(expr) => expr.eval(engine, env)?.unwrap_string(), - Dynamic(expr) => { - let mut val = expr.eval(engine, env)?; - val.coerce_to_string(); - val.unwrap_string() - } - }) - }))?; + val.force(engine)? + .select(self.attrpath.into_iter().map(|attr| { + Ok(match attr { + Str(ident) => ident, + Strs(expr) => expr.eval(engine, env)?.unwrap_string(), + Dynamic(expr) => { + let mut val = expr.eval(engine, env)?; + val.force(engine)?.coerce_to_string(); + val.unwrap_string() + } + }) + }))?; } val.ok() } } impl Evaluate for ir::If { - fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { // TODO: Error Handling let cond = self.cond.eval(engine, env)?.unwrap_bool(); if cond { @@ -175,14 +194,19 @@ impl Evaluate for ir::If { } impl Evaluate for ir::LoadFunc { - fn eval(self, _: &Engine, _: &VmEnv) -> Result { - Value::Func(Rc::new(ThunkRef::new(self.idx).into())).ok() + fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { + Value::Func(ThunkRef { + idx: engine.func_offset + self.idx, + env: Some(EnvRef::Strong(env.clone())).into(), + }) + .ok() } } impl Evaluate for ir::Call { - fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { let mut func = self.func.eval(engine, env)?; + func.force(engine)?; // FIXME: Modify Value::call for arg in self.args { func.call(arg.eval(engine, env)?, engine)?; @@ -192,34 +216,44 @@ impl Evaluate for ir::Call { } impl Evaluate for ir::Let { - fn eval(self, engine: &Engine, env: &VmEnv) -> Result { - let bindings = self.bindings.into_iter().map(|(_, v)| Ok(v.eval(engine, env)?)).collect::>>()?; - self.expr.eval(engine, &env.enter_let(bindings)) + fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { + let bindings = self + .bindings + .into_iter() + .map(|(_, v)| v.eval(engine, env)) + .collect::>>()?; + env.enter_let(bindings); + let val = self.expr.eval(engine, env)?; + env.pop_let(); + Ok(val) } } impl Evaluate for ir::With { - fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { let namespace = self.namespace.eval(engine, env)?; // TODO: Error Handling - self.expr.eval(engine, &env.enter_with(namespace.unwrap_attr_set().into_inner())) + self.expr.eval( + engine, + &mut env.enter_with(namespace.unwrap_attr_set().into_inner()), + ) } } impl Evaluate for ir::Assert { - fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { todo!() } } impl Evaluate for ir::ConcatStrings { - fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { let mut parts = self .parts .into_iter() .map(|part| { let mut part = part.eval(engine, env)?; - part.coerce_to_string(); + part.force(engine)?.coerce_to_string(); part.ok() }) .collect::>>()? @@ -235,13 +269,13 @@ impl Evaluate for ir::ConcatStrings { } impl Evaluate for ir::String { - fn eval(self, _: &Engine, _: &VmEnv) -> Result { + fn eval(self, _: &mut Engine, _: &mut VmEnv) -> Result { Value::String(self.val).ok() } } impl Evaluate for ir::Const { - fn eval(self, _: &Engine, _: &VmEnv) -> Result { + fn eval(self, _: &mut Engine, _: &mut VmEnv) -> Result { match self.val { Const::Null => Value::Null, Const::Int(x) => Value::Int(x), @@ -253,33 +287,36 @@ impl Evaluate for ir::Const { } impl Evaluate for ir::Var { - fn eval(self, _: &Engine, env: &VmEnv) -> Result { + fn eval(self, _: &mut Engine, env: &mut VmEnv) -> Result { env.lookup_with(&self.sym) - .cloned() .ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(self.sym)))) } } impl Evaluate for ir::Arg { - fn eval(self, _: &Engine, env: &VmEnv) -> Result { + fn eval(self, _: &mut Engine, env: &mut VmEnv) -> Result { env.lookup_arg(self.level).clone().ok() } } impl Evaluate for ir::LetVar { - fn eval(self, _: &Engine, env: &VmEnv) -> Result { - env.lookup_let(self.level, self.idx).clone().ok() + fn eval(self, _: &mut Engine, env: &mut VmEnv) -> Result { + let mut ret = env.lookup_let(self.level, self.idx); + if let Value::Thunk(thunk) = &mut ret { + thunk.upgrade(); + } + ret.ok() } } impl Evaluate for ir::Thunk { - fn eval(self, _: &Engine, _: &VmEnv) -> Result { - Value::Thunk(Rc::new(Thunk::new(self.idx).into())).ok() + fn eval(self, _: &mut Engine, _: &mut VmEnv) -> Result { + Value::Thunk(ThunkRef::new(self.idx)).ok() } } impl Evaluate for ir::Path { - fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { todo!() } } diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 551fa17..930d89f 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -99,7 +99,7 @@ macro_rules! ir { } impl Evaluate for Ir { - fn eval(self, engine: &Engine, env: &VmEnv) -> Result { + fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { match self { $(Ir::$ty(ir) => ir.eval(engine, env),)* } @@ -182,6 +182,8 @@ pub struct DowngradeContext { struct Env<'a, 'env> { env: EnvNode<'a>, prev: Option<&'env Env<'a, 'env>>, + arg_level: usize, + let_level: usize } enum EnvNode<'a> { @@ -205,6 +207,8 @@ impl<'a, 'env> Env<'a, 'env> { Self { env: EnvNode::Builtins(base), prev: None, + arg_level: 0, + let_level: 0 } } @@ -212,6 +216,8 @@ impl<'a, 'env> Env<'a, 'env> { Self { env: EnvNode::Let(map), prev: Some(self), + arg_level: self.arg_level, + let_level: self.let_level + 1 } } @@ -219,6 +225,8 @@ impl<'a, 'env> Env<'a, 'env> { Self { env: EnvNode::SingleArg(ident), prev: Some(self), + arg_level: self.arg_level + 1, + let_level: self.let_level } } @@ -230,6 +238,8 @@ impl<'a, 'env> Env<'a, 'env> { Self { env: EnvNode::MultiArg(map, alias), prev: Some(self), + arg_level: self.arg_level + 1, + let_level: 0 } } @@ -237,6 +247,8 @@ impl<'a, 'env> Env<'a, 'env> { Self { env: EnvNode::With, prev: Some(self), + arg_level: self.arg_level, + let_level: self.let_level } } @@ -262,30 +274,30 @@ impl<'a, 'env> Env<'a, 'env> { Let(map) => { if let Ok(idx) = map.binary_search(ident) { return Ok(LookupResult::Let { - level: let_level, + level: let_level - 1, idx, }); } else { - let_level += 1; + let_level -= 1; } } SingleArg(arg) => { if arg == ident { - return Ok(LookupResult::SingleArg { level: arg_level }); + return Ok(LookupResult::SingleArg { level: arg_level - 1 }); } else { - arg_level += 1; + arg_level -= 1; } } MultiArg(set, alias) => { if let Some(default) = set.get(ident) { return Ok(LookupResult::MultiArg { - level: arg_level, + level: arg_level - 1, default: default.clone(), }); } else if alias.as_ref() == Some(ident) { - return Ok(LookupResult::SingleArg { level: arg_level }); + return Ok(LookupResult::SingleArg { level: arg_level - 1 }); } else { - arg_level += 1; + arg_level -= 1; } } With => has_with = true, @@ -296,7 +308,7 @@ impl<'a, 'env> Env<'a, 'env> { } fn lookup(&self, ident: &EcoString) -> core::result::Result { - self._lookup(ident, 0, 0, false) + self._lookup(ident, self.arg_level, self.let_level, false) } } @@ -367,30 +379,25 @@ impl Attrs { ) -> Result<()> { if let Some(attr) = path.next() { match attr { - Attr::Str(ident) => { - if self.stcs.get(&ident).is_some() { - self.stcs - .get_mut(&ident) - .unwrap() - .as_mut() - .try_unwrap_attrs() - .map_err(|_| { - Error::DowngradeError(format!( - r#"attribute '{}' already defined"#, - Symbol::from(ident) - )) - }) - .and_then(|attrs: &mut Attrs| attrs._insert(path, name, value, ctx)) - } else { - let mut attrs = Attrs { + Attr::Str(ident) => self + .stcs + .entry(ident.clone()) + .or_insert_with(|| { + Attrs { stcs: HashMap::new(), dyns: Vec::new(), - }; - attrs._insert(path, name, value, ctx)?; - assert!(self.stcs.insert(ident, attrs.ir()).is_none()); - Ok(()) - } - } + } + .ir() + }) + .as_mut() + .try_unwrap_attrs() + .map_err(|_| { + Error::DowngradeError(format!( + r#"attribute '{}' already defined"#, + Symbol::from(ident) + )) + }) + .and_then(|attrs: &mut Attrs| attrs._insert(path, name, value, ctx)), Attr::Strs(string) => { let mut attrs = Attrs { stcs: HashMap::new(), @@ -413,13 +420,12 @@ impl Attrs { } else { match name { Attr::Str(ident) => { - if self.stcs.get(&ident).is_some() { + if self.stcs.insert(ident.clone(), value).is_some() { return Err(Error::DowngradeError(format!( r#"attribute '{}' already defined"#, Symbol::from(ident) ))); } - self.stcs.insert(ident, value); } Attr::Strs(string) => { self.dyns.push(DynamicAttrPair(string.ir(), value)); diff --git a/src/ir/utils.rs b/src/ir/utils.rs index f5529ce..03bed34 100644 --- a/src/ir/utils.rs +++ b/src/ir/utils.rs @@ -2,7 +2,6 @@ use rnix::ast; use super::*; - pub fn downgrade_param(param: ast::Param, ctx: &mut DowngradeContext) -> Result { match param { ast::Param::IdentParam(ident) => Ok(Param::Ident(ident.to_string().into())), @@ -122,7 +121,10 @@ pub fn downgrade_attr(attr: ast::Attr, ctx: &mut DowngradeContext) -> Result Result> { +pub fn downgrade_attrpath( + attrpath: ast::Attrpath, + ctx: &mut DowngradeContext, +) -> Result> { attrpath .attrs() .map(|attr| downgrade_attr(attr, ctx)) diff --git a/src/lib.rs b/src/lib.rs index b4076ad..ad05b09 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,8 +6,8 @@ mod env; mod stack; mod ty; -pub mod error; -pub mod ir; -pub mod eval; pub mod engine; +pub mod error; +pub mod eval; +pub mod ir; pub use ty::public::Value; diff --git a/src/stack.rs b/src/stack.rs index 568a049..163a389 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -1,5 +1,5 @@ use std::mem::{MaybeUninit, replace, transmute}; -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; use crate::error::*; @@ -18,13 +18,25 @@ pub struct Stack { top: usize, } - impl Default for Stack { fn default() -> Self { Self::new() } } +impl Clone for Stack { + fn clone(&self) -> Self { + let mut items = [const { MaybeUninit::uninit() }; CAP]; + for (i, item) in self.items.iter().take(self.top).enumerate() { + items[i].write(unsafe { item.assume_init_ref() }.clone()); + } + Self { + items, + top: self.top, + } + } +} + impl Stack { pub fn new() -> Self { Stack { @@ -68,6 +80,12 @@ impl Deref for Stack { } } +impl DerefMut for Stack { + fn deref_mut(&mut self) -> &mut Self::Target { + into!(&mut self.items[0..self.top]) + } +} + impl Drop for Stack { fn drop(&mut self) { self.items.as_mut_slice()[0..self.top] diff --git a/src/ty/internal/attrset.rs b/src/ty/internal/attrset.rs index 24ff79c..aac3712 100644 --- a/src/ty/internal/attrset.rs +++ b/src/ty/internal/attrset.rs @@ -1,8 +1,8 @@ use std::ops::Deref; use std::rc::Rc; -use ecow::EcoString; use derive_more::Constructor; +use ecow::EcoString; use hashbrown::{HashMap, HashSet}; use itertools::Itertools; @@ -50,30 +50,39 @@ impl AttrSet { self.data.insert(sym, val); } - pub fn select(&self, mut path: impl DoubleEndedIterator>) -> Result { - // .ok_or_else(|| Error::EvalError())), + pub fn select( + &self, + mut path: impl DoubleEndedIterator>, + ) -> Result { + // .ok_or_else(|| Error::EvalError())), let mut data = &self.data; let last = path.nth_back(0).unwrap(); for item in path { let item = item?; - let Some(Value::AttrSet(attrs)) = data.get(&item) else { - return Err(Error::EvalError(format!("{} not found", Symbol::from(item)))) + let Some(Value::AttrSet(attrs)) = data.get(&item) else { + return Err(Error::EvalError(format!( + "{} not found", + Symbol::from(item) + ))); }; data = attrs.as_inner(); } let last = last?; - data.get(&last).cloned().ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(last)))) + data.get(&last) + .cloned() + .ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(last)))) } - pub fn has_attr(&self, path: impl IntoIterator>) -> Result { + pub fn has_attr(&self, mut path: impl DoubleEndedIterator>) -> Result { let mut data = &self.data; + let last = path.nth_back(0).unwrap(); for item in path { - let Some(Value::AttrSet(attrs)) = data.get(&item?) else { - return Ok(false) + let Some(Value::AttrSet(attrs)) = data.get(&item?) else { + return Ok(false); }; data = attrs.as_inner(); } - Ok(true) + Ok(data.get(&last?).is_some()) } pub fn update(&mut self, other: &AttrSet) { diff --git a/src/ty/internal/func.rs b/src/ty/internal/func.rs index fa23524..ff64e1a 100644 --- a/src/ty/internal/func.rs +++ b/src/ty/internal/func.rs @@ -10,10 +10,7 @@ pub struct Func<'gc> { impl<'gc> Func<'gc> { pub fn new(func: &'gc ir::Func, env: Rc) -> Self { - Self { - func, - env, - } + Self { func, env } } } diff --git a/src/ty/internal/list.rs b/src/ty/internal/list.rs index 7f1a81f..50c9c7d 100644 --- a/src/ty/internal/list.rs +++ b/src/ty/internal/list.rs @@ -4,9 +4,9 @@ use std::rc::Weak; use ecow::EcoVec; use hashbrown::HashSet; +use crate::engine::Engine; use crate::env::VmEnv; use crate::ty::public as p; -use crate::engine::Engine; use super::Value; @@ -36,7 +36,9 @@ impl Deref for List { impl List { pub fn new() -> Self { - List { data: EcoVec::new() } + List { + data: EcoVec::new(), + } } pub fn with_capacity(cap: usize) -> Self { diff --git a/src/ty/internal/mod.rs b/src/ty/internal/mod.rs index 55281c7..286da2a 100644 --- a/src/ty/internal/mod.rs +++ b/src/ty/internal/mod.rs @@ -1,7 +1,6 @@ use std::cell::RefCell; use std::hash::Hash; -use std::rc::{Rc, Weak}; -use std::sync::RwLock; +use std::rc::Rc; use derive_more::{IsVariant, Unwrap}; use ecow::EcoString; @@ -9,11 +8,11 @@ use hashbrown::HashSet; use replace_with::replace_with_or_abort; use super::common::*; -use super::public::{self as p, Symbol}; +use super::public as p; -use crate::env::VmEnv; -use crate::error::*; use crate::engine::Engine; +use crate::env::{VmEnv, VmEnvWeak}; +use crate::error::*; mod attrset; mod func; @@ -27,30 +26,14 @@ pub use primop::*; #[derive(Clone)] pub enum EnvRef { - Strong(Rc), - Weak(Weak) + Strong(VmEnv), + Weak(VmEnvWeak), } #[derive(Clone)] pub struct ThunkRef { pub idx: usize, - pub env: Option -} - -pub enum Thunk { - Expr(ThunkRef), - Suspended, - Value(Value) -} - -impl Thunk { - pub fn new(idx: usize) -> Self { - Thunk::Expr(ThunkRef::new(idx)) - } - - pub fn force(&mut self, engine: &Engine) -> Result { - todo!() - } + pub env: Option, } impl ThunkRef { @@ -61,12 +44,19 @@ impl ThunkRef { pub fn capture(&mut self, env: EnvRef) { let _ = self.env.insert(env); } + + pub fn upgrade(&mut self) { + replace_with_or_abort(&mut self.env, |env| { + env.map(|env| EnvRef::Strong(env.upgraded())) + }); + } } impl EnvRef { - pub fn upgrade(&mut self) { - if let EnvRef::Weak(weak) = &*self { - *self = EnvRef::Strong(weak.upgrade().unwrap()) + pub fn upgraded(self) -> VmEnv { + match self { + EnvRef::Weak(weak) => weak.upgrade(), + EnvRef::Strong(strong) => strong, } } } @@ -78,13 +68,13 @@ pub enum Value { Bool(bool), String(EcoString), Null, - Thunk(Rc>), + Thunk(ThunkRef), AttrSet(Rc), List(List), Catchable(EcoString), PrimOp(Rc), PartialPrimOp(Rc), - Func(Rc>), + Func(ThunkRef), } impl Hash for Value { @@ -137,13 +127,13 @@ pub enum ValueAsRef<'v> { Bool(bool), String(&'v EcoString), Null, - Thunk(&'v RwLock), + Thunk(&'v ThunkRef), AttrSet(&'v AttrSet), List(&'v List), Catchable(&'v str), PrimOp(&'v PrimOp), PartialPrimOp(&'v PartialPrimOp), - Func(&'v RwLock), + Func(&'v ThunkRef), } impl Value { @@ -197,7 +187,7 @@ impl Value { } } - pub fn call(&mut self, arg: Self, engine: &Engine) -> Result<()> { + pub fn call(&mut self, arg: Self, engine: &mut Engine) -> Result<()> { use Value::*; if matches!(arg, Value::Catchable(_)) { *self = arg; @@ -206,6 +196,13 @@ impl Value { *self = match self { PrimOp(func) => func.call(arg, engine), PartialPrimOp(func) => func.call(arg, engine), + Func(func) => { + let mut env = func.env.take().unwrap().upgraded().clone(); + env.enter_arg(arg); + let val = engine.eval_thunk(func.idx, &mut env); + env.pop_arg(); + val + } Catchable(_) => return Ok(()), _ => todo!(), }?; @@ -389,10 +386,12 @@ impl Value { } } - pub fn select(&mut self, path: impl DoubleEndedIterator>) -> Result<&mut Self> { + pub fn select( + &mut self, + path: impl DoubleEndedIterator>, + ) -> Result<&mut Self> { let val = match self { - Value::AttrSet(attrs) => attrs - .select(path), + Value::AttrSet(attrs) => attrs.select(path), Value::Catchable(_) => return Ok(self), _ => Err(Error::EvalError(format!( "cannot select from {:?}", @@ -403,7 +402,11 @@ impl Value { Ok(self) } - pub fn select_with_default(&mut self, path: impl DoubleEndedIterator>, default: Self) -> Result<&mut Self> { + pub fn select_with_default( + &mut self, + path: impl DoubleEndedIterator>, + default: Self, + ) -> Result<&mut Self> { let val = match self { Value::AttrSet(attrs) => attrs.select(path).unwrap_or(default), Value::Catchable(_) => return Ok(self), @@ -418,7 +421,7 @@ impl Value { Ok(self) } - pub fn has_attr(&mut self, path: impl IntoIterator>) -> Result<()> { + pub fn has_attr(&mut self, path: impl DoubleEndedIterator>) -> Result<()> { if let Value::AttrSet(attrs) = self { let val = Value::Bool(attrs.has_attr(path)?); *self = val; @@ -438,13 +441,16 @@ impl Value { self } - pub fn force(&mut self, engine: &Engine) -> Result<&mut Self> { - if let Value::Thunk(thunk) = &*self { - let val = { - let mut thunk = thunk.write().unwrap(); - thunk.force(engine) - }?; - *self = val; + pub fn force(&mut self, engine: &mut Engine) -> Result<&mut Self> { + if let Value::Thunk(thunk) = self { + unsafe { + let old = std::ptr::read(thunk); + let mut env = old.env.unwrap().upgraded(); + std::ptr::write( + self, + engine.eval_thunk(old.idx, &mut env)?, + ); + } } Ok(self) } @@ -477,4 +483,3 @@ impl Value { } } } - diff --git a/src/ty/internal/primop.rs b/src/ty/internal/primop.rs index 8337d30..d1dbad3 100644 --- a/src/ty/internal/primop.rs +++ b/src/ty/internal/primop.rs @@ -2,8 +2,8 @@ use std::rc::Rc; use derive_more::Constructor; -use crate::error::Result; use crate::engine::Engine; +use crate::error::Result; use super::Value; diff --git a/src/ty/public.rs b/src/ty/public.rs index 3111c0b..0e60a4e 100644 --- a/src/ty/public.rs +++ b/src/ty/public.rs @@ -1,7 +1,7 @@ +use std::collections::BTreeMap; use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::ops::Deref; use std::sync::LazyLock; -use std::collections::BTreeMap; use derive_more::{Constructor, IsVariant, Unwrap}; use ecow::EcoString;