feat: graph (WIP)

This commit is contained in:
2025-08-15 12:38:11 +08:00
parent d8ad7fe904
commit 3c7722a3d2
7 changed files with 78 additions and 30 deletions

24
TODO.md
View File

@@ -2,4 +2,26 @@
- [ ] resolve stage - [ ] resolve stage
- [ ] dynamic attr resolution - [ ] dynamic attr resolution
- [ ] import resolution - [ ] import resolution
- [ ] path resolution - [ ] static path resolution
- [ ] eval stage
- [ ] dynamic path resolution
- [ ] graph update
- [ ] stack storage
阻拦变量解析的是 `with import` 而不是一般的 `with`
解析阶段可以解决除涉及非字符串常量拼接路径导入之外的问题
# 工作流程
```mermaid
flowchart TD
Start --> CtxNew[Context::new]
CtxNew --> CtxEval[Context::eval]
CtxEval --> DCtxNew[DowngradeCtx::new]
DCtxNew --> DCtxDowngradeRoot[DowngradeCtx::downgrade_root]
DCtxDowngradeRoot --> RCtxNew[ResolveCtx::new]
RCtxNew --> RCtxResolveRoot[ResolveCtx::resolve_root]
RCtxResolveRoot --> ECtxNew[EvalCtx::new]
ECtxNew --> ECtxEvalRoot[EvalCtx::eval_root]
ECtxEvalRoot --> ECtxEvalDeps[EvalCtx::eval_deps]
ECtxEvalDeps --> ECtxEval[EvalCtx::eval]
```

View File

