feat: SCC analysis (thunk capture WIP)

This commit is contained in:
2025-06-17 11:53:54 +08:00
parent b2d2490327
commit 7f6848c9e5
19 changed files with 501 additions and 458 deletions

View File

@@ -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<EcoString, Value>,
}
@@ -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<Item = Result<EcoString>>) -> Result<bool> {
pub fn has_attr(
&self,
mut path: impl DoubleEndedIterator<Item = Result<EcoString>>,
) -> Result<bool> {
let mut data = &self.data;
let last = path.nth_back(0).unwrap();
for item in path {

View File

@@ -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<Value>,
}
@@ -57,15 +57,6 @@ impl List {
}
}
pub fn capture(&mut self, env: &Weak<VmEnv>) {
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<Value> {
self.data
}

View File

@@ -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<EnvRef>,
// pub env: Option<EnvRef>,
}
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<PrimOp>),
PartialPrimOp(Rc<PartialPrimOp>),
Func(ThunkRef),
Func(usize),
PartialFunc(usize, Vec<Value>),
}
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<Value>),
}
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<Self>, 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<Item = Result<EcoString>>) -> Result<()> {
pub fn has_attr(
&mut self,
path: impl DoubleEndedIterator<Item = Result<EcoString>>,
) -> 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,
}
}
}

View File

@@ -21,13 +21,14 @@ impl PartialEq for PrimOp {
}
impl PrimOp {
pub fn call(&self, arg: Value, ctx: &Engine) -> Result<Value> {
let mut args = Vec::with_capacity(self.arity);
args.push(arg);
if self.arity > 1 {
pub fn call(&self, args: Vec<Value>, ctx: &Engine) -> Result<Value> {
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<Self>, arg: Value, ctx: &Engine) -> Result<Value> {
pub fn call(self: &mut Rc<Self>, args: Vec<Value>, ctx: &Engine) -> Result<Value> {
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 {