diff --git a/flake.nix b/flake.nix index 18f6f42..8b0a5ab 100644 --- a/flake.nix +++ b/flake.nix @@ -28,6 +28,8 @@ libffi libxml2 ncurses + gdb + valgrind ]; LLVM_SYS_181_PREFIX = toString pkgs.llvm_18.dev; LD_LIBRARY_PATH = let diff --git a/src/bin/eval.rs b/src/bin/eval.rs index 4bae80b..ea025c7 100644 --- a/src/bin/eval.rs +++ b/src/bin/eval.rs @@ -22,7 +22,7 @@ fn main() -> Result<()> { )); } let expr = root.tree().expr().unwrap(); - let (downgraded, _) = downgrade(expr)?; + let downgraded = downgrade(expr)?; println!("{}", eval(downgraded)?); Ok(()) diff --git a/src/bin/repl.rs b/src/bin/repl.rs index 5f8db05..124feef 100644 --- a/src/bin/repl.rs +++ b/src/bin/repl.rs @@ -2,10 +2,9 @@ use itertools::Itertools; use rustyline::error::ReadlineError; use rustyline::{DefaultEditor, Result}; -use nixjit::compile::compile; -use nixjit::error::Error; use nixjit::ir::downgrade; -use nixjit::vm::run; +use nixjit::error::Error; +use nixjit::engine::eval; macro_rules! unwrap { ($e:expr) => { @@ -41,8 +40,7 @@ fn main() -> Result<()> { } let expr = root.tree().expr().unwrap(); let downgraded = unwrap!(downgrade(expr)); - let prog = compile(downgraded); - println!("{}", unwrap!(run(prog))); + println!("{}", unwrap!(eval(downgraded))); } Err(ReadlineError::Interrupted) => { println!("CTRL-C"); diff --git a/src/bin/scc.rs b/src/bin/scc.rs index 1f219bb..c617e0e 100644 --- a/src/bin/scc.rs +++ b/src/bin/scc.rs @@ -38,8 +38,8 @@ fn main() -> Result<()> { continue; } let expr = root.tree().expr().unwrap(); - let (_, graph) = unwrap!(downgrade(expr)); - println!("{:?}", graph); + let downgraded = unwrap!(downgrade(expr)); + println!("{:?}", downgraded.graph); } Err(ReadlineError::Interrupted) => { println!("CTRL-C"); diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index 3b17c92..3c45749 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -3,7 +3,7 @@ use hashbrown::HashMap; use crate::ir::{Const, DowngradeContext, Ir}; -pub fn ir_env(ctx: &mut DowngradeContext) -> HashMap { +pub fn ir_env(_: &mut DowngradeContext) -> HashMap { let mut map = HashMap::new(); map.insert("true".into(), Const::from(true).ir()); map.insert("false".into(), Const::from(false).ir()); diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 60748ff..ad5e744 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -1,14 +1,13 @@ 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::internal as i; +use crate::ir::{Dep, Downgraded, Ir, SccNode}; +use crate::ty::internal::{self as i, ThunkRef}; use crate::ty::public::Value; #[cfg(test)] @@ -18,40 +17,63 @@ type ThunkIdx = usize; type EnvIdx = usize; pub struct Engine { - thunks: Box<[Ir]>, + pub thunks: Box<[Ir]>, pub func_offset: usize, + pub func_deps: Vec>, 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() - .map(|mut val| { - Ok(val - .force(&mut engine)? - .to_public(&engine, &mut HashSet::new())) - })? + let mut engine = Engine::new(downgraded.thunks, downgraded.func_offset, downgraded.func_deps); + engine.eval(downgraded.graph) } impl Engine { - pub fn new(thunks: Box<[Ir]>, func_offset: usize) -> Self { + pub fn new(thunks: Box<[Ir]>, func_offset: usize, func_deps: Vec>) -> Self { Self { - lru: LruCache::new(thunks.len().try_into().unwrap()), + tasks: PriorityQueue::new(), thunks, func_offset, - tasks: PriorityQueue::new(), + func_deps } } - pub fn eval(&mut self) -> Result { - self.thunks.last().unwrap().clone().eval(self, &mut VmEnv::new()) + pub fn eval(&mut self, graph: Vec) -> Result { + let mut env = VmEnv::new(); + let last = graph.last().unwrap().members[0]; + for SccNode { members, .. } in graph.into_iter() { + // TODO: + assert!(members.len() == 1); + for member in members.into_iter() { + let val = self.thunks[member].clone().eval(self, &mut env)?; + env.insert_cache(member, val); + } + } + env.lookup_cache(last, |_| unreachable!()) + .map(|mut val| Ok(val.force(self, &mut env)?.to_public(self, &mut HashSet::new())))? } pub fn eval_thunk(&mut self, idx: usize, env: &mut VmEnv) -> Result { self.thunks[idx].clone().eval(self, env) } + + pub fn eval_func_deps(&mut self, idx: usize, env: &mut VmEnv) -> Result<()> { + for dep in self.func_deps[idx - self.func_offset].clone() { + match dep { + Dep::Arg(idx) => { + if let i::Value::Thunk(ThunkRef { idx }) = env.lookup_arg(idx) { + let val = self.thunks[idx].clone().eval(self, env)?; + env.insert_cache(idx, val) + } + }, + Dep::Thunk(idx) =>{ + let val = self.thunks[idx].clone().eval(self, env)?; + env.insert_cache(idx, val) + } + } + } + Ok(()) + } } enum Thunk { diff --git a/src/engine/test.rs b/src/engine/test.rs index 24e06f8..198fdff 100644 --- a/src/engine/test.rs +++ b/src/engine/test.rs @@ -16,7 +16,9 @@ use super::eval; #[inline] fn test_expr(expr: &str, expected: Value) { - let (downgraded, _) = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap(); + println!("{expr}"); + let downgraded = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap(); + println!("{downgraded:?}"); assert_eq!(eval(downgraded).unwrap(), expected); } @@ -148,6 +150,13 @@ fn test_attrs() { symbol!("a") => int!(1) }, ); + test_expr( + "rec { a = 1; b = a; }", + attrs! { + symbol!("a") => int!(1), + symbol!("b") => thunk!() + }, + ); test_expr("{ a = 1; }.a", int!(1)); test_expr("{ a = 1; }.b or 1", int!(1)); test_expr( @@ -193,7 +202,7 @@ fn test_let() { 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)); + test_expr("(x: y: x / y) 1 2", int!(0)); test_expr("({ x, y }: x + y) { x = 1; y = 2; }", int!(3)); test_expr("({ x, y, ... }: x + y) { x = 1; y = 2; z = 3; }", int!(3)); test_expr( diff --git a/src/env.rs b/src/env.rs index 131efb2..1465911 100644 --- a/src/env.rs +++ b/src/env.rs @@ -6,14 +6,14 @@ use hashbrown::HashMap; use crate::stack::Stack; use crate::ty::internal::{EnvRef, Value}; +use crate::error::{Error, Result}; #[derive(Clone)] pub struct VmEnv { - let_: Rc, 1000>>>, - with: Rc, - args: Rc>>, - new: bool, - pub id: usize, + cache: Vec>, + with: Vec>>, + args: Vec, + pub args_len: usize, } #[derive(Clone)] @@ -41,104 +41,71 @@ pub enum Type { impl VmEnv { pub fn new() -> Self { Self { - let_: Rc::default(), - with: With { - map: None, - last: None, - } - .into(), - args: Rc::default(), - new: false, - id: 0, + cache: Vec::from([HashMap::new()]), + with: Vec::new(), + args: Vec::new(), + args_len: 0, } } - pub fn lookup_let(&self, level: usize, idx: usize) -> Value { - self.let_.borrow()[level][idx].clone() + pub fn enter_cache_level(&mut self, f: impl FnOnce(&mut Self) -> T) -> T { + self.cache.push(HashMap::new()); + let ret = f(self); + self.cache.pop(); + ret + } + + pub fn pop_cache_level(&mut self) { + self.cache.pop(); + } + + pub fn insert_cache(&mut self, idx: usize, val: Value) { + self.cache.last_mut().unwrap().insert(idx, val); + } + + pub fn lookup_cache(&mut self, idx: usize, f: impl FnOnce(&mut VmEnv) -> Result) -> Result { + for level in self.cache.iter().rev() { + if let Some(ret) = level.get(&idx) { + return ret.clone().ok(); + } + } + let val = f(self)?; + self.cache.last_mut().unwrap().insert(idx, val.clone()); + val.ok() } pub fn lookup_arg(&self, level: usize) -> Value { - self.args.borrow()[level].clone() + self.args[self.args.len() - level - 1].clone() } pub fn lookup_with(&self, symbol: &EcoString) -> Option { - self.with.lookup(symbol) - } - - pub fn has_with(&self) -> bool { - self.with.map.is_some() - } - - 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); + for with in self.with.iter().rev() { + if let Some(ret) = with.get(symbol) { + return Some(ret.clone()); + } } + None } - 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 enter_arg(&mut self, arg: Value) { + self.args.push(arg) } - 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_args(&mut self, len: usize) -> Vec { + self.args.split_off(self.args.len() - len) } - 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(); + pub fn enter_args(&mut self, args: Vec) { + self.args.extend(args); } - #[must_use] - 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 pop_with(&mut self) { + self.with.pop(); } - 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, - } + pub fn enter_with(&mut self, map: Rc>) { + self.with.push(map) } } @@ -157,15 +124,3 @@ impl With { }) } } - -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 3c766bf..ba5d5a6 100644 --- a/src/eval/jit/compile.rs +++ b/src/eval/jit/compile.rs @@ -1,4 +1,4 @@ -use inkwell::values::{StructValue, FunctionValue}; +use inkwell::values::{FunctionValue, StructValue}; use crate::ir::*; use crate::ty::common as c; @@ -41,11 +41,18 @@ impl JITCompile for UnOp { let tag = ctx.get_tag(rhs); let fallback = ctx.context.append_basic_block(func, "fallback"); let ret = ctx.context.append_basic_block(func, "fallback"); - let res = ctx.builder.build_alloca(ctx.helpers.value_type, "res_alloca").unwrap(); + let res = ctx + .builder + .build_alloca(ctx.helpers.value_type, "res_alloca") + .unwrap(); ctx.builder.build_switch(tag, fallback, &[]).unwrap(); ctx.builder.position_at_end(fallback); ctx.builder.position_at_end(ret); - ctx.builder.build_load(ctx.helpers.value_type, res, "load_res").unwrap().try_into().unwrap() + ctx.builder + .build_load(ctx.helpers.value_type, res, "load_res") + .unwrap() + .try_into() + .unwrap() } } diff --git a/src/eval/jit/helpers.rs b/src/eval/jit/helpers.rs index a4130a0..f7fb59c 100644 --- a/src/eval/jit/helpers.rs +++ b/src/eval/jit/helpers.rs @@ -184,7 +184,7 @@ impl<'ctx> Helpers<'ctx> { self.value_type .const_named_struct(&[ self.int_type.const_int(ValueTag::Int as _, false).into(), - self.const_int(int).into() + self.const_int(int).into(), ]) .into() } @@ -197,7 +197,7 @@ impl<'ctx> Helpers<'ctx> { self.value_type .const_named_struct(&[ self.int_type.const_int(ValueTag::Float as _, false).into(), - self.const_float(float).into() + self.const_float(float).into(), ]) .into() } diff --git a/src/eval/jit/mod.rs b/src/eval/jit/mod.rs index 35d5364..bd198f8 100644 --- a/src/eval/jit/mod.rs +++ b/src/eval/jit/mod.rs @@ -6,7 +6,9 @@ use inkwell::builder::Builder; use inkwell::context::Context; use inkwell::execution_engine::ExecutionEngine; use inkwell::module::Module; -use inkwell::values::{AnyValue, AsValueRef, BasicMetadataValueEnum, BasicValueEnum, IntValue, StructValue}; +use inkwell::values::{ + AnyValue, AsValueRef, BasicMetadataValueEnum, BasicValueEnum, IntValue, StructValue, +}; use crate::env::VmEnv; use crate::ir::{Ir, UnOpKind}; @@ -144,21 +146,25 @@ impl<'ctx> JITContext<'ctx> { pub fn get_tag(&self, val: StructValue<'ctx>) -> IntValue<'ctx> { let alloca = self - .builder - .build_alloca(self.helpers.value_type, "get_tag_alloca").unwrap(); + .builder + .build_alloca(self.helpers.value_type, "get_tag_alloca") + .unwrap(); self.builder.build_store(alloca, val).unwrap(); self.builder .build_load( self.context.bool_type(), self.builder - .build_struct_gep(self.helpers.value_type, alloca, 1, "get_tag_gep").unwrap(), + .build_struct_gep(self.helpers.value_type, alloca, 1, "get_tag_gep") + .unwrap(), "get_tag", - ).unwrap() + ) + .unwrap() .into_int_value() } pub fn call(&self, args: &[BasicMetadataValueEnum<'ctx>]) -> StructValue<'ctx> { - self.builder.build_call(self.helpers.call, args, "call") + self.builder + .build_call(self.helpers.call, args, "call") .unwrap() .as_any_value_enum() .into_struct_value() diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 4479ba2..0cef6cf 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -3,7 +3,8 @@ use ecow::EcoVec; use crate::engine::Engine; use crate::env::VmEnv; use crate::error::{Error, Result}; -use crate::ir::{self, DynAttr}; use crate::ty::common::Const; +use crate::ir::{self, DynAttr}; +use crate::ty::common::Const; use crate::ty::internal::{AttrSet, EnvRef, List, ThunkRef, Value}; use crate::ty::public::Symbol; @@ -18,20 +19,12 @@ impl Evaluate for ir::Attrs { let mut attrs = AttrSet::new( self.stcs .into_iter() - .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 - })) - }) + .map(|(k, v)| Ok((k, v.eval(engine, env)?))) .collect::>()?, ); for DynAttr(k, v) in self.dyns { let mut k = k.eval(engine, env)?; - k.force(engine)?.coerce_to_string(); + k.force(engine, env)?.coerce_to_string(); attrs.push_attr(k.unwrap_string(), v.eval(engine, env)?); } Value::AttrSet(attrs.into()).ok() @@ -43,13 +36,7 @@ impl Evaluate for ir::List { Value::List(List::from( self.items .into_iter() - .map(|val| { - let mut val = val.eval(engine, env)?; - if let Value::Thunk(thunk) = &mut val { - thunk.capture(EnvRef::Strong(env.clone())); - } - val.ok() - }) + .map(|val| val.eval(engine, env)) .collect::>>()?, )) .ok() @@ -66,7 +53,7 @@ impl Evaluate for ir::HasAttr { Strs(expr) => expr.eval(engine, env)?.unwrap_string(), Dynamic(expr) => { let mut val = expr.eval(engine, env)?; - val.force(engine)?.coerce_to_string(); + val.force(engine, env)?.coerce_to_string(); val.unwrap_string() } }) @@ -80,8 +67,8 @@ impl Evaluate for ir::BinOp { 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)?; + lhs.force(engine, env)?; + rhs.force(engine, env)?; match self.kind { Add => lhs.add(rhs), Sub => { @@ -117,9 +104,9 @@ impl Evaluate for ir::BinOp { } Con => lhs.concat(rhs), Upd => lhs.update(rhs), - PipeL => lhs.call(rhs, engine)?, + PipeL => lhs.call(vec![rhs], engine, env)?, PipeR => { - rhs.call(lhs, engine)?; + rhs.call(vec![lhs], engine, env)?; lhs = rhs; } } @@ -131,7 +118,7 @@ impl Evaluate for ir::UnOp { fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { use ir::UnOpKind::*; let mut rhs = self.rhs.eval(engine, env)?; - rhs.force(engine)?; + rhs.force(engine, env)?; match self.kind { Neg => rhs.neg(), Not => rhs.not(), @@ -146,14 +133,14 @@ impl Evaluate for ir::Select { let mut val = self.expr.eval(engine, env)?; if let Some(default) = self.default { let default = default.eval(engine, env)?; - val.force(engine)?.select_with_default( + val.force(engine, env)?.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.force(engine)?.coerce_to_string(); + val.force(engine, env)?.coerce_to_string(); val.unwrap_string() } }) @@ -161,14 +148,14 @@ impl Evaluate for ir::Select { default, )?; } else { - val.force(engine)? + val.force(engine, env)? .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.force(engine, env)?.coerce_to_string(); val.unwrap_string() } }) @@ -192,10 +179,8 @@ impl Evaluate for ir::If { impl Evaluate for ir::LoadFunc { 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(), - }) + let idx = engine.func_offset + self.idx; + Value::Func(idx) .ok() } } @@ -203,26 +188,19 @@ impl Evaluate for ir::LoadFunc { impl Evaluate for ir::Call { fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { let mut func = self.func.eval(engine, env)?; - func.force(engine)?; + func.force(engine, env)?; + func.call(self.args.into_iter().map(|arg| arg.eval(engine, env)).collect::>()?, engine, env)?; // FIXME: Modify Value::call - for arg in self.args { - func.call(arg.eval(engine, env)?, engine)?; - } + // for arg in self.args { + // func.call(arg.eval(engine, env)?, engine, env)?; + // } func.ok() } } impl Evaluate for ir::Let { 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) + unreachable!() } } @@ -230,10 +208,10 @@ impl Evaluate for ir::With { fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { let namespace = self.namespace.eval(engine, env)?; // TODO: Error Handling - self.expr.eval( - engine, - &mut env.enter_with(namespace.unwrap_attr_set().into_inner()), - ) + env.enter_with(namespace.unwrap_attr_set().into_inner()); + let ret = self.expr.eval(engine, env); + env.pop_with(); + ret } } @@ -250,7 +228,7 @@ impl Evaluate for ir::ConcatStrings { .into_iter() .map(|part| { let mut part = part.eval(engine, env)?; - part.force(engine)?.coerce_to_string(); + part.force(engine, env)?.coerce_to_string(); part.ok() }) .collect::>>()? @@ -286,7 +264,7 @@ impl Evaluate for ir::Const { impl Evaluate for ir::Var { fn eval(self, _: &mut Engine, env: &mut VmEnv) -> Result { env.lookup_with(&self.sym) - .ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(self.sym)))) + .ok_or_else(|| Error::EvalError(format!("variable {} not found", Symbol::from(self.sym)))) } } @@ -298,11 +276,7 @@ impl Evaluate for ir::Arg { impl Evaluate for ir::LetVar { 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() + unreachable!() } } @@ -316,7 +290,8 @@ impl Evaluate for ir::MaybeThunk { fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result { match self { ir::MaybeThunk::Const(cnst) => cnst.eval(engine, env), - ir::MaybeThunk::Thunk(thunk) => thunk.eval(engine, env) + ir::MaybeThunk::String(string) => string.eval(engine, env), + ir::MaybeThunk::Thunk(thunk) => thunk.eval(engine, env), } } } diff --git a/src/ir/ctx.rs b/src/ir/ctx.rs index f9e5b2c..3126113 100644 --- a/src/ir/ctx.rs +++ b/src/ir/ctx.rs @@ -1,21 +1,28 @@ +use derive_more::Unwrap; use ecow::EcoString; use hashbrown::{HashMap, HashSet}; -use crate::error::Result; +use crate::{error::{Error, Result}, ty::common::Const}; -use super::{Func, Ir, LoadFunc, MaybeThunk, Thunk}; +use super::{Dep, Func, Ir, LoadFunc, MaybeThunk, SccAnalyzer, SccNode, Thunk}; + +#[derive(Clone, Copy, Unwrap)] +pub enum Index { + Thunk(usize), + Func(usize), +} pub struct DowngradeContext { - pub thunks: Vec, - pub deps: Vec>, + pub thunks: Vec<(Ir, bool)>, + pub thunk_deps: Vec>, + pub func_deps: Vec>, + pub func_arg_dep: Vec, pub funcs: Vec, } pub struct Env<'a, 'env> { env: EnvNode<'a>, prev: Option<&'env Env<'a, 'env>>, - arg_level: usize, - let_level: usize, } enum EnvNode<'a> { @@ -40,8 +47,6 @@ impl<'a, 'env> Env<'a, 'env> { Self { env: EnvNode::Builtins(base), prev: None, - arg_level: 0, - let_level: 0, } } @@ -49,8 +54,6 @@ 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, } } @@ -58,8 +61,6 @@ 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, } } @@ -71,8 +72,6 @@ impl<'a, 'env> Env<'a, 'env> { Self { env: EnvNode::MultiArg(map, alias), prev: Some(self), - arg_level: self.arg_level + 1, - let_level: 0, } } @@ -80,8 +79,6 @@ impl<'a, 'env> Env<'a, 'env> { Self { env: EnvNode::With, prev: Some(self), - arg_level: self.arg_level, - let_level: self.let_level, } } @@ -89,7 +86,6 @@ impl<'a, 'env> Env<'a, 'env> { &self, ident: &EcoString, mut arg_level: usize, - mut let_level: usize, has_with: bool, ) -> core::result::Result { use EnvNode::*; @@ -106,47 +102,41 @@ impl<'a, 'env> Env<'a, 'env> { } Let(map) => { if let Ok(idx) = map.binary_search_by(|(k, _)| k.cmp(ident)) { - return Ok(LookupResult::MaybeThunk(map[idx].1)) - /* return Ok(LookupResult::Let { - level: let_level - 1, - idx, - }); */ - } else { - let_level -= 1; + return Ok(LookupResult::MaybeThunk(map[idx].1.clone())); } } SingleArg(arg) => { if arg == ident { return Ok(LookupResult::SingleArg { - level: arg_level - 1, + level: arg_level, }); } else { - arg_level -= 1; + arg_level += 1; } } MultiArg(set, alias) => { if let Some(default) = set.get(ident) { return Ok(LookupResult::MultiArg { - level: arg_level - 1, + level: arg_level, default: default.clone(), }); } else if alias.as_ref() == Some(ident) { return Ok(LookupResult::SingleArg { - level: arg_level - 1, + level: arg_level, }); } else { - arg_level -= 1; + arg_level += 1; } } With => has_with = true, } self.prev - .map(|prev| prev._lookup(ident, arg_level, let_level, has_with)) + .map(|prev| prev._lookup(ident, arg_level, has_with)) .map_or_else(|| unreachable!(), |x| x) } pub fn lookup(&self, ident: &EcoString) -> core::result::Result { - self._lookup(ident, self.arg_level, self.let_level, false) + self._lookup(ident, 0, false) } } @@ -154,7 +144,9 @@ impl DowngradeContext { pub fn new() -> Self { DowngradeContext { thunks: Vec::new(), - deps: Vec::new(), + thunk_deps: Vec::new(), + func_deps: Vec::new(), + func_arg_dep: Vec::new(), funcs: Vec::new(), } } @@ -163,51 +155,79 @@ impl DowngradeContext { impl DowngradeContext { pub fn new_thunk(&mut self, thunk: Ir) -> Thunk { let idx = self.thunks.len(); - self.thunks.push(thunk); - self.deps.push(HashSet::new()); + self.thunks.push((thunk, false)); + self.thunk_deps.push(HashSet::new()); Thunk { idx } } pub fn maybe_thunk(&mut self, ir: Ir) -> MaybeThunk { match ir { Ir::Const(cnst) => MaybeThunk::Const(cnst), + Ir::String(string) => MaybeThunk::String(string), Ir::Thunk(thunk) => MaybeThunk::Thunk(thunk), - ir => MaybeThunk::Thunk(self.new_thunk(ir)) + ir => MaybeThunk::Thunk(self.new_thunk(ir)), } } - pub fn new_dep(&mut self, this: usize, dep: usize) { - self.deps[this].insert(dep); + pub fn new_dep(&mut self, this: Index, dep: Dep) -> Result<()> { + match this { + Index::Thunk(idx) => { + if dep == Dep::Thunk(idx) { + return Err(Error::DowngradeError("infinite recursion encountered".into())) + } + self.thunk_deps[idx].insert(dep.unwrap_thunk()) + }, + Index::Func(idx) => self.func_deps[idx].insert(dep), + }; + Ok(()) } pub fn new_func(&mut self, func: Func) -> LoadFunc { let idx = self.funcs.len(); self.funcs.push(func); + self.func_deps.push(HashSet::new()); + self.func_arg_dep.push(false); LoadFunc { idx } } - pub fn resolve_func(&mut self, thunk_idx: usize, func_idx: usize, env: &Env) -> Result<()> { + pub fn resolve_func(&mut self, idx: usize, env: &Env) -> Result<()> { let self_ptr = self as *mut Self; - self.funcs.get_mut(func_idx).map_or_else( + self.funcs.get_mut(idx).map_or_else( || unreachable!(), |func| { unsafe { let old = std::ptr::read(func); - std::ptr::write(func, old.resolve(thunk_idx, Some(func_idx), self_ptr.as_mut().unwrap(), env)?); + match old.resolve(Index::Func(idx), self_ptr.as_mut().unwrap(), env) { + Ok(ok) => std::ptr::write(func, ok), + Err(err) => { + std::ptr::write(func, Func { param: crate::ir::Param::Ident(EcoString::new()), body: super::Const { val: Const::Null }.ir().boxed() }); + return Err(err) + }, + } } Ok(()) }, ) } - pub fn resolve_thunk(&mut self, thunk_idx: usize, func_idx: Option, env: &Env) -> Result<()> { + pub fn resolve_thunk(&mut self, idx: usize, env: &Env) -> Result<()> { + if self.thunks[idx].1 { + return Ok(()) + } + self.thunks[idx].1 = true; let self_ptr = self as *mut Self; - self.thunks.get_mut(thunk_idx).map_or_else( + self.thunks.get_mut(idx).map_or_else( || unreachable!(), |thunk| { unsafe { - let old = std::ptr::read(thunk); - std::ptr::write(thunk, old.resolve(thunk_idx, func_idx, self_ptr.as_mut().unwrap(), env)?); + let (old, _) = std::ptr::read(thunk); + match old.resolve(Index::Thunk(idx), self_ptr.as_mut().unwrap(), env) { + Ok(ok) => std::ptr::write(&mut thunk.0, ok), + Err(err) => { + std::ptr::write(&mut thunk.0, Ir::Const(super::Const { val: Const::Null })); + return Err(err) + }, + } } Ok(()) }, @@ -215,18 +235,24 @@ impl DowngradeContext { } } +#[derive(Debug)] pub struct Downgraded { pub thunks: Box<[Ir]>, pub func_offset: usize, + pub func_deps: Vec>, + pub graph: Vec, } impl Downgraded { pub fn new(ctx: DowngradeContext) -> Self { Self { + graph: SccAnalyzer::new(&ctx).analyze(), func_offset: ctx.thunks.len(), + func_deps: ctx.func_deps, thunks: ctx .thunks .into_iter() + .map(|(ir, _)| ir) .chain(ctx.funcs.into_iter().map(|Func { body, .. }| *body)) .collect(), } diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 4385414..377a81b 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -1,6 +1,6 @@ use derive_more::{IsVariant, TryUnwrap, Unwrap}; use ecow::EcoString; -use hashbrown::{HashMap, HashSet}; +use hashbrown::HashMap; use inkwell::values::{FunctionValue, StructValue}; use itertools::Itertools; use rnix::ast::HasEntry; @@ -25,15 +25,14 @@ use utils::*; pub use ctx::{DowngradeContext, Downgraded}; pub use scc::*; -pub fn downgrade(expr: Expr) -> Result<(Downgraded, Vec)> { +pub fn downgrade(expr: Expr) -> Result { let mut ctx = DowngradeContext::new(); let builtins = ir_env(&mut ctx); let env = Env::new(&builtins); let top_level = expr.downgrade(&mut ctx)?; let Thunk { idx } = ctx.new_thunk(top_level); - ctx.resolve_thunk(idx, None, &env)?; - let scc = SccAnalyzer::new(&ctx).analyze(); - Ok((Downgraded::new(ctx), scc)) + ctx.resolve_thunk(idx, &env)?; + Ok(Downgraded::new(ctx)) } macro_rules! ir { @@ -83,9 +82,9 @@ macro_rules! ir { } #[inline] - fn resolve<'a, 'env>(self, self_idx: usize, func_idx: Option, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { + fn resolve<'a, 'env>(self, self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { match self { - $(Ir::$ty(ir) => ir.resolve(self_idx, func_idx, ctx, env),)* + $(Ir::$ty(ir) => ir.resolve(self_idx, ctx, env),)* } } } @@ -166,23 +165,24 @@ impl Ir { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub enum MaybeThunk { Const(Const), - Thunk(Thunk) + String(String), + Thunk(Thunk), } impl MaybeThunk { fn resolve<'a, 'env>( self, - self_idx: usize, - func_idx: Option, + self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { match self { - MaybeThunk::Thunk(thunk) => thunk.resolve(self_idx, func_idx, ctx, env), - MaybeThunk::Const(cnst) => cnst.ir().ok() + MaybeThunk::Thunk(thunk) => thunk.resolve(self_idx, ctx, env), + MaybeThunk::Const(cnst) => cnst.ir().ok(), + MaybeThunk::String(string) => string.ir().ok(), } } } @@ -199,7 +199,7 @@ pub struct DynAttr(pub Ir, pub Ir); impl Attrs { fn _insert( &mut self, - mut path: std::vec::IntoIter, + mut path: impl Iterator, name: Attr, value: Ir, ctx: &mut DowngradeContext, @@ -299,7 +299,10 @@ impl Attrs { .get(ident) .and_then(|attrs| attrs.as_ref().try_unwrap_attrs().ok()) .ok_or_else(|| { - Error::DowngradeError(format!("{} not found", Symbol::from(ident.clone()))) + Error::DowngradeError(format!( + "attribute {} not found", + Symbol::from(ident.clone()) + )) })? ._select(path, name), None => match name { @@ -313,7 +316,7 @@ impl Attrs { Ok(None) } else { Err(Error::DowngradeError(format!( - "{} not found", + "attribute {} not found", Symbol::from(ident.clone()) ))) } @@ -343,14 +346,13 @@ pub enum Attr { impl Attr { fn resolve<'a, 'env>( self, - self_idx: usize, - func_idx: Option, + self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { use Attr::*; Ok(match self { - Dynamic(ir) => Dynamic(ir.resolve(self_idx, func_idx, ctx, env)?), + Dynamic(ir) => Dynamic(ir.resolve(self_idx, ctx, env)?), other => other, }) } @@ -359,13 +361,12 @@ impl Attr { impl Thunk { fn resolve<'a, 'env>( self, - self_idx: usize, - func_idx: Option, + self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { - ctx.new_dep(self_idx, self.idx); - ctx.resolve_thunk(self.idx, func_idx, env)?; + ctx.new_dep(self_idx, Dep::Thunk(self.idx))?; + ctx.resolve_thunk(self.idx, env)?; self.ir().ok() } } @@ -452,12 +453,11 @@ pub enum Param { impl LoadFunc { fn resolve<'a, 'env>( self, - self_idx: usize, - _: Option, + _: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { - ctx.resolve_func(self_idx, self.idx, env)?; + ctx.resolve_func(self.idx, env)?; self.ir().ok() } } @@ -510,17 +510,13 @@ impl Downgrade for ast::Assert { impl Assert { fn resolve<'a, 'env>( self, - self_idx: usize, - func_idx: Option, + self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { Self { - assertion: self - .assertion - .resolve(self_idx, func_idx, ctx, env)? - .boxed(), - expr: self.expr.resolve(self_idx, func_idx, ctx, env)?.boxed(), + assertion: self.assertion.resolve(self_idx, ctx, env)?.boxed(), + expr: self.expr.resolve(self_idx, ctx, env)?.boxed(), } .ir() .ok() @@ -542,15 +538,14 @@ impl Downgrade for ast::IfElse { impl If { fn resolve<'a, 'env>( self, - self_idx: usize, - func_idx: Option, + self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { If { - cond: self.cond.resolve(self_idx, func_idx, ctx, env)?.boxed(), - consq: self.consq.resolve(self_idx, func_idx, ctx, env)?.boxed(), - alter: self.alter.resolve(self_idx, func_idx, ctx, env)?.boxed(), + cond: self.cond.resolve(self_idx, ctx, env)?.boxed(), + consq: self.consq.resolve(self_idx, ctx, env)?.boxed(), + alter: self.alter.resolve(self_idx, ctx, env)?.boxed(), } .ir() .ok() @@ -589,13 +584,12 @@ impl Downgrade for ast::Path { impl Path { fn resolve<'a, 'env>( self, - self_idx: usize, - func_idx: Option, + self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { Self { - expr: self.expr.resolve(self_idx, func_idx, ctx, env)?.boxed(), + expr: self.expr.resolve(self_idx, ctx, env)?.boxed(), } .ir() .ok() @@ -625,8 +619,7 @@ impl Downgrade for ast::Str { impl ConcatStrings { fn resolve<'a, 'env>( self, - self_idx: usize, - func_idx: Option, + self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { @@ -634,7 +627,7 @@ impl ConcatStrings { parts: self .parts .into_iter() - .map(|ir| ir.resolve(self_idx, func_idx, ctx, env)) + .map(|ir| ir.resolve(self_idx, ctx, env)) .collect::>>()?, } .ir() @@ -659,8 +652,7 @@ impl Downgrade for ast::Literal { impl Const { fn resolve<'a, 'env>( self, - _: usize, - _: Option, + _: Index, _: &mut DowngradeContext, _: &Env<'a, 'env>, ) -> Result { @@ -671,8 +663,7 @@ impl Const { impl String { fn resolve<'a, 'env>( self, - _: usize, - _: Option, + _: Index, _: &mut DowngradeContext, _: &Env<'a, 'env>, ) -> Result { @@ -690,15 +681,14 @@ impl Downgrade for ast::Ident { impl Var { fn resolve<'a, 'env>( self, - self_idx: usize, - func_idx: Option, + self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { use LookupResult::*; let Ok(res) = env.lookup(&self.sym) else { return Err(Error::DowngradeError(format!( - "{} not found", + "variable {} not found", Symbol::from(self.sym) ))); }; @@ -712,7 +702,7 @@ impl Var { default: default.map(Box::new), } .ir(), - MaybeThunk(thunk) => thunk.resolve(self_idx, func_idx, ctx, env)?, + MaybeThunk(thunk) => thunk.resolve(self_idx, ctx, env)?, With => self.ir(), } .ok() @@ -722,8 +712,7 @@ impl Var { impl Arg { fn resolve<'a, 'env>( self, - _: usize, - _: Option, + _: Index, _: &mut DowngradeContext, _: &Env<'a, 'env>, ) -> Result { @@ -734,8 +723,7 @@ impl Arg { impl LetVar { fn resolve<'a, 'env>( self, - _: usize, - _: Option, + _: Index, _: &mut DowngradeContext, _: &Env<'a, 'env>, ) -> Result { @@ -773,8 +761,7 @@ impl Downgrade for ast::AttrSet { impl Attrs { fn resolve<'a, 'env>( self, - self_idx: usize, - func_idx: Option, + self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { @@ -782,15 +769,15 @@ impl Attrs { stcs: self .stcs .into_iter() - .map(|(k, v)| Ok((k, v.resolve(self_idx, func_idx, ctx, env)?))) + .map(|(k, v)| Ok((k, v.resolve(self_idx, ctx, env)?))) .collect::>()?, dyns: self .dyns .into_iter() .map(|DynAttr(k, v)| { Ok(DynAttr( - k.resolve(self_idx, func_idx, ctx, env)?, - v.resolve(self_idx, func_idx, ctx, env)?, + k.resolve(self_idx, ctx, env)?, + v.resolve(self_idx, ctx, env)?, )) }) .collect::>()?, @@ -813,8 +800,7 @@ impl Downgrade for ast::List { impl List { fn resolve<'a, 'env>( self, - self_idx: usize, - func_idx: Option, + self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { @@ -822,7 +808,7 @@ impl List { items: self .items .into_iter() - .map(|item| item.resolve(self_idx, func_idx, ctx, env)) + .map(|item| item.resolve(self_idx, ctx, env)) .collect::>()?, } .ir() @@ -845,14 +831,13 @@ impl Downgrade for ast::BinOp { impl BinOp { fn resolve<'a, 'env>( self, - self_idx: usize, - func_idx: Option, + self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { Self { - lhs: self.lhs.resolve(self_idx, func_idx, ctx, env)?.boxed(), - rhs: self.rhs.resolve(self_idx, func_idx, ctx, env)?.boxed(), + lhs: self.lhs.resolve(self_idx, ctx, env)?.boxed(), + rhs: self.rhs.resolve(self_idx, ctx, env)?.boxed(), ..self } .ir() @@ -876,17 +861,16 @@ impl Downgrade for ast::HasAttr { impl HasAttr { fn resolve<'a, 'env>( self, - self_idx: usize, - func_idx: Option, + self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { Self { - lhs: self.lhs.resolve(self_idx, func_idx, ctx, env)?.boxed(), + lhs: self.lhs.resolve(self_idx, ctx, env)?.boxed(), rhs: self .rhs .into_iter() - .map(|attr| attr.resolve(self_idx, func_idx, ctx, env)) + .map(|attr| attr.resolve(self_idx, ctx, env)) .collect::>()?, } .ir() @@ -908,13 +892,12 @@ impl Downgrade for ast::UnaryOp { impl UnOp { fn resolve<'a, 'env>( self, - self_idx: usize, - func_idx: Option, + self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { Self { - rhs: self.rhs.resolve(self_idx, func_idx, ctx, env)?.boxed(), + rhs: self.rhs.resolve(self_idx, ctx, env)?.boxed(), ..self } .ir() @@ -940,40 +923,37 @@ impl Downgrade for ast::Select { impl Select { fn resolve<'a, 'env>( self, - self_idx: usize, - func_idx: Option, + self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { - let expr = self.expr.resolve(self_idx, func_idx, ctx, env)?; + let expr = self.expr.resolve(self_idx, ctx, env)?; let attrpath = self .attrpath .into_iter() - .map(|attr| attr.resolve(self_idx, func_idx, ctx, env)) + .map(|attr| attr.resolve(self_idx, ctx, env)) .collect::>>()?; let res = match &expr { - Ir::Attrs(attrs) => attrs.select(&attrpath)?, - &Ir::Thunk(Thunk { idx }) => { - let res = ctx.thunks[idx] - .as_ref() - .try_unwrap_attrs() - .map_err(|_| { - Error::DowngradeError("can only select from a attribute set".into()) - })? - .select(&attrpath); - match res { - Err(err) => { - if let Some(default) = self.default.clone() { - Ok(Some(default.resolve(self_idx, func_idx, ctx, env)?)) - } else { - Err(err) - } - } - ok => ok, - }? - } + Ir::Attrs(attrs) => attrs.select(&attrpath), + &Ir::Thunk(Thunk { idx }) => ctx.thunks[idx] + .0 + .as_ref() + .try_unwrap_attrs() + .map_err(|_| Error::DowngradeError("can not select from ".into()))? + .select(&attrpath), + Ir::Arg(_) => Ok(None), _ => return Err(Error::DowngradeError("can not select from ".into())), }; + let res = match res { + Err(err) => { + if let Some(default) = self.default.clone() { + Ok(Some(default.resolve(self_idx, ctx, env)?)) + } else { + Err(err) + } + } + ok => ok, + }?; if let Some(res) = res { res.ok() } else { @@ -981,7 +961,7 @@ impl Select { expr: expr.boxed(), attrpath, default: if let Some(default) = self.default { - Some(default.resolve(self_idx, func_idx, ctx, env)?.boxed()) + Some(default.resolve(self_idx, ctx, env)?.boxed()) } else { None }, @@ -1075,17 +1055,13 @@ impl Downgrade for ast::LetIn { impl Let { fn resolve<'a, 'env>( self, - self_idx: usize, - func_idx: Option, + self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { - let map = self - .bindings - .clone(); + let map = self.bindings.clone(); let env = env.enter_let(&map); - let expr = self.expr.resolve(self_idx, func_idx, ctx, &env)?.boxed(); - Self { expr, ..self }.ir().ok() + self.expr.resolve(self_idx, ctx, &env)?.ok() } } @@ -1100,19 +1076,12 @@ impl Downgrade for ast::With { impl With { fn resolve<'a, 'env>( self, - self_idx: usize, - func_idx: Option, + self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { - let namespace = self - .namespace - .resolve(self_idx, func_idx, ctx, env)? - .boxed(); - let expr = self - .expr - .resolve(self_idx, func_idx, ctx, &env.enter_with())? - .boxed(); + let namespace = self.namespace.resolve(self_idx, ctx, env)?.boxed(); + let expr = self.expr.resolve(self_idx, ctx, &env.enter_with())?.boxed(); Self { namespace, expr }.ir().ok() } } @@ -1129,8 +1098,7 @@ impl Downgrade for ast::Lambda { impl Func { fn resolve<'a, 'env>( self, - self_idx: usize, - func_idx: Option, + self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { @@ -1145,7 +1113,7 @@ impl Func { alias, ), }; - let body = self.body.resolve(self_idx, func_idx, ctx, &env)?.boxed(); + let body = self.body.resolve(self_idx, ctx, &env)?.boxed(); Ok(Self { body, ..self }) } } @@ -1167,17 +1135,16 @@ impl Downgrade for ast::Apply { impl Call { fn resolve<'a, 'env>( self, - self_idx: usize, - func_idx: Option, + self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { Self { - func: self.func.resolve(self_idx, func_idx, ctx, env)?.boxed(), + func: self.func.resolve(self_idx, ctx, env)?.boxed(), args: self .args .into_iter() - .map(|arg| arg.resolve(self_idx, func_idx, ctx, env)) + .map(|arg| arg.resolve(self_idx, ctx, env)) .collect::>()?, } .ir() diff --git a/src/ir/scc.rs b/src/ir/scc.rs index 4df5cd3..1066654 100644 --- a/src/ir/scc.rs +++ b/src/ir/scc.rs @@ -4,9 +4,15 @@ use super::*; #[derive(Debug, Clone)] pub struct SccNode { - id: usize, - members: Vec, - deps: HashSet, + pub id: usize, + pub members: Vec, + pub deps: HashSet, +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Unwrap)] +pub enum Dep { + Thunk(usize), + Arg(usize) } #[derive(Default, Debug)] @@ -33,7 +39,7 @@ impl SccGraph { ); } - for (from_node_id, from_deps) in ctx.deps.iter().enumerate() { + for (from_node_id, from_deps) in ctx.thunk_deps.iter().enumerate() { let from_scc_id = thunk_to_scc[&from_node_id]; for &to_node_id in from_deps { let to_scc_id = thunk_to_scc[&to_node_id]; @@ -52,46 +58,46 @@ impl SccGraph { } fn sorted(self) -> Vec { - let mut in_degrees: HashMap = self.nodes.keys().map(|&id| (id, 0)).collect(); - for node in self.nodes.values() { - in_degrees.insert(node.id, node.deps.len()); - } - - let mut reverse_adj: HashMap> = HashMap::new(); - for (node_id, node) in &self.nodes { - for &dep_id in &node.deps { - reverse_adj.entry(dep_id).or_default().push(*node_id); - } - } - - let mut queue: std::collections::VecDeque = in_degrees - .iter() - .filter_map(|(&id, °)| if deg == 0 { Some(id) } else { None }) - .collect(); + let mut in_degrees: HashMap = self.nodes.keys().map(|&id| (id, 0)).collect(); + for node in self.nodes.values() { + in_degrees.insert(node.id, node.deps.len()); + } - queue.make_contiguous().sort(); - - let mut sorted_order = Vec::new(); - while let Some(u) = queue.pop_front() { - sorted_order.push(self.nodes[&u].clone()); - - if let Some(dependents) = reverse_adj.get(&u) { - for &v in dependents { - if let Some(degree) = in_degrees.get_mut(&v) { - *degree -= 1; - if *degree == 0 { - queue.push_back(v); - } - } - } - } - } - - if sorted_order.len() != self.nodes.len() { - panic!("Cycle detected in SCC graph, which is impossible!"); - } - - sorted_order + let mut reverse_adj: HashMap> = HashMap::new(); + for (node_id, node) in &self.nodes { + for &dep_id in &node.deps { + reverse_adj.entry(dep_id).or_default().push(*node_id); + } + } + + let mut queue: std::collections::VecDeque = in_degrees + .iter() + .filter_map(|(&id, °)| if deg == 0 { Some(id) } else { None }) + .collect(); + + queue.make_contiguous().sort(); + + let mut sorted_order = Vec::new(); + while let Some(u) = queue.pop_front() { + sorted_order.push(self.nodes[&u].clone()); + + if let Some(dependents) = reverse_adj.get(&u) { + for &v in dependents { + if let Some(degree) = in_degrees.get_mut(&v) { + *degree -= 1; + if *degree == 0 { + queue.push_back(v); + } + } + } + } + } + + if sorted_order.len() != self.nodes.len() { + panic!("Cycle detected in SCC graph, which is impossible!"); + } + + sorted_order } } @@ -134,7 +140,7 @@ impl<'ctx> SccAnalyzer<'ctx> { self.stack.push(v_id); self.on_stack.insert(v_id); - if let Some(deps) = self.ctx.deps.get(v_id) { + if let Some(deps) = self.ctx.thunk_deps.get(v_id) { for &w_id in deps { if !self.indices.contains_key(&w_id) { self.strong_connect(w_id); diff --git a/src/ty/internal/attrset.rs b/src/ty/internal/attrset.rs index aac3712..627c70c 100644 --- a/src/ty/internal/attrset.rs +++ b/src/ty/internal/attrset.rs @@ -14,7 +14,7 @@ use super::super::public as p; use super::Value; #[repr(transparent)] -#[derive(Constructor, Clone, PartialEq)] +#[derive(Constructor, Clone, PartialEq, Debug)] pub struct AttrSet { data: HashMap, } @@ -61,7 +61,7 @@ impl AttrSet { let item = item?; let Some(Value::AttrSet(attrs)) = data.get(&item) else { return Err(Error::EvalError(format!( - "{} not found", + "attribute {} not found", Symbol::from(item) ))); }; @@ -73,7 +73,10 @@ impl AttrSet { .ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(last)))) } - pub fn has_attr(&self, mut path: impl DoubleEndedIterator>) -> 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 { diff --git a/src/ty/internal/list.rs b/src/ty/internal/list.rs index 50c9c7d..3a49f46 100644 --- a/src/ty/internal/list.rs +++ b/src/ty/internal/list.rs @@ -10,7 +10,7 @@ use crate::ty::public as p; use super::Value; -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Debug)] pub struct List { data: EcoVec, } @@ -57,15 +57,6 @@ impl List { } } - pub fn capture(&mut self, env: &Weak) { - self.data.iter().for_each(|v| { - if let Value::Thunk(ref thunk) = v.clone() { - todo!() - // thunk.capture_env_weak(env.clone()); - } - }) - } - pub fn into_inner(self) -> EcoVec { self.data } diff --git a/src/ty/internal/mod.rs b/src/ty/internal/mod.rs index 286da2a..2a65d74 100644 --- a/src/ty/internal/mod.rs +++ b/src/ty/internal/mod.rs @@ -1,4 +1,3 @@ -use std::cell::RefCell; use std::hash::Hash; use std::rc::Rc; @@ -30,17 +29,19 @@ pub enum EnvRef { Weak(VmEnvWeak), } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ThunkRef { pub idx: usize, - pub env: Option, + // pub env: Option, } impl ThunkRef { pub fn new(idx: usize) -> Self { - ThunkRef { idx, env: None } + ThunkRef { + idx, /* env: None */ + } } - + /* pub fn capture(&mut self, env: EnvRef) { let _ = self.env.insert(env); } @@ -49,9 +50,9 @@ impl ThunkRef { replace_with_or_abort(&mut self.env, |env| { env.map(|env| EnvRef::Strong(env.upgraded())) }); - } + } */ } - +/* impl EnvRef { pub fn upgraded(self) -> VmEnv { match self { @@ -59,9 +60,9 @@ impl EnvRef { EnvRef::Strong(strong) => strong, } } -} +} */ -#[derive(IsVariant, Unwrap, Clone)] +#[derive(IsVariant, Unwrap, Clone, Debug)] pub enum Value { Int(i64), Float(f64), @@ -74,7 +75,8 @@ pub enum Value { Catchable(EcoString), PrimOp(Rc), PartialPrimOp(Rc), - Func(ThunkRef), + Func(usize), + PartialFunc(usize, Vec), } impl Hash for Value { @@ -133,7 +135,8 @@ pub enum ValueAsRef<'v> { Catchable(&'v str), PrimOp(&'v PrimOp), PartialPrimOp(&'v PartialPrimOp), - Func(&'v ThunkRef), + Func(usize), + PartialFunc(usize, &'v Vec), } impl Value { @@ -152,7 +155,8 @@ impl Value { Catchable(x) => R::Catchable(x), PrimOp(x) => R::PrimOp(x), PartialPrimOp(x) => R::PartialPrimOp(x), - Func(x) => R::Func(x), + Func(x) => R::Func(*x), + PartialFunc(x, args) => R::PartialFunc(*x, args), } } } @@ -176,6 +180,7 @@ impl Value { PrimOp(_) => "lambda", PartialPrimOp(_) => "lambda", Func(_) => "lambda", + PartialFunc(..) => "lambda", } } @@ -187,24 +192,85 @@ impl Value { } } - pub fn call(&mut self, arg: Self, engine: &mut Engine) -> Result<()> { + pub fn call(&mut self, args: Vec, engine: &mut Engine, env: &mut VmEnv) -> Result<()> { use Value::*; - if matches!(arg, Value::Catchable(_)) { - *self = arg; - return Ok(()); + for arg in args.iter() { + if matches!(arg, Value::Catchable(_)) { + *self = arg.clone(); + return Ok(()); + } } *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 + PrimOp(func) => func.call(args, engine), + PartialPrimOp(func) => func.call(args, engine), + PartialFunc(idx, old) => { + let idx = *idx; + let len = args.len() + old.len(); + env.enter_args(std::mem::take(old)); + env.enter_cache_level(|env| { + let mut args = args.into_iter().peekable(); + env.enter_arg(args.next().unwrap()); + let mut ret = engine.eval_thunk(idx, env)?; + while args.peek().is_some() { + match ret { + Value::Func(func) => { + env.enter_arg(args.next().unwrap()); + ret = engine.eval_thunk(func, env)?; + } + Value::PrimOp(primop) => { + ret = primop.call(args.collect(), engine)?; + break; + } + Value::PartialPrimOp(mut primop) => { + ret = primop.call(args.collect(), engine)?; + break; + } + _ => todo!(), + } + } + let args = env.pop_args(len); + if let Value::Func(idx) = ret { + ret = Value::PartialFunc(idx, args) + } else if let Value::PartialFunc(_, old) = &mut ret { + old.extend(args); + } + ret.ok() + }) + } + &mut Func(idx) => { + let len = args.len(); + let mut args = args.into_iter().peekable(); + env.enter_cache_level(|env| { + env.enter_arg(args.next().unwrap()); + let mut ret = engine.eval_thunk(idx, env)?; + while args.peek().is_some() { + match ret { + Value::Func(func) => { + env.enter_arg(args.next().unwrap()); + ret = engine.eval_thunk(func, env)?; + } + Value::PrimOp(primop) => { + ret = primop.call(args.collect(), engine)?; + break; + } + Value::PartialPrimOp(mut primop) => { + ret = primop.call(args.collect(), engine)?; + break; + } + _ => todo!(), + } + } + let args = env.pop_args(len); + if let Value::Func(idx) = ret { + ret = Value::PartialFunc(idx, args) + } else if let Value::PartialFunc(_, old) = &mut ret { + old.extend(args); + } + ret.ok() + }) } Catchable(_) => return Ok(()), - _ => todo!(), + other => todo!("{}", other.typename()), }?; Ok(()) } @@ -421,7 +487,10 @@ impl Value { Ok(self) } - pub fn has_attr(&mut self, path: impl DoubleEndedIterator>) -> 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; @@ -441,15 +510,17 @@ impl Value { self } - pub fn force(&mut self, engine: &mut Engine) -> Result<&mut Self> { + pub fn force(&mut self, engine: &mut Engine, env: &mut VmEnv) -> 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)?, - ); + match env.lookup_cache(old.idx, |env| engine.eval_thunk(old.idx, env)) { + Ok(ok) => std::ptr::write(self, ok), + Err(err) => { + std::ptr::write(self, Self::Null); + return Err(err); + } + } } } Ok(self) @@ -480,6 +551,7 @@ impl Value { PrimOp(primop) => Value::PrimOp(primop.name), PartialPrimOp(primop) => Value::PartialPrimOp(primop.name), Func(_) => Value::Func, + PartialFunc(..) => Value::Func, } } } diff --git a/src/ty/internal/primop.rs b/src/ty/internal/primop.rs index d1dbad3..430d5fc 100644 --- a/src/ty/internal/primop.rs +++ b/src/ty/internal/primop.rs @@ -21,13 +21,14 @@ impl PartialEq for PrimOp { } impl PrimOp { - pub fn call(&self, arg: Value, ctx: &Engine) -> Result { - let mut args = Vec::with_capacity(self.arity); - args.push(arg); - if self.arity > 1 { + pub fn call(&self, args: Vec, ctx: &Engine) -> Result { + if args.len() > self.arity { + todo!() + } + if self.arity > args.len() { Value::PartialPrimOp(Rc::new(PartialPrimOp { name: self.name, - arity: self.arity - 1, + arity: self.arity - args.len(), args, func: self.func, })) @@ -38,7 +39,7 @@ impl PrimOp { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct PartialPrimOp { pub name: &'static str, arity: usize, @@ -53,12 +54,15 @@ impl PartialEq for PartialPrimOp { } impl PartialPrimOp { - pub fn call(self: &mut Rc, arg: Value, ctx: &Engine) -> Result { + pub fn call(self: &mut Rc, args: Vec, ctx: &Engine) -> Result { + if self.arity < args.len() { + todo!() + } let func = self.func; let Some(ret) = ({ let self_mut = Rc::make_mut(self); - self_mut.args.push(arg); - self_mut.arity -= 1; + self_mut.arity -= args.len(); + self_mut.args.extend(args); if self_mut.arity == 0 { Some(func(std::mem::take(&mut self_mut.args), ctx)) } else {