@@ -8,6 +8,7 @@ use nixjit_eval::{Args, EvalContext, Evaluate, StackFrame, Value};
use nixjit_ir::ExprId; use nixjit_ir::ExprId;
use nixjit_jit::JITContext; use nixjit_jit::JITContext;
use nixjit_lir::Lir; use nixjit_lir::Lir;
use petgraph::visit::{Topo, Walker};
use super::Context; use super::Context;
@@ -40,10 +41,7 @@ impl<'ctx, 'bump> EvalCtx<'ctx, 'bump> {
dbg!(&deps, &self.stack); dbg!(&deps, &self.stack);
for (dep, idx) in deps { for (dep, idx) in deps {
unsafe { unsafe {
if matches!( if matches!(&**self.ctx.lirs.get_unchecked(dep.raw()), Lir::Arg(_)) {
&**self.ctx.lirs.get_unchecked(dep.raw()),
Lir::Arg(_)
) {
*frame.get_unchecked_mut(idx.raw()) = arg.as_ref().unwrap().clone(); *frame.get_unchecked_mut(idx.raw()) = arg.as_ref().unwrap().clone();
continue; continue;
} }
@@ -76,7 +74,7 @@ impl EvalContext for EvalCtx<'_, '_> {
self.stack.push(frame); self.stack.push(frame);
if let Err(err) = self.eval_deps(func, arg) { if let Err(err) = self.eval_deps(func, arg) {
self.stack.pop(); self.stack.pop();
return Err(err) return Err(err);
} }
let ret = self.eval(func); let ret = self.eval(func);
self.stack.pop(); self.stack.pop();

View File

@@ -1,4 +1,4 @@
use std::{marker::PhantomPinned, ops::Deref}; use std::{marker::PhantomPinned, ops::{Deref, DerefMut}};
use bumpalo::{Bump, boxed::Box}; use bumpalo::{Bump, boxed::Box};
use hashbrown::HashMap; use hashbrown::HashMap;
@@ -32,6 +32,12 @@ struct Pin<'bump, T> {
_marker: PhantomPinned, _marker: PhantomPinned,
} }
impl<T> Pin<'_, T> {
fn into_inner(self) -> T {
Box::into_inner(self.ptr)
}
}
impl<T> Deref for Pin<'_, T> { impl<T> Deref for Pin<'_, T> {
type Target = T; type Target = T;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
@@ -39,6 +45,12 @@ impl<T> Deref for Pin<'_, T> {
} }
} }
impl<T> DerefMut for Pin<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.ptr.as_mut()
}
}
impl<'bump, T> Pin<'bump, T> { impl<'bump, T> Pin<'bump, T> {
fn new_in(x: T, bump: &'bump Bump) -> Self { fn new_in(x: T, bump: &'bump Bump) -> Self {
Self { Self {

View File

@@ -1,7 +1,5 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::pin::Pin;
use bumpalo::boxed::Box;
use derive_more::Unwrap; use derive_more::Unwrap;
use hashbrown::HashMap; use hashbrown::HashMap;
@@ -11,7 +9,7 @@ use nixjit_ir::{Const, ExprId, Param, StackIdx};
use nixjit_lir::{Lir, LookupResult, Resolve, ResolveContext}; use nixjit_lir::{Lir, LookupResult, Resolve, ResolveContext};
use replace_with::replace_with_and_return; use replace_with::replace_with_and_return;
use super::Context; use super::{Context, Pin};
#[derive(Clone)] #[derive(Clone)]
enum Scope<'ctx> { enum Scope<'ctx> {
@@ -20,7 +18,7 @@ enum Scope<'ctx> {
/// A function argument scope. `Some` holds the name of the argument set if present. /// A function argument scope. `Some` holds the name of the argument set if present.
Arg(Option<String>), Arg(Option<String>),
Builtins(&'ctx HashMap<&'static str, ExprId>), Builtins(&'ctx HashMap<&'static str, ExprId>),
Repl(&'ctx HashMap<String, ExprId>) Repl(&'ctx HashMap<String, ExprId>),
} }
/// Represents an expression at different stages of compilation. /// Represents an expression at different stages of compilation.
@@ -44,7 +42,7 @@ impl Ir {
pub struct ResolveCtx<'ctx, 'bump> { pub struct ResolveCtx<'ctx, 'bump> {
ctx: &'ctx mut Context<'bump>, ctx: &'ctx mut Context<'bump>,
irs: Vec<Pin<Box<'bump, RefCell<Ir>>>>, irs: Vec<Pin<'bump, RefCell<Ir>>>,
scopes: Vec<Scope<'ctx>>, scopes: Vec<Scope<'ctx>>,
has_with: bool, has_with: bool,
with_used: bool, with_used: bool,
@@ -58,15 +56,14 @@ impl<'ctx, 'bump> ResolveCtx<'ctx, 'bump> {
Self { Self {
scopes: vec![ scopes: vec![
Scope::Builtins(&ctx.global_scope), Scope::Builtins(&ctx.global_scope),
Scope::Repl(&ctx.repl_scope) Scope::Repl(&ctx.repl_scope),
], ],
has_with: false, has_with: false,
with_used: false, with_used: false,
irs: core::mem::take(&mut ctx.hirs) irs: core::mem::take(&mut ctx.hirs)
.into_iter() .into_iter()
.map(|hir| Ir::Hir(hir).into()) .map(|hir| Ir::Hir(hir).into())
.map(|ir| Box::new_in(ir, ctx.bump)) .map(|ir| Pin::new_in(ir, ctx.bump))
.map(Pin::new)
.collect(), .collect(),
ctx: ctx_mut, ctx: ctx_mut,
closures: Vec::new(), closures: Vec::new(),
@@ -105,10 +102,10 @@ impl<'ctx, 'bump> ResolveCtx<'ctx, 'bump> {
} }
fn new_lir(&mut self, lir: Lir) -> ExprId { fn new_lir(&mut self, lir: Lir) -> ExprId {
self.irs.push(Pin::new(Box::new_in( self.irs.push(Pin::new_in(
RefCell::new(Ir::Lir(lir)), RefCell::new(Ir::Lir(lir)),
self.ctx.bump, self.ctx.bump,
))); ));
unsafe { ExprId::from_raw(self.ctx.lirs.len() + self.irs.len() - 1) } unsafe { ExprId::from_raw(self.ctx.lirs.len() + self.irs.len() - 1) }
} }
} }
@@ -144,6 +141,10 @@ impl ResolveContext for ResolveCtx<'_, '_> {
result result
} }
fn resolve_call(&mut self, func: ExprId, arg: ExprId) -> Result<()> {
todo!()
}
fn resolve_root(mut self, expr: ExprId) -> Result<()> { fn resolve_root(mut self, expr: ExprId) -> Result<()> {
self.closures.push((expr, None, 0)); self.closures.push((expr, None, 0));
let ret = self.resolve(expr); let ret = self.resolve(expr);
@@ -151,11 +152,10 @@ impl ResolveContext for ResolveCtx<'_, '_> {
self.ctx.lirs.extend( self.ctx.lirs.extend(
self.irs self.irs
.into_iter() .into_iter()
.map(|pin| unsafe { core::mem::transmute::<Pin<_>, Box<_>>(pin) }) .map(Pin::into_inner)
.map(Box::into_inner)
.map(RefCell::into_inner) .map(RefCell::into_inner)
.map(Ir::unwrap_lir) .map(Ir::unwrap_lir)
.map(|lir| crate::Pin::new_in(lir, self.ctx.bump)) .map(|lir| crate::Pin::new_in(lir, self.ctx.bump)),
); );
} }
ret ret
@@ -215,7 +215,9 @@ impl ResolveContext for ResolveCtx<'_, '_> {
} }
fn lookup_arg(&mut self) -> StackIdx { fn lookup_arg(&mut self) -> StackIdx {
let Some((func, Some(arg), count)) = unsafe { &mut *(self as *mut Self) }.closures.last_mut() else { let Some((func, Some(arg), count)) =
unsafe { &mut *(self as *mut Self) }.closures.last_mut()
else {
unreachable!() unreachable!()
}; };
self.add_dep(*func, *arg, count) self.add_dep(*func, *arg, count)
@@ -246,7 +248,7 @@ impl ResolveContext for ResolveCtx<'_, '_> {
(core::mem::replace(&mut self.with_used, with_used), res) (core::mem::replace(&mut self.with_used, with_used), res)
} }
fn with_param_env<T>( fn with_closure_env<T>(
&mut self, &mut self,
func: ExprId, func: ExprId,
ident: Option<String>, ident: Option<String>,

View File

@@ -24,11 +24,11 @@ mod value;
pub trait EvalContext { pub trait EvalContext {
fn eval_root(self, expr: ExprId) -> Result<Value>; fn eval_root(self, expr: ExprId) -> Result<Value>;
/// Evaluates an expression by its ID. /// Evaluates an expression by its ID.
fn eval(&mut self, expr: ExprId) -> Result<Value>; fn eval(&mut self, expr: ExprId) -> Result<Value>;
fn call(&mut self, func: ExprId, arg: Option<Value>, frame: StackFrame) -> Result<Value>; fn call(&mut self, func: ExprId, arg: Option<Value>, frame: StackFrame) -> Result<Value>;
/// Enters a `with` scope for the duration of a closure's execution. /// Enters a `with` scope for the duration of a closure's execution.
fn with_with_env<T>( fn with_with_env<T>(
&mut self, &mut self,
@@ -83,9 +83,15 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for lir::Lir {
Str(x) => x.eval(ctx), Str(x) => x.eval(ctx),
Var(x) => x.eval(ctx), Var(x) => x.eval(ctx),
Path(x) => x.eval(ctx), Path(x) => x.eval(ctx),
&StackRef(idx) => Ok(ctx.lookup_stack(idx).clone()), &StackRef(idx) => {
let mut val = ctx.lookup_stack(idx).clone();
val.force(ctx)?;
Ok(val)
}
&ExprRef(expr) => ctx.eval(expr), &ExprRef(expr) => ctx.eval(expr),
&FuncRef(body) => Ok(Value::Closure(Closure::new(body, ctx.capture_stack().clone()).into())), &FuncRef(body) => Ok(Value::Closure(
Closure::new(body, ctx.capture_stack().clone()).into(),
)),
&Arg(_) => unreachable!(), &Arg(_) => unreachable!(),
&PrimOp(primop) => Ok(Value::PrimOp(primop)), &PrimOp(primop) => Ok(Value::PrimOp(primop)),
&Thunk(id) => Ok(Value::Thunk(id)), &Thunk(id) => Ok(Value::Thunk(id)),

View File

@@ -18,7 +18,11 @@ pub struct Closure {
} }
impl Closure { impl Closure {
pub fn call<Ctx: EvalContext>(self: Rc<Self>, arg: Option<Value>, ctx: &mut Ctx) -> Result<Value> { pub fn call<Ctx: EvalContext>(
self: Rc<Self>,
arg: Option<Value>,
ctx: &mut Ctx,
) -> Result<Value> {
let Self { body: func, frame } = Rc::unwrap_or_clone(self); let Self { body: func, frame } = Rc::unwrap_or_clone(self);
ctx.call(func, arg, frame) ctx.call(func, arg, frame)
} }

View File

@@ -71,6 +71,8 @@ pub trait ResolveContext {
/// Triggers the resolution of a given expression. /// Triggers the resolution of a given expression.
fn resolve(&mut self, expr: ExprId) -> Result<()>; fn resolve(&mut self, expr: ExprId) -> Result<()>;
fn resolve_call(&mut self, func: ExprId, arg: ExprId) -> Result<()>;
fn resolve_root(self, expr: ExprId) -> Result<()>; fn resolve_root(self, expr: ExprId) -> Result<()>;
/// Looks up a variable by name in the current scope. /// Looks up a variable by name in the current scope.
@@ -89,7 +91,7 @@ pub trait ResolveContext {
) -> T; ) -> T;
/// Enters a function parameter scope for the duration of a closure. /// Enters a function parameter scope for the duration of a closure.
fn with_param_env<T>( fn with_closure_env<T>(
&mut self, &mut self,
func: ExprId, func: ExprId,
ident: Option<String>, ident: Option<String>,
@@ -129,7 +131,7 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for hir::Hir {
ctx.resolve(x)?; ctx.resolve(x)?;
Ok(Lir::Thunk(x)) Ok(Lir::Thunk(x))
} }
Arg(_) => Ok(Lir::StackRef(ctx.lookup_arg())) Arg(_) => Ok(Lir::StackRef(ctx.lookup_arg())),
} }
} }
} }
@@ -219,17 +221,19 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for If {
/// It then registers the function with the context. /// It then registers the function with the context.
impl<Ctx: ResolveContext> Resolve<Ctx> for Func { impl<Ctx: ResolveContext> Resolve<Ctx> for Func {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> { fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
ctx.with_param_env(self.body, self.param.ident.clone(), |ctx| ctx.resolve(self.body))?; ctx.with_closure_env(self.body, self.param.ident.clone(), |ctx| {
ctx.resolve(self.body)
})?;
ctx.new_func(self.body, self.param); ctx.new_func(self.body, self.param);
Ok(Lir::FuncRef(self.body)) Ok(Lir::FuncRef(self.body))
} }
} }
/// Resolves a `Call` by resolving the function and all of its arguments.
impl<Ctx: ResolveContext> Resolve<Ctx> for Call { impl<Ctx: ResolveContext> Resolve<Ctx> for Call {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> { fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
ctx.resolve(self.func)?; ctx.resolve(self.func)?;
ctx.resolve(self.arg)?; ctx.resolve(self.arg)?;
ctx.resolve_call(self.func, self.arg)?;
Ok(self.to_lir()) Ok(self.to_lir())
} }
} }