feat: a lot
This commit is contained in:
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -396,15 +396,8 @@ dependencies = [
|
|||||||
name = "nixjit"
|
name = "nixjit"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown 0.15.4",
|
|
||||||
"nixjit_context",
|
"nixjit_context",
|
||||||
"nixjit_error",
|
|
||||||
"nixjit_eval",
|
|
||||||
"nixjit_hir",
|
|
||||||
"nixjit_ir",
|
|
||||||
"nixjit_lir",
|
|
||||||
"nixjit_value",
|
"nixjit_value",
|
||||||
"rnix",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -425,6 +418,7 @@ dependencies = [
|
|||||||
"cranelift-jit",
|
"cranelift-jit",
|
||||||
"cranelift-module",
|
"cranelift-module",
|
||||||
"cranelift-native",
|
"cranelift-native",
|
||||||
|
"derive_more",
|
||||||
"hashbrown 0.15.4",
|
"hashbrown 0.15.4",
|
||||||
"itertools",
|
"itertools",
|
||||||
"nixjit_builtins",
|
"nixjit_builtins",
|
||||||
@@ -436,6 +430,8 @@ dependencies = [
|
|||||||
"nixjit_lir",
|
"nixjit_lir",
|
||||||
"nixjit_value",
|
"nixjit_value",
|
||||||
"petgraph",
|
"petgraph",
|
||||||
|
"replace_with",
|
||||||
|
"rnix",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -4,13 +4,5 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rnix = "0.12"
|
|
||||||
hashbrown = "0.15"
|
|
||||||
|
|
||||||
nixjit_context = { path = "../nixjit_context" }
|
nixjit_context = { path = "../nixjit_context" }
|
||||||
nixjit_error = { path = "../nixjit_error" }
|
|
||||||
nixjit_eval = { path = "../nixjit_eval" }
|
|
||||||
nixjit_hir = { path = "../nixjit_hir" }
|
|
||||||
nixjit_ir = { path = "../nixjit_ir" }
|
|
||||||
nixjit_lir = { path = "../nixjit_lir" }
|
|
||||||
nixjit_value = { path = "../nixjit_value" }
|
nixjit_value = { path = "../nixjit_value" }
|
||||||
|
|||||||
@@ -12,4 +12,3 @@
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,17 @@
|
|||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use hashbrown::HashSet;
|
|
||||||
use nixjit_context::Context;
|
use nixjit_context::Context;
|
||||||
use nixjit_eval::EvalContext;
|
|
||||||
use nixjit_hir::Downgrade;
|
|
||||||
use nixjit_lir::ResolveContext;
|
|
||||||
use nixjit_value::{AttrSet, Const, List, Symbol, Value};
|
use nixjit_value::{AttrSet, Const, List, Symbol, Value};
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn test_expr(expr: &str, expected: Value) {
|
fn test_expr(expr: &str, expected: Value) {
|
||||||
println!("{expr}");
|
println!("{expr}");
|
||||||
let mut ctx = Context::new();
|
assert_eq!(
|
||||||
let expr = rnix::Root::parse(expr).tree().expr().unwrap().downgrade(&mut ctx).unwrap();
|
Context::new().eval(expr).unwrap(),
|
||||||
ctx.resolve(expr).unwrap();
|
expected
|
||||||
assert_eq!(ctx.eval(expr).unwrap().to_public(&ctx, &mut HashSet::new()), expected);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! map {
|
macro_rules! map {
|
||||||
@@ -59,7 +54,7 @@ macro_rules! string {
|
|||||||
|
|
||||||
macro_rules! symbol {
|
macro_rules! symbol {
|
||||||
($e:expr) => {
|
($e:expr) => {
|
||||||
Symbol::from($e.to_string())
|
Symbol::from($e)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +201,10 @@ fn test_func() {
|
|||||||
"(inputs@{ x, y, ... }: x + inputs.y) { x = 1; y = 2; z = 3; }",
|
"(inputs@{ x, y, ... }: x + inputs.y) { x = 1; y = 2; z = 3; }",
|
||||||
int!(3),
|
int!(3),
|
||||||
);
|
);
|
||||||
test_expr("let fix = f: let x = f x; in x; in (fix (self: { x = 1; y = self.x + 1; })).y", int!(2));
|
test_expr(
|
||||||
|
"let fix = f: let x = f x; in x; in (fix (self: { x = 1; y = self.x + 1; })).y",
|
||||||
|
int!(2),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
use nixjit_macros::builtins;
|
use nixjit_macros::builtins;
|
||||||
use nixjit_eval::EvalContext;
|
|
||||||
|
|
||||||
pub trait BuiltinsContext: EvalContext {}
|
pub trait BuiltinsContext {}
|
||||||
|
|
||||||
#[builtins]
|
#[builtins]
|
||||||
mod builtins {
|
pub mod builtins {
|
||||||
use nixjit_error::{Error, Result};
|
use nixjit_error::{Error, Result};
|
||||||
use nixjit_eval::Value;
|
use nixjit_eval::Value;
|
||||||
use nixjit_value::Const;
|
use nixjit_value::Const;
|
||||||
@@ -15,7 +14,7 @@ mod builtins {
|
|||||||
const FALSE: Const = Const::Bool(false);
|
const FALSE: Const = Const::Bool(false);
|
||||||
const NULL: Const = Const::Null;
|
const NULL: Const = Const::Null;
|
||||||
|
|
||||||
fn add<Ctx: BuiltinsContext>(a: Value<Ctx>, b: Value<Ctx>) -> Result<Value<Ctx>> {
|
fn add(a: Value, b: Value) -> Result<Value> {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
Ok(match (a, b) {
|
Ok(match (a, b) {
|
||||||
(Int(a), Int(b)) => Int(a + b),
|
(Int(a), Int(b)) => Int(a + b),
|
||||||
@@ -28,11 +27,11 @@ mod builtins {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn import<Ctx: BuiltinsContext>(ctx: &mut Ctx, path: Value<Ctx>) -> Result<Value<Ctx>> {
|
pub fn import<Ctx: BuiltinsContext>(ctx: &mut Ctx, path: Value) -> Result<Value> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn elem_at<Ctx: BuiltinsContext>(list: Value<Ctx>, idx: Value<Ctx>) -> Result<Value<Ctx>> {
|
fn elem_at(list: Value, idx: Value) -> Result<Value> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,12 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
derive_more = { version = "2.0", features = ["full"] }
|
||||||
hashbrown = "0.15"
|
hashbrown = "0.15"
|
||||||
itertools = "0.14"
|
itertools = "0.14"
|
||||||
petgraph = "0.8"
|
petgraph = "0.8"
|
||||||
|
replace_with = "0.1"
|
||||||
|
rnix = "0.12"
|
||||||
|
|
||||||
cranelift = "0.122"
|
cranelift = "0.122"
|
||||||
cranelift-module = "0.122"
|
cranelift-module = "0.122"
|
||||||
|
|||||||
@@ -1,50 +1,162 @@
|
|||||||
use core::mem::MaybeUninit;
|
|
||||||
use std::cell::{OnceCell, RefCell};
|
use std::cell::{OnceCell, RefCell};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use derive_more::Unwrap;
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use petgraph::algo::toposort;
|
use itertools::Itertools;
|
||||||
use petgraph::graph::{DiGraph, NodeIndex};
|
use petgraph::graph::{DiGraph, NodeIndex};
|
||||||
|
|
||||||
|
use nixjit_builtins::{
|
||||||
|
Builtins, BuiltinsContext,
|
||||||
|
builtins::{CONSTS_LEN, GLOBAL_LEN, SCOPED_LEN},
|
||||||
|
};
|
||||||
use nixjit_error::{Error, Result};
|
use nixjit_error::{Error, Result};
|
||||||
use nixjit_eval::{EvalContext, Evaluate, Value};
|
use nixjit_eval::{EvalContext, Evaluate, Value};
|
||||||
use nixjit_hir::{DowngradeContext, Hir};
|
use nixjit_hir::{Downgrade, DowngradeContext, Hir};
|
||||||
use nixjit_ir::{ArgIdx, ExprId, Param};
|
use nixjit_ir::{ArgIdx, Const, ExprId, Param, PrimOp, PrimOpId};
|
||||||
use nixjit_lir::{Lir, LookupResult, Resolve, ResolveContext};
|
use nixjit_lir::{Lir, LookupResult, Resolve, ResolveContext};
|
||||||
|
|
||||||
use nixjit_jit::{JITCompiler, JITContext, JITFunc};
|
use nixjit_jit::{JITCompiler, JITContext, JITFunc};
|
||||||
|
use replace_with::replace_with_and_return;
|
||||||
|
|
||||||
#[derive(Debug)]
|
enum Scope {
|
||||||
struct Frame {
|
With,
|
||||||
values: Vec<Value<Context>>,
|
Let(HashMap<String, ExprId>),
|
||||||
left: usize,
|
Arg(Option<String>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Unwrap)]
|
||||||
|
enum Ir {
|
||||||
|
Hir(Hir),
|
||||||
|
Lir(Lir),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ir {
|
||||||
|
unsafe fn unwrap_hir_ref_unchecked(&self) -> &Hir {
|
||||||
|
if let Self::Hir(hir) = self {
|
||||||
|
hir
|
||||||
|
} else {
|
||||||
|
unsafe { core::hint::unreachable_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn unwrap_hir_mut_unchecked(&mut self) -> &mut Hir {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
if let Self::Hir(hir) = self {
|
||||||
|
hir
|
||||||
|
} else {
|
||||||
|
unsafe { core::hint::unreachable_unchecked() }
|
||||||
|
}
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
if let Self::Hir(hir) = self {
|
||||||
|
hir
|
||||||
|
} else {
|
||||||
|
unsafe { core::hint::unreachable_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn unwrap_hir_unchecked(self) -> Hir {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.unwrap_hir()
|
||||||
|
} else if let Self::Hir(hir) = self {
|
||||||
|
hir
|
||||||
|
} else {
|
||||||
|
unsafe { core::hint::unreachable_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn unwrap_lir_ref_unchecked(&self) -> &Lir {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
if let Self::Lir(lir) = self {
|
||||||
|
lir
|
||||||
|
} else {
|
||||||
|
unsafe { core::hint::unreachable_unchecked() }
|
||||||
|
}
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
if let Self::Lir(lir) = self {
|
||||||
|
lir
|
||||||
|
} else {
|
||||||
|
panic!()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
hirs: Vec<RefCell<Hir>>,
|
irs: Vec<RefCell<Ir>>,
|
||||||
lirs: Vec<MaybeUninit<Lir>>,
|
|
||||||
resolved: Vec<bool>,
|
resolved: Vec<bool>,
|
||||||
scopes: Vec<HashMap<String, LookupResult>>,
|
scopes: Vec<Scope>,
|
||||||
|
args_count: usize,
|
||||||
|
primops: Vec<fn(&mut Context, Vec<Value>) -> Result<Value>>,
|
||||||
funcs: HashMap<ExprId, Param>,
|
funcs: HashMap<ExprId, Param>,
|
||||||
graph: DiGraph<ExprId, ()>,
|
graph: DiGraph<ExprId, ()>,
|
||||||
nodes: Vec<NodeIndex>,
|
nodes: Vec<NodeIndex>,
|
||||||
|
|
||||||
stack: Vec<Frame>,
|
stack: Vec<Vec<Value>>,
|
||||||
with_scopes: Vec<Rc<HashMap<String, Value<Self>>>>,
|
with_scopes: Vec<Rc<HashMap<String, Value>>>,
|
||||||
|
|
||||||
jit: JITCompiler<Self>,
|
jit: JITCompiler<Self>,
|
||||||
compiled: Vec<OnceCell<JITFunc<Self>>>,
|
compiled: Vec<OnceCell<JITFunc<Self>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Context {
|
impl Default for Context {
|
||||||
fn drop(&mut self) {
|
fn default() -> Self {
|
||||||
for (i, lir) in self.lirs.iter_mut().enumerate() {
|
let Builtins {
|
||||||
if self.resolved[i] {
|
consts,
|
||||||
unsafe {
|
global,
|
||||||
lir.assume_init_drop();
|
scoped,
|
||||||
}
|
} = Builtins::new();
|
||||||
}
|
let global_scope = Scope::Let(
|
||||||
|
consts
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(id, (k, _))| (k.to_string(), unsafe { ExprId::from(id) }))
|
||||||
|
.chain(global.iter().enumerate().map(|(idx, (k, _, _))| {
|
||||||
|
(k.to_string(), unsafe {
|
||||||
|
ExprId::from(idx + CONSTS_LEN)
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
.chain(core::iter::once(("builtins".to_string(), unsafe {
|
||||||
|
ExprId::from(CONSTS_LEN + GLOBAL_LEN + SCOPED_LEN)
|
||||||
|
})))
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
let primops = global
|
||||||
|
.iter()
|
||||||
|
.map(|&(_, _, f)| f)
|
||||||
|
.chain(scoped.iter().map(|&(_, _, f)| f))
|
||||||
|
.collect();
|
||||||
|
let irs = consts
|
||||||
|
.into_iter()
|
||||||
|
.map(|(_, val)| Ir::Lir(Lir::Const(Const { val })))
|
||||||
|
.chain(
|
||||||
|
global
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(idx, (name, arity, _))| {
|
||||||
|
Ir::Lir(Lir::PrimOp(PrimOp {
|
||||||
|
name,
|
||||||
|
arity,
|
||||||
|
id: unsafe { PrimOpId::from(idx) },
|
||||||
|
}))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.map(RefCell::new)
|
||||||
|
.collect();
|
||||||
|
Self {
|
||||||
|
irs,
|
||||||
|
resolved: Vec::new(),
|
||||||
|
scopes: vec![global_scope],
|
||||||
|
args_count: 0,
|
||||||
|
primops,
|
||||||
|
funcs: HashMap::new(),
|
||||||
|
graph: DiGraph::new(),
|
||||||
|
nodes: Vec::new(),
|
||||||
|
|
||||||
|
stack: Vec::new(),
|
||||||
|
with_scopes: Vec::new(),
|
||||||
|
|
||||||
|
jit: JITCompiler::new(),
|
||||||
|
compiled: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,114 +165,166 @@ impl Context {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn eval(mut self, expr: &str) -> Result<nixjit_value::Value> {
|
||||||
|
let root = rnix::Root::parse(expr);
|
||||||
|
if !root.errors().is_empty() {
|
||||||
|
return Err(Error::ParseError(
|
||||||
|
root.errors().iter().map(|err| err.to_string()).join(";"),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let root = root.tree().expr().unwrap().downgrade(&mut self)?;
|
||||||
|
self.resolve(&root)?;
|
||||||
|
Ok(EvalContext::eval(&mut self, &root)?.to_public(&mut HashSet::new()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DowngradeContext for Context {
|
impl DowngradeContext for Context {
|
||||||
fn new_expr(&mut self, expr: Hir) -> ExprId {
|
fn new_expr(&mut self, expr: Hir) -> ExprId {
|
||||||
let id = ExprId::from(self.hirs.len());
|
let id = unsafe { ExprId::from(self.irs.len()) };
|
||||||
self.hirs.push(expr.into());
|
self.irs.push(Ir::Hir(expr).into());
|
||||||
self.lirs.push(MaybeUninit::uninit());
|
self.nodes.push(self.graph.add_node(unsafe { id.clone() }));
|
||||||
self.nodes.push(self.graph.add_node(id));
|
|
||||||
self.resolved.push(false);
|
self.resolved.push(false);
|
||||||
self.compiled.push(OnceCell::new());
|
self.compiled.push(OnceCell::new());
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
fn with_expr<T>(&self, id: ExprId, f: impl FnOnce(&Hir, &Self) -> T) -> T {
|
fn with_expr<T>(&self, id: ExprId, f: impl FnOnce(&Hir, &Self) -> T) -> T {
|
||||||
let idx = usize::from(id);
|
|
||||||
f(&self.hirs[idx].borrow(), self)
|
|
||||||
}
|
|
||||||
fn with_expr_mut<T>(&mut self, id: ExprId, f: impl FnOnce(&mut Hir, &mut Self) -> T) -> T {
|
|
||||||
let idx = usize::from(id);
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
let idx = id.raw();
|
||||||
|
f(&self.irs[idx].borrow().unwrap_hir_ref_unchecked(), self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn with_expr_mut<T>(&mut self, id: &ExprId, f: impl FnOnce(&mut Hir, &mut Self) -> T) -> T {
|
||||||
|
unsafe {
|
||||||
|
let idx = id.clone().raw();
|
||||||
let self_mut = &mut *(self as *mut Self);
|
let self_mut = &mut *(self as *mut Self);
|
||||||
f(&mut self.hirs.get_unchecked_mut(idx).borrow_mut(), self_mut)
|
f(
|
||||||
|
&mut self
|
||||||
|
.irs
|
||||||
|
.get_unchecked_mut(idx)
|
||||||
|
.borrow_mut()
|
||||||
|
.unwrap_hir_mut_unchecked(),
|
||||||
|
self_mut,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResolveContext for Context {
|
impl ResolveContext for Context {
|
||||||
fn lookup(&self, name: &str) -> nixjit_lir::LookupResult {
|
fn lookup(&self, name: &str) -> LookupResult {
|
||||||
|
let mut arg_idx = 0;
|
||||||
for scope in self.scopes.iter().rev() {
|
for scope in self.scopes.iter().rev() {
|
||||||
if let Some(val) = scope.get(name) {
|
match scope {
|
||||||
return val.clone();
|
Scope::Let(scope) => {
|
||||||
|
if let Some(expr) = scope.get(name) {
|
||||||
|
return LookupResult::Expr(unsafe { expr.clone() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Scope::Arg(ident) => {
|
||||||
|
if ident.as_deref() == Some(name) {
|
||||||
|
return LookupResult::Arg(unsafe { ArgIdx::from(arg_idx) });
|
||||||
|
}
|
||||||
|
arg_idx += 1;
|
||||||
|
}
|
||||||
|
Scope::With => return LookupResult::Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LookupResult::NotFound
|
LookupResult::NotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_dep(&mut self, expr: ExprId, dep: ExprId) {
|
fn new_dep(&mut self, expr: &ExprId, dep: ExprId) {
|
||||||
let expr = *self.nodes.get(usize::from(expr)).unwrap();
|
unsafe {
|
||||||
let dep = *self.nodes.get(usize::from(dep)).unwrap();
|
let expr = expr.clone().raw();
|
||||||
|
let dep = dep.raw();
|
||||||
|
let expr = *self.nodes.get_unchecked(expr);
|
||||||
|
let dep = *self.nodes.get_unchecked(dep);
|
||||||
self.graph.add_edge(expr, dep, ());
|
self.graph.add_edge(expr, dep, ());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve(&mut self, expr: ExprId) -> Result<()> {
|
|
||||||
let idx = usize::from(expr);
|
|
||||||
if self.resolved[idx] {
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let hir = self.hirs[idx].replace(nixjit_hir::Hir::Const(nixjit_ir::Const::from(false)));
|
fn resolve(&mut self, expr: &ExprId) -> Result<()> {
|
||||||
let lir = hir.resolve(self)?;
|
unsafe {
|
||||||
self.lirs[idx].write(lir);
|
let idx = expr.clone().raw();
|
||||||
self.resolved[idx] = true;
|
let self_mut = &mut *(self as *mut Self);
|
||||||
|
replace_with_and_return(
|
||||||
|
&mut *self.irs.get_unchecked(idx).borrow_mut(),
|
||||||
|
|| {
|
||||||
|
Ir::Hir(Hir::Const(Const {
|
||||||
|
val: nixjit_value::Const::Null,
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
|ir| {
|
||||||
|
let hir = ir.unwrap_hir_unchecked();
|
||||||
|
match hir.resolve(self_mut) {
|
||||||
|
Ok(lir) => (Ok(()), Ir::Lir(lir)),
|
||||||
|
Err(err) => (
|
||||||
|
Err(err),
|
||||||
|
Ir::Hir(Hir::Const(Const {
|
||||||
|
val: nixjit_value::Const::Null,
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_func(&mut self, body: ExprId, param: Param) {
|
fn new_func(&mut self, body: &ExprId, param: Param) {
|
||||||
self.funcs.insert(body, param);
|
self.funcs.insert(unsafe { body.clone() }, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_let_env<'a, T>(
|
fn with_let_env<'a, T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
bindings: impl IntoIterator<Item = (&'a String, &'a ExprId)>,
|
bindings: impl Iterator<Item = (&'a String, &'a ExprId)>,
|
||||||
f: impl FnOnce(&mut Self) -> T,
|
f: impl FnOnce(&mut Self) -> T,
|
||||||
) -> T {
|
) -> T {
|
||||||
let mut scope = HashMap::new();
|
let mut scope = HashMap::new();
|
||||||
for (name, expr) in bindings {
|
for (name, expr) in bindings {
|
||||||
scope.insert(name.clone(), LookupResult::Expr(*expr));
|
scope.insert(name.clone(), unsafe { expr.clone() });
|
||||||
}
|
}
|
||||||
self.scopes.push(scope);
|
self.scopes.push(Scope::Let(scope));
|
||||||
let res = f(self);
|
let res = f(self);
|
||||||
self.scopes.pop();
|
self.scopes.pop();
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_with_env<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> (bool, T) {
|
fn with_with_env<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> (bool, T) {
|
||||||
self.scopes.push(HashMap::new());
|
self.scopes.push(Scope::With);
|
||||||
let res = f(self);
|
let res = f(self);
|
||||||
self.scopes.pop();
|
self.scopes.pop();
|
||||||
(true, res)
|
(true, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_param_env<'a, T>(
|
fn with_param_env<T>(&mut self, ident: Option<String>, f: impl FnOnce(&mut Self) -> T) -> T {
|
||||||
&mut self,
|
self.scopes.push(Scope::Arg(ident));
|
||||||
ident: Option<&'a str>,
|
self.args_count += 1;
|
||||||
f: impl FnOnce(&mut Self) -> T,
|
|
||||||
) -> T {
|
|
||||||
let mut scope = HashMap::new();
|
|
||||||
if let Some(ident) = ident {
|
|
||||||
scope.insert(ident.to_string(), LookupResult::Arg(ArgIdx::from(0)));
|
|
||||||
}
|
|
||||||
self.scopes.push(scope);
|
|
||||||
let res = f(self);
|
let res = f(self);
|
||||||
|
self.args_count -= 1;
|
||||||
self.scopes.pop();
|
self.scopes.pop();
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EvalContext for Context {
|
impl EvalContext for Context {
|
||||||
fn eval(&mut self, expr: ExprId) -> Result<nixjit_eval::Value<Self>> {
|
fn eval(&mut self, expr: &ExprId) -> Result<nixjit_eval::Value> {
|
||||||
let idx = usize::from(expr);
|
let idx = unsafe { expr.clone().raw() };
|
||||||
let lir = unsafe { &*(self.lirs[idx].assume_init_ref() as *const Lir) };
|
let lir = unsafe {
|
||||||
|
&*(self
|
||||||
|
.irs
|
||||||
|
.get_unchecked(idx)
|
||||||
|
.borrow()
|
||||||
|
.unwrap_lir_ref_unchecked() as *const Lir)
|
||||||
|
};
|
||||||
|
println!("{:#?}", self.irs);
|
||||||
lir.eval(self)
|
lir.eval(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_frame(&mut self) -> Vec<nixjit_eval::Value<Self>> {
|
fn pop_frame(&mut self) -> Vec<nixjit_eval::Value> {
|
||||||
self.stack.pop().unwrap().values
|
self.stack.pop().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lookup_with<'a>(&'a self, ident: &str) -> Option<&'a nixjit_eval::Value<Self>> {
|
fn lookup_with<'a>(&'a self, ident: &str) -> Option<&'a nixjit_eval::Value> {
|
||||||
for scope in self.with_scopes.iter().rev() {
|
for scope in self.with_scopes.iter().rev() {
|
||||||
if let Some(val) = scope.get(ident) {
|
if let Some(val) = scope.get(ident) {
|
||||||
return Some(val);
|
return Some(val);
|
||||||
@@ -169,13 +333,17 @@ impl EvalContext for Context {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lookup_arg<'a>(&'a self, offset: usize) -> Option<&'a Value<Self>> {
|
fn lookup_arg<'a>(&'a self, idx: ArgIdx) -> &'a Value {
|
||||||
self.stack.last().and_then(|frame| frame.values.get(offset))
|
unsafe {
|
||||||
|
let values = self.stack.last().unwrap_unchecked();
|
||||||
|
dbg!(values, idx);
|
||||||
|
&values[values.len() - idx.raw() - 1]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_with_env<T>(
|
fn with_with_env<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
namespace: std::rc::Rc<HashMap<String, nixjit_eval::Value<Self>>>,
|
namespace: std::rc::Rc<HashMap<String, nixjit_eval::Value>>,
|
||||||
f: impl FnOnce(&mut Self) -> T,
|
f: impl FnOnce(&mut Self) -> T,
|
||||||
) -> T {
|
) -> T {
|
||||||
self.with_scopes.push(namespace);
|
self.with_scopes.push(namespace);
|
||||||
@@ -186,53 +354,33 @@ impl EvalContext for Context {
|
|||||||
|
|
||||||
fn with_args_env<T>(
|
fn with_args_env<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
args: Vec<nixjit_eval::Value<Self>>,
|
args: Vec<nixjit_eval::Value>,
|
||||||
f: impl FnOnce(&mut Self) -> T,
|
f: impl FnOnce(&mut Self) -> T,
|
||||||
) -> (Vec<nixjit_eval::Value<Self>>, T) {
|
) -> (Vec<Value>, T) {
|
||||||
self.stack.push(Frame {
|
self.stack.push(args);
|
||||||
left: args.len(),
|
|
||||||
values: args,
|
|
||||||
});
|
|
||||||
let res = f(self);
|
let res = f(self);
|
||||||
(self.stack.pop().unwrap().values, res)
|
let frame = self.stack.pop().unwrap();
|
||||||
|
(frame, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consume_arg(&mut self, func: ExprId) -> Result<bool> {
|
fn call_primop(&mut self, id: nixjit_ir::PrimOpId, args: Vec<Value>) -> Result<Value> {
|
||||||
let Some(frame) = self.stack.last_mut() else {
|
unsafe {
|
||||||
return Ok(false);
|
(self.primops.get_unchecked(id.raw()))(self, args)
|
||||||
};
|
|
||||||
if frame.left == 0 {
|
|
||||||
return Ok(false);
|
|
||||||
}
|
}
|
||||||
frame.left -= 1;
|
|
||||||
let param = self.funcs.get(&func).unwrap();
|
|
||||||
if let Some(required) = ¶m.required {
|
|
||||||
let attrs = frame.values[frame.values.len() - frame.left - 1]
|
|
||||||
.as_ref()
|
|
||||||
.try_unwrap_attr_set()
|
|
||||||
.map_err(|_| Error::EvalError(format!("expected a set but found ...")))?;
|
|
||||||
if required.iter().any(|attr| attrs.get(attr).is_none())
|
|
||||||
|| param.allowed.as_ref().map_or(false, |allowed| {
|
|
||||||
attrs.iter().any(|(attr, _)| allowed.get(attr).is_none())
|
|
||||||
})
|
|
||||||
{
|
|
||||||
return Err(Error::EvalError(format!("TODO")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITContext for Context {
|
impl JITContext for Context {
|
||||||
fn lookup_arg(&self, offset: usize) -> &nixjit_eval::Value<Self> {
|
fn lookup_arg(&self, offset: usize) -> &nixjit_eval::Value {
|
||||||
&self.stack.last().unwrap().values[offset]
|
let values = self.stack.last().unwrap();
|
||||||
|
&values[values.len() - offset - 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lookup_stack(&self, offset: usize) -> &nixjit_eval::Value<Self> {
|
fn lookup_stack(&self, offset: usize) -> &nixjit_eval::Value {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_with(&mut self, namespace: std::rc::Rc<HashMap<String, nixjit_eval::Value<Self>>>) {
|
fn enter_with(&mut self, namespace: std::rc::Rc<HashMap<String, nixjit_eval::Value>>) {
|
||||||
self.with_scopes.push(namespace);
|
self.with_scopes.push(namespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,3 +388,5 @@ impl JITContext for Context {
|
|||||||
self.with_scopes.pop();
|
self.with_scopes.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BuiltinsContext for Context {}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::rc::Rc;
|
|||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
use nixjit_error::{Error, Result};
|
use nixjit_error::{Error, Result};
|
||||||
use nixjit_ir::{self as ir, ExprId};
|
use nixjit_ir::{self as ir, ArgIdx, ExprId, PrimOpId};
|
||||||
use nixjit_lir as lir;
|
use nixjit_lir as lir;
|
||||||
use nixjit_value::{Const, Symbol};
|
use nixjit_value::{Const, Symbol};
|
||||||
|
|
||||||
@@ -12,35 +12,31 @@ pub use crate::value::*;
|
|||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
pub trait EvalContext: Sized {
|
pub trait EvalContext: Sized {
|
||||||
fn eval(&mut self, expr: ExprId) -> Result<Value<Self>>;
|
fn eval(&mut self, expr: &ExprId) -> Result<Value>;
|
||||||
fn with_with_env<T>(
|
fn with_with_env<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
namespace: Rc<HashMap<String, Value<Self>>>,
|
namespace: Rc<HashMap<String, Value>>,
|
||||||
f: impl FnOnce(&mut Self) -> T,
|
f: impl FnOnce(&mut Self) -> T,
|
||||||
) -> T;
|
) -> T;
|
||||||
fn with_args_env<T>(
|
fn with_args_env<T>(&mut self, args: Vec<Value>, f: impl FnOnce(&mut Self) -> T) -> (Vec<Value>, T);
|
||||||
&mut self,
|
fn lookup_with<'a>(&'a self, ident: &str) -> Option<&'a Value>;
|
||||||
args: Vec<Value<Self>>,
|
fn lookup_arg<'a>(&'a self, idx: ArgIdx) -> &'a Value;
|
||||||
f: impl FnOnce(&mut Self) -> T,
|
fn pop_frame(&mut self) -> Vec<Value>;
|
||||||
) -> (Vec<Value<Self>>, T);
|
fn call_primop(&mut self, id: PrimOpId, args: Vec<Value>) -> Result<Value>;
|
||||||
fn lookup_with<'a>(&'a self, ident: &str) -> Option<&'a Value<Self>>;
|
|
||||||
fn lookup_arg<'a>(&'a self, offset: usize) -> Option<&'a Value<Self>>;
|
|
||||||
fn pop_frame(&mut self) -> Vec<Value<Self>>;
|
|
||||||
fn consume_arg(&mut self, func: ExprId) -> Result<bool>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Evaluate<Ctx: EvalContext> {
|
pub trait Evaluate<Ctx: EvalContext> {
|
||||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>>;
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ExprId {
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ExprId {
|
||||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||||
ctx.eval(*self)
|
ctx.eval(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Evaluate<Ctx> for lir::Lir {
|
impl<Ctx: EvalContext> Evaluate<Ctx> for lir::Lir {
|
||||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||||
use lir::Lir::*;
|
use lir::Lir::*;
|
||||||
match self {
|
match self {
|
||||||
AttrSet(x) => x.eval(ctx),
|
AttrSet(x) => x.eval(ctx),
|
||||||
@@ -58,26 +54,16 @@ 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),
|
||||||
ExprRef(expr) => expr.eval(ctx),
|
ExprRef(expr) => ctx.eval(expr),
|
||||||
&FuncRef(func) => {
|
FuncRef(func) => Ok(Value::Func(unsafe { func.clone() })),
|
||||||
if ctx.consume_arg(func)? {
|
&ArgRef(idx) => Ok(ctx.lookup_arg(idx).clone()),
|
||||||
ctx.eval(func)
|
&PrimOp(primop) => Ok(Value::PrimOp(primop)),
|
||||||
} else {
|
|
||||||
Ok(Value::Func(func))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
&ArgRef(arg) => {
|
|
||||||
let idx: usize = unsafe { core::mem::transmute(arg) };
|
|
||||||
ctx.lookup_arg(idx)
|
|
||||||
.cloned()
|
|
||||||
.ok_or_else(|| Error::EvalError("argument not found".to_string()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::AttrSet {
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::AttrSet {
|
||||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||||
let mut attrs = AttrSet::new(
|
let mut attrs = AttrSet::new(
|
||||||
self.stcs
|
self.stcs
|
||||||
.iter()
|
.iter()
|
||||||
@@ -99,7 +85,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::AttrSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::List {
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::List {
|
||||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||||
let items = self
|
let items = self
|
||||||
.items
|
.items
|
||||||
.iter()
|
.iter()
|
||||||
@@ -111,7 +97,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::List {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::HasAttr {
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::HasAttr {
|
||||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||||
use ir::Attr::*;
|
use ir::Attr::*;
|
||||||
let mut val = self.lhs.eval(ctx)?;
|
let mut val = self.lhs.eval(ctx)?;
|
||||||
val.has_attr(self.rhs.iter().map(|attr| {
|
val.has_attr(self.rhs.iter().map(|attr| {
|
||||||
@@ -130,7 +116,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::HasAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::BinOp {
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::BinOp {
|
||||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||||
use ir::BinOpKind::*;
|
use ir::BinOpKind::*;
|
||||||
let mut lhs = self.lhs.eval(ctx)?;
|
let mut lhs = self.lhs.eval(ctx)?;
|
||||||
let mut rhs = self.rhs.eval(ctx)?;
|
let mut rhs = self.rhs.eval(ctx)?;
|
||||||
@@ -169,9 +155,9 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::BinOp {
|
|||||||
}
|
}
|
||||||
Con => lhs.concat(rhs),
|
Con => lhs.concat(rhs),
|
||||||
Upd => lhs.update(rhs),
|
Upd => lhs.update(rhs),
|
||||||
PipeL => lhs.call(vec![rhs], ctx)?,
|
PipeL => lhs.call(core::iter::once(Ok(rhs)), ctx)?,
|
||||||
PipeR => {
|
PipeR => {
|
||||||
rhs.call(vec![lhs], ctx)?;
|
rhs.call(core::iter::once(Ok(lhs)), ctx)?;
|
||||||
lhs = rhs;
|
lhs = rhs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,7 +166,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::BinOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::UnOp {
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::UnOp {
|
||||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||||
use ir::UnOpKind::*;
|
use ir::UnOpKind::*;
|
||||||
let mut rhs = self.rhs.eval(ctx)?;
|
let mut rhs = self.rhs.eval(ctx)?;
|
||||||
match self.kind {
|
match self.kind {
|
||||||
@@ -196,7 +182,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::UnOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Select {
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Select {
|
||||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||||
use ir::Attr::*;
|
use ir::Attr::*;
|
||||||
let mut val = self.expr.eval(ctx)?;
|
let mut val = self.expr.eval(ctx)?;
|
||||||
if let Some(default) = &self.default {
|
if let Some(default) = &self.default {
|
||||||
@@ -232,7 +218,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Select {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::If {
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::If {
|
||||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||||
// TODO: Error Handling
|
// TODO: Error Handling
|
||||||
let cond = self.cond.eval(ctx)?;
|
let cond = self.cond.eval(ctx)?;
|
||||||
let cond = cond
|
let cond = cond
|
||||||
@@ -248,21 +234,22 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::If {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Call {
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Call {
|
||||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||||
let mut func = self.func.eval(ctx)?;
|
let mut func = self.func.eval(ctx)?;
|
||||||
|
// FIXME: ?
|
||||||
|
let ctx_mut = unsafe { &mut *(ctx as *mut Ctx) };
|
||||||
func.call(
|
func.call(
|
||||||
self.args
|
self.args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|arg| arg.eval(ctx))
|
.map(|arg| arg.eval(ctx)),
|
||||||
.collect::<Result<_>>()?,
|
ctx_mut,
|
||||||
ctx,
|
|
||||||
)?;
|
)?;
|
||||||
Ok(func.ok().unwrap())
|
Ok(func.ok().unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::With {
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::With {
|
||||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||||
let namespace = self.namespace.eval(ctx)?;
|
let namespace = self.namespace.eval(ctx)?;
|
||||||
ctx.with_with_env(
|
ctx.with_with_env(
|
||||||
namespace
|
namespace
|
||||||
@@ -275,7 +262,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::With {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Assert {
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Assert {
|
||||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||||
let cond = self.assertion.eval(ctx)?;
|
let cond = self.assertion.eval(ctx)?;
|
||||||
let cond = cond
|
let cond = cond
|
||||||
.try_unwrap_bool()
|
.try_unwrap_bool()
|
||||||
@@ -289,7 +276,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Assert {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::ConcatStrings {
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::ConcatStrings {
|
||||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||||
let mut parts = self
|
let mut parts = self
|
||||||
.parts
|
.parts
|
||||||
.iter()
|
.iter()
|
||||||
@@ -310,14 +297,14 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::ConcatStrings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Str {
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Str {
|
||||||
fn eval(&self, _: &mut Ctx) -> Result<Value<Ctx>> {
|
fn eval(&self, _: &mut Ctx) -> Result<Value> {
|
||||||
let result = Value::String(self.val.clone()).ok();
|
let result = Value::String(self.val.clone()).ok();
|
||||||
Ok(result.unwrap())
|
Ok(result.unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Const {
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Const {
|
||||||
fn eval(&self, _: &mut Ctx) -> Result<Value<Ctx>> {
|
fn eval(&self, _: &mut Ctx) -> Result<Value> {
|
||||||
let result = match self.val {
|
let result = match self.val {
|
||||||
Const::Null => Value::Null,
|
Const::Null => Value::Null,
|
||||||
Const::Int(x) => Value::Int(x),
|
Const::Int(x) => Value::Int(x),
|
||||||
@@ -330,7 +317,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Var {
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Var {
|
||||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||||
ctx.lookup_with(&self.sym)
|
ctx.lookup_with(&self.sym)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
Error::EvalError(format!(
|
Error::EvalError(format!(
|
||||||
@@ -343,7 +330,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Var {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Path {
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Path {
|
||||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
use std::rc::Rc;
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use derive_more::Constructor;
|
use derive_more::Constructor;
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
@@ -15,11 +15,11 @@ use crate::EvalContext;
|
|||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Constructor, PartialEq)]
|
#[derive(Constructor, PartialEq)]
|
||||||
pub struct AttrSet<Ctx: EvalContext> {
|
pub struct AttrSet {
|
||||||
data: HashMap<String, Value<Ctx>>,
|
data: HashMap<String, Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Debug for AttrSet<Ctx> {
|
impl Debug for AttrSet {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
write!(f, "{{ ")?;
|
write!(f, "{{ ")?;
|
||||||
@@ -34,7 +34,7 @@ impl<Ctx: EvalContext> Debug for AttrSet<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Clone for AttrSet<Ctx> {
|
impl Clone for AttrSet {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
AttrSet {
|
AttrSet {
|
||||||
data: self.data.clone(),
|
data: self.data.clone(),
|
||||||
@@ -42,31 +42,31 @@ impl<Ctx: EvalContext> Clone for AttrSet<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> From<HashMap<String, Value<Ctx>>> for AttrSet<Ctx> {
|
impl From<HashMap<String, Value>> for AttrSet {
|
||||||
fn from(data: HashMap<String, Value<Ctx>>) -> Self {
|
fn from(data: HashMap<String, Value>) -> Self {
|
||||||
Self { data }
|
Self { data }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Deref for AttrSet<Ctx> {
|
impl Deref for AttrSet {
|
||||||
type Target = HashMap<String, Value<Ctx>>;
|
type Target = HashMap<String, Value>;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.data
|
&self.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> AttrSet<Ctx> {
|
impl AttrSet {
|
||||||
pub fn with_capacity(cap: usize) -> Self {
|
pub fn with_capacity(cap: usize) -> Self {
|
||||||
AttrSet {
|
AttrSet {
|
||||||
data: HashMap::with_capacity(cap),
|
data: HashMap::with_capacity(cap),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_attr_force(&mut self, sym: String, val: Value<Ctx>) {
|
pub fn push_attr_force(&mut self, sym: String, val: Value) {
|
||||||
self.data.insert(sym, val);
|
self.data.insert(sym, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_attr(&mut self, sym: String, val: Value<Ctx>) {
|
pub fn push_attr(&mut self, sym: String, val: Value) {
|
||||||
if self.data.get(&sym).is_some() {
|
if self.data.get(&sym).is_some() {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,7 @@ impl<Ctx: EvalContext> AttrSet<Ctx> {
|
|||||||
pub fn select(
|
pub fn select(
|
||||||
&self,
|
&self,
|
||||||
mut path: impl DoubleEndedIterator<Item = Result<String>>,
|
mut path: impl DoubleEndedIterator<Item = Result<String>>,
|
||||||
) -> Result<Value<Ctx>> {
|
) -> Result<Value> {
|
||||||
let mut data = &self.data;
|
let mut data = &self.data;
|
||||||
let last = path.nth_back(0).unwrap();
|
let last = path.nth_back(0).unwrap();
|
||||||
for item in path {
|
for item in path {
|
||||||
@@ -116,15 +116,15 @@ impl<Ctx: EvalContext> AttrSet<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_inner(&self) -> &HashMap<String, Value<Ctx>> {
|
pub fn as_inner(&self) -> &HashMap<String, Value> {
|
||||||
&self.data
|
&self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_inner(self: Rc<Self>) -> Rc<HashMap<String, Value<Ctx>>> {
|
pub fn into_inner(self: Rc<Self>) -> Rc<HashMap<String, Value>> {
|
||||||
unsafe { core::mem::transmute(self) }
|
unsafe { core::mem::transmute(self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_inner(data: HashMap<String, Value<Ctx>>) -> Self {
|
pub fn from_inner(data: HashMap<String, Value>) -> Self {
|
||||||
Self { data }
|
Self { data }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,11 +137,11 @@ impl<Ctx: EvalContext> AttrSet<Ctx> {
|
|||||||
.all(|((k1, v1), (k2, v2))| k1 == k2 && v1.eq_impl(v2))
|
.all(|((k1, v1), (k2, v2))| k1 == k2 && v1.eq_impl(v2))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_public(&self, ctx: &Ctx, seen: &mut HashSet<Value<Ctx>>) -> p::Value {
|
pub fn to_public(&self, seen: &mut HashSet<Value>) -> p::Value {
|
||||||
p::Value::AttrSet(p::AttrSet::new(
|
p::Value::AttrSet(p::AttrSet::new(
|
||||||
self.data
|
self.data
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(sym, value)| (sym.as_str().into(), value.to_public(ctx, seen)))
|
.map(|(sym, value)| (sym.as_str().into(), value.to_public(seen)))
|
||||||
.collect(),
|
.collect(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,39 +9,59 @@ use super::Value;
|
|||||||
use crate::EvalContext;
|
use crate::EvalContext;
|
||||||
|
|
||||||
#[derive(Debug, Constructor)]
|
#[derive(Debug, Constructor)]
|
||||||
pub struct FuncApp<Ctx: EvalContext> {
|
pub struct FuncApp {
|
||||||
pub body: ExprId,
|
pub body: ExprId,
|
||||||
pub args: Vec<Value<Ctx>>,
|
pub args: Vec<Value>,
|
||||||
pub frame: Vec<Value<Ctx>>,
|
pub frame: Vec<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Clone for FuncApp<Ctx> {
|
impl Clone for FuncApp {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
body: self.body,
|
body: unsafe { self.body.clone() },
|
||||||
args: self.args.clone(),
|
args: self.args.clone(),
|
||||||
frame: self.frame.clone(),
|
frame: self.frame.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> FuncApp<Ctx> {
|
impl FuncApp {
|
||||||
pub fn call(
|
pub fn call<Ctx: EvalContext>(
|
||||||
self: &mut Rc<Self>,
|
self: &mut Rc<Self>,
|
||||||
new_args: Vec<Value<Ctx>>,
|
mut iter: impl Iterator<Item = Result<Value>> + ExactSizeIterator,
|
||||||
ctx: &mut Ctx,
|
ctx: &mut Ctx,
|
||||||
) -> Result<Value<Ctx>> {
|
) -> Result<Value> {
|
||||||
let FuncApp { body: expr, args, frame } = Rc::make_mut(self);
|
let FuncApp {
|
||||||
args.extend(new_args);
|
body: expr,
|
||||||
let (args, ret) = ctx.with_args_env(core::mem::take(args), |ctx| ctx.eval(*expr));
|
args,
|
||||||
let mut ret = ret?;
|
frame,
|
||||||
if let Value::Func(expr) = ret {
|
} = Rc::make_mut(self);
|
||||||
let frame = ctx.pop_frame();
|
let mut val;
|
||||||
ret = Value::FuncApp(FuncApp::new(expr, args, frame).into());
|
let mut args = core::mem::take(args);
|
||||||
} else if let Value::FuncApp(func) = &mut ret {
|
args.push(iter.next().unwrap()?);
|
||||||
todo!();
|
let (ret_args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(expr));
|
||||||
let func = Rc::make_mut(func);
|
args = ret_args;
|
||||||
|
val = ret?;
|
||||||
|
loop {
|
||||||
|
if !matches!(val, Value::Func(_) | Value::FuncApp(_)) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
ret.ok()
|
let Some(arg) = iter.next() else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
args.push(arg?);
|
||||||
|
if let Value::Func(expr) = val {
|
||||||
|
let (ret_args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(&expr));
|
||||||
|
args = ret_args;
|
||||||
|
val = ret?;
|
||||||
|
} else if let Value::FuncApp(func) = val {
|
||||||
|
let mut func = Rc::unwrap_or_clone(func);
|
||||||
|
func.args.push(args.pop().unwrap());
|
||||||
|
let (ret_args, ret) = ctx.with_args_env(func.args, |ctx| ctx.eval(&func.body));
|
||||||
|
args = ret_args;
|
||||||
|
val = ret?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val.ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use std::ops::Deref;
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
|
|
||||||
@@ -10,11 +10,11 @@ use super::Value;
|
|||||||
use crate::EvalContext;
|
use crate::EvalContext;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct List<Ctx: EvalContext> {
|
pub struct List {
|
||||||
data: Vec<Value<Ctx>>,
|
data: Vec<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Debug for List<Ctx> {
|
impl Debug for List {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "[ ")?;
|
write!(f, "[ ")?;
|
||||||
for v in self.data.iter() {
|
for v in self.data.iter() {
|
||||||
@@ -24,7 +24,7 @@ impl<Ctx: EvalContext> Debug for List<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Clone for List<Ctx> {
|
impl Clone for List {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
data: self.data.clone(),
|
data: self.data.clone(),
|
||||||
@@ -32,20 +32,20 @@ impl<Ctx: EvalContext> Clone for List<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext, T: Into<Vec<Value<Ctx>>>> From<T> for List<Ctx> {
|
impl<T: Into<Vec<Value>>> From<T> for List {
|
||||||
fn from(value: T) -> Self {
|
fn from(value: T) -> Self {
|
||||||
Self { data: value.into() }
|
Self { data: value.into() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Deref for List<Ctx> {
|
impl Deref for List {
|
||||||
type Target = [Value<Ctx>];
|
type Target = [Value];
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.data
|
&self.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> List<Ctx> {
|
impl List {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
List { data: Vec::new() }
|
List { data: Vec::new() }
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@ impl<Ctx: EvalContext> List<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, elem: Value<Ctx>) {
|
pub fn push(&mut self, elem: Value) {
|
||||||
self.data.push(elem);
|
self.data.push(elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ impl<Ctx: EvalContext> List<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_inner(self) -> Vec<Value<Ctx>> {
|
pub fn into_inner(self) -> Vec<Value> {
|
||||||
self.data
|
self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,11 +75,11 @@ impl<Ctx: EvalContext> List<Ctx> {
|
|||||||
&& core::iter::zip(self.iter(), other.iter()).all(|(a, b)| a.eq_impl(b))
|
&& core::iter::zip(self.iter(), other.iter()).all(|(a, b)| a.eq_impl(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_public(&self, engine: &Ctx, seen: &mut HashSet<Value<Ctx>>) -> PubValue {
|
pub fn to_public(&self, seen: &mut HashSet<Value>) -> PubValue {
|
||||||
PubValue::List(PubList::new(
|
PubValue::List(PubList::new(
|
||||||
self.data
|
self.data
|
||||||
.iter()
|
.iter()
|
||||||
.map(|value| value.clone().to_public(engine, seen))
|
.map(|value| value.clone().to_public(seen))
|
||||||
.collect(),
|
.collect(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
|
use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::process::abort;
|
use std::process::abort;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::fmt::{write, Debug};
|
|
||||||
|
|
||||||
use derive_more::TryUnwrap;
|
use derive_more::TryUnwrap;
|
||||||
use derive_more::{IsVariant, Unwrap};
|
use derive_more::{IsVariant, Unwrap};
|
||||||
use func::FuncApp;
|
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use nixjit_ir::ExprId;
|
use nixjit_ir::{ExprId, PrimOp};
|
||||||
use replace_with::{replace_with_and_return, replace_with_or_abort};
|
use replace_with::replace_with_and_return;
|
||||||
|
|
||||||
use nixjit_error::{Error, Result};
|
use nixjit_error::{Error, Result};
|
||||||
use nixjit_value::Const;
|
use nixjit_value::Const;
|
||||||
@@ -23,28 +22,29 @@ mod primop;
|
|||||||
mod string;
|
mod string;
|
||||||
|
|
||||||
pub use attrset::*;
|
pub use attrset::*;
|
||||||
|
pub use func::*;
|
||||||
pub use list::List;
|
pub use list::List;
|
||||||
pub use primop::*;
|
pub use primop::*;
|
||||||
|
|
||||||
#[repr(C, u64)]
|
#[repr(C, u64)]
|
||||||
#[derive(IsVariant, TryUnwrap, Unwrap)]
|
#[derive(IsVariant, TryUnwrap, Unwrap)]
|
||||||
pub enum Value<Ctx: EvalContext> {
|
pub enum Value {
|
||||||
Int(i64),
|
Int(i64),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
String(String),
|
String(String),
|
||||||
Null,
|
Null,
|
||||||
Thunk(usize),
|
Thunk(usize),
|
||||||
AttrSet(Rc<AttrSet<Ctx>>),
|
AttrSet(Rc<AttrSet>),
|
||||||
List(Rc<List<Ctx>>),
|
List(Rc<List>),
|
||||||
Catchable(String),
|
Catchable(String),
|
||||||
PrimOp(Rc<PrimOp<Ctx>>),
|
PrimOp(PrimOp),
|
||||||
PrimOpApp(Rc<PrimOpApp<Ctx>>),
|
PrimOpApp(Rc<PrimOpApp>),
|
||||||
Func(ExprId),
|
Func(ExprId),
|
||||||
FuncApp(Rc<FuncApp<Ctx>>),
|
FuncApp(Rc<FuncApp>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Debug for Value<Ctx> {
|
impl Debug for Value {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
match self {
|
match self {
|
||||||
@@ -65,28 +65,28 @@ impl<Ctx: EvalContext> Debug for Value<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Clone for Value<Ctx> {
|
impl Clone for Value {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
match self {
|
match self {
|
||||||
AttrSet(attrs) => AttrSet(attrs.clone()),
|
AttrSet(attrs) => AttrSet(attrs.clone()),
|
||||||
List(list) => List(list.clone()),
|
List(list) => List(list.clone()),
|
||||||
Catchable(catchable) => Catchable(catchable.clone()),
|
Catchable(catchable) => Catchable(catchable.clone()),
|
||||||
Int(x) => Int(*x),
|
&Int(x) => Int(x),
|
||||||
Float(x) => Float(*x),
|
&Float(x) => Float(x),
|
||||||
Bool(x) => Bool(*x),
|
&Bool(x) => Bool(x),
|
||||||
String(x) => String(x.clone()),
|
String(x) => String(x.clone()),
|
||||||
Null => Null,
|
Null => Null,
|
||||||
Thunk(expr) => Thunk(*expr),
|
&Thunk(expr) => Thunk(expr),
|
||||||
PrimOp(primop) => PrimOp(primop.clone()),
|
&PrimOp(primop) => PrimOp(primop),
|
||||||
PrimOpApp(primop) => PrimOpApp(primop.clone()),
|
PrimOpApp(primop) => PrimOpApp(primop.clone()),
|
||||||
Func(expr) => Func(*expr),
|
Func(expr) => Func(unsafe { expr.clone() }),
|
||||||
FuncApp(func) => FuncApp(func.clone()),
|
FuncApp(func) => FuncApp(func.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Hash for Value<Ctx> {
|
impl Hash for Value {
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
std::mem::discriminant(self).hash(state);
|
std::mem::discriminant(self).hash(state);
|
||||||
@@ -98,7 +98,7 @@ impl<Ctx: EvalContext> Hash for Value<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Value<Ctx> {
|
impl Value {
|
||||||
pub const INT: u64 = 0;
|
pub const INT: u64 = 0;
|
||||||
pub const FLOAT: u64 = 1;
|
pub const FLOAT: u64 = 1;
|
||||||
pub const BOOL: u64 = 2;
|
pub const BOOL: u64 = 2;
|
||||||
@@ -130,7 +130,7 @@ impl<Ctx: EvalContext> Value<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> PartialEq for Value<Ctx> {
|
impl PartialEq for Value {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
@@ -141,27 +141,27 @@ impl<Ctx: EvalContext> PartialEq for Value<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Eq for Value<Ctx> {}
|
impl Eq for Value {}
|
||||||
|
|
||||||
#[derive(IsVariant, TryUnwrap, Unwrap, Clone)]
|
#[derive(IsVariant, TryUnwrap, Unwrap, Clone)]
|
||||||
pub enum ValueAsRef<'v, Ctx: EvalContext> {
|
pub enum ValueAsRef<'v> {
|
||||||
Int(i64),
|
Int(i64),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
String(&'v String),
|
String(&'v String),
|
||||||
Null,
|
Null,
|
||||||
Thunk(usize),
|
Thunk(usize),
|
||||||
AttrSet(&'v AttrSet<Ctx>),
|
AttrSet(&'v AttrSet),
|
||||||
List(&'v List<Ctx>),
|
List(&'v List),
|
||||||
Catchable(&'v str),
|
Catchable(&'v str),
|
||||||
PrimOp(&'v PrimOp<Ctx>),
|
PrimOp(&'v PrimOp),
|
||||||
PartialPrimOp(&'v PrimOpApp<Ctx>),
|
PartialPrimOp(&'v PrimOpApp),
|
||||||
Func(ExprId),
|
Func(&'v ExprId),
|
||||||
PartialFunc(&'v FuncApp<Ctx>),
|
PartialFunc(&'v FuncApp),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Value<Ctx> {
|
impl Value {
|
||||||
pub fn as_ref(&self) -> ValueAsRef<'_, Ctx> {
|
pub fn as_ref(&self) -> ValueAsRef<'_> {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
use ValueAsRef as R;
|
use ValueAsRef as R;
|
||||||
match self {
|
match self {
|
||||||
@@ -176,12 +176,12 @@ impl<Ctx: EvalContext> Value<Ctx> {
|
|||||||
Catchable(x) => R::Catchable(x),
|
Catchable(x) => R::Catchable(x),
|
||||||
PrimOp(x) => R::PrimOp(x),
|
PrimOp(x) => R::PrimOp(x),
|
||||||
PrimOpApp(x) => R::PartialPrimOp(x),
|
PrimOpApp(x) => R::PartialPrimOp(x),
|
||||||
Func(x) => R::Func(*x),
|
Func(x) => R::Func(x),
|
||||||
FuncApp(x) => R::PartialFunc(x),
|
FuncApp(x) => R::PartialFunc(x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<Ctx: EvalContext> Value<Ctx> {
|
impl Value {
|
||||||
pub fn ok(self) -> Result<Self> {
|
pub fn ok(self) -> Result<Self> {
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
@@ -213,32 +213,58 @@ impl<Ctx: EvalContext> Value<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(&mut self, args: Vec<Self>, ctx: &mut Ctx) -> Result<()> {
|
pub fn call<Ctx: EvalContext>(&mut self, mut iter: impl Iterator<Item = Result<Value>> + ExactSizeIterator, ctx: &mut Ctx) -> Result<()> {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
for arg in args.iter() {
|
|
||||||
if matches!(arg, Value::Catchable(_)) {
|
|
||||||
*self = arg.clone();
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*self = match self {
|
*self = match self {
|
||||||
PrimOp(func) => func.call(args, ctx),
|
&mut PrimOp(func) => {
|
||||||
PrimOpApp(func) => func.call(args, ctx),
|
if iter.len() > func.arity {
|
||||||
FuncApp(func) => func.call(args, ctx),
|
todo!()
|
||||||
&mut Func(expr) => {
|
|
||||||
let (args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(expr));
|
|
||||||
let mut ret = ret?;
|
|
||||||
if let Value::Func(expr) = ret {
|
|
||||||
let frame = ctx.pop_frame();
|
|
||||||
ret = Value::FuncApp(self::FuncApp::new(expr, args, frame).into());
|
|
||||||
} else if let Value::FuncApp(func) = &mut ret {
|
|
||||||
todo!();
|
|
||||||
let func = Rc::make_mut(func);
|
|
||||||
}
|
}
|
||||||
ret.ok()
|
if func.arity > iter.len() {
|
||||||
|
Value::PrimOpApp(Rc::new(self::PrimOpApp::new(
|
||||||
|
func.name,
|
||||||
|
func.arity - iter.len(),
|
||||||
|
func.id,
|
||||||
|
iter.collect::<Result<_>>()?,
|
||||||
|
)))
|
||||||
|
.ok()
|
||||||
|
} else {
|
||||||
|
ctx.call_primop(func.id, iter.collect::<Result<_>>()?)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Func(expr) => {
|
||||||
|
let mut val;
|
||||||
|
let mut args = Vec::with_capacity(iter.len());
|
||||||
|
args.push(iter.next().unwrap()?);
|
||||||
|
let (ret_args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(expr));
|
||||||
|
args = ret_args;
|
||||||
|
val = ret?;
|
||||||
|
loop {
|
||||||
|
if !matches!(val, Value::Func(_) | Value::FuncApp(_)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let Some(arg) = iter.next() else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
args.push(arg?);
|
||||||
|
if let Value::Func(expr) = val {
|
||||||
|
let (ret_args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(&expr));
|
||||||
|
args = ret_args;
|
||||||
|
val = ret?;
|
||||||
|
} else if let Value::FuncApp(func) = val {
|
||||||
|
let mut func = Rc::unwrap_or_clone(func);
|
||||||
|
func.args.push(args.pop().unwrap());
|
||||||
|
let (ret_args, ret) = ctx.with_args_env(func.args, |ctx| ctx.eval(&func.body));
|
||||||
|
args = ret_args;
|
||||||
|
val = ret?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val.ok()
|
||||||
|
}
|
||||||
|
PrimOpApp(func) => func.call(iter.collect::<Result<_>>()?, ctx),
|
||||||
|
FuncApp(func) => func.call(iter, ctx),
|
||||||
Catchable(_) => return Ok(()),
|
Catchable(_) => return Ok(()),
|
||||||
other => todo!("{}", other.typename()),
|
_ => Err(Error::EvalError("attempt to call something which is not a function but ...".to_string()))
|
||||||
}?;
|
}?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -310,7 +336,10 @@ impl<Ctx: EvalContext> Value<Ctx> {
|
|||||||
|
|
||||||
pub fn add(&mut self, other: Self) -> Result<()> {
|
pub fn add(&mut self, other: Self) -> Result<()> {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
replace_with_and_return(self, || abort(), |a| {
|
replace_with_and_return(
|
||||||
|
self,
|
||||||
|
|| abort(),
|
||||||
|
|a| {
|
||||||
let val = match (a, other) {
|
let val = match (a, other) {
|
||||||
(Int(a), Int(b)) => Int(a + b),
|
(Int(a), Int(b)) => Int(a + b),
|
||||||
(Int(a), Float(b)) => Float(a as f64 + b),
|
(Int(a), Float(b)) => Float(a as f64 + b),
|
||||||
@@ -325,7 +354,8 @@ impl<Ctx: EvalContext> Value<Ctx> {
|
|||||||
_ => return (Err(Error::EvalError(format!(""))), Value::Null),
|
_ => return (Err(Error::EvalError(format!(""))), Value::Null),
|
||||||
};
|
};
|
||||||
(Ok(()), val)
|
(Ok(()), val)
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mul(&mut self, other: Self) {
|
pub fn mul(&mut self, other: Self) {
|
||||||
@@ -490,7 +520,7 @@ impl<Ctx: EvalContext> Value<Ctx> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_public(&self, ctx: &Ctx, seen: &mut HashSet<Value<Ctx>>) -> PubValue {
|
pub fn to_public(&self, seen: &mut HashSet<Value>) -> PubValue {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
if seen.contains(self) {
|
if seen.contains(self) {
|
||||||
return PubValue::Repeated;
|
return PubValue::Repeated;
|
||||||
@@ -498,11 +528,11 @@ impl<Ctx: EvalContext> Value<Ctx> {
|
|||||||
match self {
|
match self {
|
||||||
AttrSet(attrs) => {
|
AttrSet(attrs) => {
|
||||||
seen.insert(self.clone());
|
seen.insert(self.clone());
|
||||||
attrs.to_public(ctx, seen)
|
attrs.to_public(seen)
|
||||||
}
|
}
|
||||||
List(list) => {
|
List(list) => {
|
||||||
seen.insert(self.clone());
|
seen.insert(self.clone());
|
||||||
list.to_public(ctx, seen)
|
list.to_public(seen)
|
||||||
}
|
}
|
||||||
Catchable(catchable) => PubValue::Catchable(catchable.clone().into()),
|
Catchable(catchable) => PubValue::Catchable(catchable.clone().into()),
|
||||||
Int(x) => PubValue::Const(Const::Int(*x)),
|
Int(x) => PubValue::Const(Const::Int(*x)),
|
||||||
|
|||||||
@@ -3,67 +3,34 @@ use std::rc::Rc;
|
|||||||
use derive_more::Constructor;
|
use derive_more::Constructor;
|
||||||
|
|
||||||
use nixjit_error::Result;
|
use nixjit_error::Result;
|
||||||
|
use nixjit_ir::PrimOpId;
|
||||||
|
|
||||||
use super::Value;
|
use super::Value;
|
||||||
use crate::EvalContext;
|
use crate::EvalContext;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Constructor)]
|
#[derive(Debug, Clone, Constructor)]
|
||||||
pub struct PrimOp<Ctx: EvalContext> {
|
pub struct PrimOpApp {
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
arity: usize,
|
arity: usize,
|
||||||
func: fn(Vec<Value<Ctx>>, &Ctx) -> Result<Value<Ctx>>,
|
id: PrimOpId,
|
||||||
|
args: Vec<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: EvalContext> PrimOp<Ctx> {
|
impl PrimOpApp {
|
||||||
pub fn call(&self, args: Vec<Value<Ctx>>, ctx: &Ctx) -> Result<Value<Ctx>> {
|
pub fn call(
|
||||||
if args.len() > self.arity {
|
self: &mut Rc<Self>,
|
||||||
todo!()
|
args: Vec<Value>,
|
||||||
}
|
ctx: &mut impl EvalContext,
|
||||||
if self.arity > args.len() {
|
) -> Result<Value> {
|
||||||
Value::PrimOpApp(Rc::new(PrimOpApp {
|
|
||||||
name: self.name,
|
|
||||||
arity: self.arity - args.len(),
|
|
||||||
args,
|
|
||||||
func: self.func,
|
|
||||||
}))
|
|
||||||
.ok()
|
|
||||||
} else {
|
|
||||||
(self.func)(args, ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PrimOpApp<Ctx: EvalContext> {
|
|
||||||
pub name: &'static str,
|
|
||||||
arity: usize,
|
|
||||||
args: Vec<Value<Ctx>>,
|
|
||||||
func: fn(Vec<Value<Ctx>>, &Ctx) -> Result<Value<Ctx>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Ctx: EvalContext> Clone for PrimOpApp<Ctx> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
name: self.name,
|
|
||||||
arity: self.arity,
|
|
||||||
args: self.args.clone(),
|
|
||||||
func: self.func,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Ctx: EvalContext> PrimOpApp<Ctx> {
|
|
||||||
pub fn call(self: &mut Rc<Self>, args: Vec<Value<Ctx>>, ctx: &Ctx) -> Result<Value<Ctx>> {
|
|
||||||
if self.arity < args.len() {
|
if self.arity < args.len() {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
let func = self.func;
|
|
||||||
let Some(ret) = ({
|
let Some(ret) = ({
|
||||||
let self_mut = Rc::make_mut(self);
|
let self_mut = Rc::make_mut(self);
|
||||||
self_mut.arity -= args.len();
|
self_mut.arity -= args.len();
|
||||||
self_mut.args.extend(args);
|
self_mut.args.extend(args);
|
||||||
if self_mut.arity == 0 {
|
if self_mut.arity == 0 {
|
||||||
Some(func(std::mem::take(&mut self_mut.args), ctx))
|
Some(ctx.call_primop(self_mut.id, std::mem::take(&mut self_mut.args)))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -256,16 +256,43 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Lambda {
|
|||||||
let param = downgrade_param(self.param().unwrap(), ctx)?;
|
let param = downgrade_param(self.param().unwrap(), ctx)?;
|
||||||
let mut body = self.body().unwrap().downgrade(ctx)?;
|
let mut body = self.body().unwrap().downgrade(ctx)?;
|
||||||
|
|
||||||
|
let ident;
|
||||||
|
let required;
|
||||||
|
let allowed;
|
||||||
|
match param {
|
||||||
|
Param::Ident(id) => {
|
||||||
|
ident = Some(id);
|
||||||
|
required = None;
|
||||||
|
allowed = None;
|
||||||
|
}
|
||||||
|
Param::Formals {
|
||||||
|
formals,
|
||||||
|
ellipsis,
|
||||||
|
alias,
|
||||||
|
} => {
|
||||||
|
ident = alias.clone();
|
||||||
|
required = Some(
|
||||||
|
formals
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, default)| default.is_none())
|
||||||
|
.map(|(k, _)| k.clone())
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
allowed = if ellipsis {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(formals.iter().map(|(k, _)| k.clone()).collect())
|
||||||
|
};
|
||||||
|
|
||||||
// Desugar pattern matching in function arguments into a `let` expression.
|
// Desugar pattern matching in function arguments into a `let` expression.
|
||||||
// For example, `({ a, b ? 2 }): a + b` is desugared into:
|
// For example, `({ a, b ? 2 }): a + b` is desugared into:
|
||||||
// `arg: let a = arg.a; b = arg.b or 2; in a + b`
|
// `arg: let a = arg.a; b = arg.b or 2; in a + b`
|
||||||
if let Param::Formals { formals, alias, .. } = ¶m {
|
let mut bindings: HashMap<_, _> = formals
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, default)| {
|
||||||
|
// For each formal parameter, create a `Select` expression to extract it from the argument set.
|
||||||
// `Arg` represents the raw argument (the attribute set) passed to the function.
|
// `Arg` represents the raw argument (the attribute set) passed to the function.
|
||||||
let arg = ctx.new_expr(Hir::Arg(Arg));
|
let arg = ctx.new_expr(Hir::Arg(Arg));
|
||||||
let mut bindings: HashMap<_, _> = formals
|
|
||||||
.iter()
|
|
||||||
.map(|&(ref k, default)| {
|
|
||||||
// For each formal parameter, create a `Select` expression to extract it from the argument set.
|
|
||||||
(
|
(
|
||||||
k.clone(),
|
k.clone(),
|
||||||
ctx.new_expr(
|
ctx.new_expr(
|
||||||
@@ -290,36 +317,8 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Lambda {
|
|||||||
let let_ = Let { bindings, body };
|
let let_ = Let { bindings, body };
|
||||||
body = ctx.new_expr(let_.to_hir());
|
body = ctx.new_expr(let_.to_hir());
|
||||||
}
|
}
|
||||||
let ident;
|
|
||||||
let required;
|
|
||||||
let allowed;
|
|
||||||
match param {
|
|
||||||
Param::Ident(id) => {
|
|
||||||
ident = Some(id);
|
|
||||||
required = None;
|
|
||||||
allowed = None;
|
|
||||||
}
|
|
||||||
Param::Formals {
|
|
||||||
formals,
|
|
||||||
ellipsis,
|
|
||||||
alias,
|
|
||||||
} => {
|
|
||||||
ident = alias;
|
|
||||||
required = Some(
|
|
||||||
formals
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.filter(|(_, default)| default.is_none())
|
|
||||||
.map(|(k, _)| k)
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
allowed = if ellipsis {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(formals.into_iter().map(|(k, _)| k).collect())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let param = ir::Param {
|
let param = ir::Param {
|
||||||
ident,
|
ident,
|
||||||
required,
|
required,
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ pub trait DowngradeContext {
|
|||||||
fn with_expr<T>(&self, id: ExprId, f: impl FnOnce(&Hir, &Self) -> T) -> T;
|
fn with_expr<T>(&self, id: ExprId, f: impl FnOnce(&Hir, &Self) -> T) -> T;
|
||||||
|
|
||||||
/// Provides temporary mutable access to an expression.
|
/// Provides temporary mutable access to an expression.
|
||||||
fn with_expr_mut<T>(&mut self, id: ExprId, f: impl FnOnce(&mut Hir, &mut Self) -> T) -> T;
|
fn with_expr_mut<T>(&mut self, id: &ExprId, f: impl FnOnce(&mut Hir, &mut Self) -> T) -> T;
|
||||||
}
|
}
|
||||||
|
|
||||||
ir! {
|
ir! {
|
||||||
@@ -123,7 +123,7 @@ impl Attrs for AttrSet {
|
|||||||
match attr {
|
match attr {
|
||||||
Attr::Str(ident) => {
|
Attr::Str(ident) => {
|
||||||
// If the next attribute is a static string.
|
// If the next attribute is a static string.
|
||||||
if let Some(&id) = self.stcs.get(&ident) {
|
if let Some(id) = self.stcs.get(&ident) {
|
||||||
// If a sub-attrset already exists, recurse into it.
|
// If a sub-attrset already exists, recurse into it.
|
||||||
ctx.with_expr_mut(id, |expr, ctx| {
|
ctx.with_expr_mut(id, |expr, ctx| {
|
||||||
expr.as_mut()
|
expr.as_mut()
|
||||||
@@ -186,7 +186,7 @@ impl Attrs for AttrSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Debug)]
|
||||||
enum Param {
|
enum Param {
|
||||||
/// A simple parameter, e.g., `x: ...`.
|
/// A simple parameter, e.g., `x: ...`.
|
||||||
Ident(String),
|
Ident(String),
|
||||||
|
|||||||
@@ -125,13 +125,13 @@ pub fn downgrade_inherit(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let expr = from.map_or_else(
|
let expr = from.as_ref().map_or_else(
|
||||||
// If `from` is None, `inherit foo;` becomes `foo = foo;`.
|
// If `from` is None, `inherit foo;` becomes `foo = foo;`.
|
||||||
|| Var { sym: ident.clone() }.to_hir(),
|
|| Var { sym: ident.clone() }.to_hir(),
|
||||||
// If `from` is Some, `inherit (from) foo;` becomes `foo = from.foo;`.
|
// If `from` is Some, `inherit (from) foo;` becomes `foo = from.foo;`.
|
||||||
|expr| {
|
|expr| {
|
||||||
Select {
|
Select {
|
||||||
expr,
|
expr: unsafe { expr.clone() },
|
||||||
attrpath: vec![Attr::Str(ident.clone())],
|
attrpath: vec![Attr::Str(ident.clone())],
|
||||||
default: None,
|
default: None,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//! The IR provides a simplified, language-agnostic representation of Nix expressions,
|
//! The IR provides a simplified, language-agnostic representation of Nix expressions,
|
||||||
//! serving as a bridge between the high-level representation (HIR) and the low-level
|
//! serving as a bridge between the high-level representation (HIR) and the low-level
|
||||||
//! representation (LIR). It defines the fundamental building blocks like expression IDs,
|
//! representation (LIR). It defines the fundamental building blocks like expression IDs,
|
||||||
//! function IDs, and structures for various expression types (e.g., binary operations,
|
//! argument indexes, and structures for various expression types (e.g., binary operations,
|
||||||
//! attribute sets, function calls).
|
//! attribute sets, function calls).
|
||||||
//!
|
//!
|
||||||
//! These structures are designed to be generic and reusable across different stages of
|
//! These structures are designed to be generic and reusable across different stages of
|
||||||
@@ -18,35 +18,40 @@ use nixjit_value::Const as PubConst;
|
|||||||
|
|
||||||
/// A type-safe wrapper for an index into an expression table.
|
/// A type-safe wrapper for an index into an expression table.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct ExprId(usize);
|
pub struct ExprId(usize);
|
||||||
|
|
||||||
impl From<usize> for ExprId {
|
impl ExprId {
|
||||||
fn from(id: usize) -> Self {
|
#[inline(always)]
|
||||||
ExprId(id)
|
pub unsafe fn clone(&self) -> Self {
|
||||||
|
Self(self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn raw(self) -> usize {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn from(id: usize) -> Self {
|
||||||
|
Self(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ExprId> for usize {
|
/// A type-safe wrapper for an index into a primop (builtin function) table.
|
||||||
fn from(id: ExprId) -> Self {
|
|
||||||
id.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A type-safe wrapper for an index into a function table.
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct FuncId(usize);
|
pub struct PrimOpId(usize);
|
||||||
|
|
||||||
impl From<usize> for FuncId {
|
impl PrimOpId {
|
||||||
fn from(id: usize) -> Self {
|
#[inline(always)]
|
||||||
FuncId(id)
|
pub unsafe fn raw(self) -> usize {
|
||||||
|
self.0
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl From<FuncId> for usize {
|
#[inline(always)]
|
||||||
fn from(id: FuncId) -> Self {
|
pub unsafe fn from(id: usize) -> Self {
|
||||||
id.0
|
Self(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,15 +60,15 @@ impl From<FuncId> for usize {
|
|||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct ArgIdx(usize);
|
pub struct ArgIdx(usize);
|
||||||
|
|
||||||
impl From<usize> for ArgIdx {
|
impl ArgIdx {
|
||||||
fn from(id: usize) -> Self {
|
#[inline(always)]
|
||||||
ArgIdx(id)
|
pub unsafe fn raw(self) -> usize {
|
||||||
|
self.0
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ArgIdx> for usize {
|
#[inline(always)]
|
||||||
fn from(id: ArgIdx) -> Self {
|
pub unsafe fn from(idx: usize) -> Self {
|
||||||
id.0
|
Self(idx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +84,7 @@ pub struct AttrSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a key in an attribute path.
|
/// Represents a key in an attribute path.
|
||||||
#[derive(Clone, Debug, TryUnwrap)]
|
#[derive(Debug, TryUnwrap)]
|
||||||
pub enum Attr {
|
pub enum Attr {
|
||||||
/// A dynamic attribute key, which is an expression that must evaluate to a string.
|
/// A dynamic attribute key, which is an expression that must evaluate to a string.
|
||||||
Dynamic(ExprId),
|
Dynamic(ExprId),
|
||||||
@@ -234,6 +239,14 @@ pub struct Call {
|
|||||||
pub args: Vec<ExprId>,
|
pub args: Vec<ExprId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Represents a primitive operation (builtin function)
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct PrimOp {
|
||||||
|
pub name: &'static str,
|
||||||
|
pub id: PrimOpId,
|
||||||
|
pub arity: usize,
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents a `with` expression.
|
/// Represents a `with` expression.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct With {
|
pub struct With {
|
||||||
|
|||||||
@@ -88,10 +88,10 @@ impl<Ctx: JITContext> JITCompile<Ctx> for BinOp {
|
|||||||
let float_block = ctx.builder.create_block();
|
let float_block = ctx.builder.create_block();
|
||||||
let float_check_block = ctx.builder.create_block();
|
let float_check_block = ctx.builder.create_block();
|
||||||
|
|
||||||
let is_int =
|
let is_int = ctx
|
||||||
ctx.builder
|
.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp_imm(IntCC::Equal, lhs_tag, Value::<Ctx>::INT as i64);
|
.icmp_imm(IntCC::Equal, lhs_tag, Value::INT as i64);
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.brif(is_int, int_block, [], float_check_block, []);
|
.brif(is_int, int_block, [], float_check_block, []);
|
||||||
@@ -109,7 +109,7 @@ impl<Ctx: JITContext> JITCompile<Ctx> for BinOp {
|
|||||||
let is_float =
|
let is_float =
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp_imm(IntCC::Equal, lhs_tag, Value::<Ctx>::FLOAT as i64);
|
.icmp_imm(IntCC::Equal, lhs_tag, Value::FLOAT as i64);
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.brif(is_float, float_block, [], default_block, []);
|
.brif(is_float, float_block, [], default_block, []);
|
||||||
@@ -141,10 +141,10 @@ impl<Ctx: JITContext> JITCompile<Ctx> for BinOp {
|
|||||||
let float_block = ctx.builder.create_block();
|
let float_block = ctx.builder.create_block();
|
||||||
let float_check_block = ctx.builder.create_block();
|
let float_check_block = ctx.builder.create_block();
|
||||||
|
|
||||||
let is_int =
|
let is_int = ctx
|
||||||
ctx.builder
|
.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp_imm(IntCC::Equal, lhs_tag, Value::<Ctx>::INT as i64);
|
.icmp_imm(IntCC::Equal, lhs_tag, Value::INT as i64);
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.brif(is_int, int_block, [], float_check_block, []);
|
.brif(is_int, int_block, [], float_check_block, []);
|
||||||
@@ -162,7 +162,7 @@ impl<Ctx: JITContext> JITCompile<Ctx> for BinOp {
|
|||||||
let is_float =
|
let is_float =
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp_imm(IntCC::Equal, lhs_tag, Value::<Ctx>::FLOAT as i64);
|
.icmp_imm(IntCC::Equal, lhs_tag, Value::FLOAT as i64);
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.brif(is_float, float_block, [], default_block, []);
|
.brif(is_float, float_block, [], default_block, []);
|
||||||
@@ -194,10 +194,10 @@ impl<Ctx: JITContext> JITCompile<Ctx> for BinOp {
|
|||||||
let float_block = ctx.builder.create_block();
|
let float_block = ctx.builder.create_block();
|
||||||
let float_check_block = ctx.builder.create_block();
|
let float_check_block = ctx.builder.create_block();
|
||||||
|
|
||||||
let is_int =
|
let is_int = ctx
|
||||||
ctx.builder
|
.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp_imm(IntCC::Equal, lhs_tag, Value::<Ctx>::INT as i64);
|
.icmp_imm(IntCC::Equal, lhs_tag, Value::INT as i64);
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.brif(is_int, int_block, [], float_check_block, []);
|
.brif(is_int, int_block, [], float_check_block, []);
|
||||||
@@ -215,7 +215,7 @@ impl<Ctx: JITContext> JITCompile<Ctx> for BinOp {
|
|||||||
let is_float =
|
let is_float =
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp_imm(IntCC::Equal, lhs_tag, Value::<Ctx>::FLOAT as i64);
|
.icmp_imm(IntCC::Equal, lhs_tag, Value::FLOAT as i64);
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.brif(is_float, float_block, [], default_block, []);
|
.brif(is_float, float_block, [], default_block, []);
|
||||||
@@ -245,10 +245,10 @@ impl<Ctx: JITContext> JITCompile<Ctx> for BinOp {
|
|||||||
let bool_block = ctx.builder.create_block();
|
let bool_block = ctx.builder.create_block();
|
||||||
let non_bool_block = ctx.builder.create_block();
|
let non_bool_block = ctx.builder.create_block();
|
||||||
|
|
||||||
let is_bool =
|
let is_bool = ctx
|
||||||
ctx.builder
|
.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp_imm(IntCC::Equal, lhs_tag, Value::<Ctx>::BOOL as i64);
|
.icmp_imm(IntCC::Equal, lhs_tag, Value::BOOL as i64);
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.brif(is_bool, bool_block, [], non_bool_block, []);
|
.brif(is_bool, bool_block, [], non_bool_block, []);
|
||||||
@@ -275,10 +275,10 @@ impl<Ctx: JITContext> JITCompile<Ctx> for BinOp {
|
|||||||
let bool_block = ctx.builder.create_block();
|
let bool_block = ctx.builder.create_block();
|
||||||
let non_bool_block = ctx.builder.create_block();
|
let non_bool_block = ctx.builder.create_block();
|
||||||
|
|
||||||
let is_bool =
|
let is_bool = ctx
|
||||||
ctx.builder
|
.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp_imm(IntCC::Equal, lhs_tag, Value::<Ctx>::BOOL as i64);
|
.icmp_imm(IntCC::Equal, lhs_tag, Value::BOOL as i64);
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.brif(is_bool, bool_block, [], non_bool_block, []);
|
.brif(is_bool, bool_block, [], non_bool_block, []);
|
||||||
@@ -378,10 +378,10 @@ impl<Ctx: JITContext> JITCompile<Ctx> for If {
|
|||||||
let judge_block = ctx.builder.create_block();
|
let judge_block = ctx.builder.create_block();
|
||||||
let slot = ctx.alloca();
|
let slot = ctx.alloca();
|
||||||
|
|
||||||
let is_bool =
|
let is_bool = ctx
|
||||||
ctx.builder
|
.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp_imm(IntCC::Equal, cond_type, Value::<Ctx>::BOOL as i64);
|
.icmp_imm(IntCC::Equal, cond_type, Value::BOOL as i64);
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.brif(is_bool, judge_block, [], error_block, []);
|
.brif(is_bool, judge_block, [], error_block, []);
|
||||||
@@ -480,37 +480,25 @@ impl<Ctx: JITContext> JITCompile<Ctx> for Const {
|
|||||||
let slot = ctx.alloca();
|
let slot = ctx.alloca();
|
||||||
match self.val {
|
match self.val {
|
||||||
Bool(x) => {
|
Bool(x) => {
|
||||||
let tag = ctx
|
let tag = ctx.builder.ins().iconst(types::I64, Value::BOOL as i64);
|
||||||
.builder
|
|
||||||
.ins()
|
|
||||||
.iconst(types::I64, Value::<Ctx>::BOOL as i64);
|
|
||||||
let val = ctx.builder.ins().iconst(types::I64, x as i64);
|
let val = ctx.builder.ins().iconst(types::I64, x as i64);
|
||||||
ctx.builder.ins().stack_store(tag, slot, 0);
|
ctx.builder.ins().stack_store(tag, slot, 0);
|
||||||
ctx.builder.ins().stack_store(val, slot, 8);
|
ctx.builder.ins().stack_store(val, slot, 8);
|
||||||
}
|
}
|
||||||
Int(x) => {
|
Int(x) => {
|
||||||
let tag = ctx
|
let tag = ctx.builder.ins().iconst(types::I64, Value::INT as i64);
|
||||||
.builder
|
|
||||||
.ins()
|
|
||||||
.iconst(types::I64, Value::<Ctx>::INT as i64);
|
|
||||||
let val = ctx.builder.ins().iconst(types::I64, x);
|
let val = ctx.builder.ins().iconst(types::I64, x);
|
||||||
ctx.builder.ins().stack_store(tag, slot, 0);
|
ctx.builder.ins().stack_store(tag, slot, 0);
|
||||||
ctx.builder.ins().stack_store(val, slot, 8);
|
ctx.builder.ins().stack_store(val, slot, 8);
|
||||||
}
|
}
|
||||||
Float(x) => {
|
Float(x) => {
|
||||||
let tag = ctx
|
let tag = ctx.builder.ins().iconst(types::I64, Value::FLOAT as i64);
|
||||||
.builder
|
|
||||||
.ins()
|
|
||||||
.iconst(types::I64, Value::<Ctx>::FLOAT as i64);
|
|
||||||
let val = ctx.builder.ins().f64const(x);
|
let val = ctx.builder.ins().f64const(x);
|
||||||
ctx.builder.ins().stack_store(tag, slot, 0);
|
ctx.builder.ins().stack_store(tag, slot, 0);
|
||||||
ctx.builder.ins().stack_store(val, slot, 8);
|
ctx.builder.ins().stack_store(val, slot, 8);
|
||||||
}
|
}
|
||||||
Null => {
|
Null => {
|
||||||
let tag = ctx
|
let tag = ctx.builder.ins().iconst(types::I64, Value::NULL as i64);
|
||||||
.builder
|
|
||||||
.ins()
|
|
||||||
.iconst(types::I64, Value::<Ctx>::NULL as i64);
|
|
||||||
ctx.builder.ins().stack_store(tag, slot, 0);
|
ctx.builder.ins().stack_store(tag, slot, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,21 +11,21 @@ use nixjit_eval::{AttrSet, EvalContext, List, Value};
|
|||||||
use super::JITContext;
|
use super::JITContext;
|
||||||
|
|
||||||
pub extern "C" fn helper_call<Ctx: JITContext>(
|
pub extern "C" fn helper_call<Ctx: JITContext>(
|
||||||
func: &mut Value<Ctx>,
|
func: &mut Value,
|
||||||
args_ptr: *mut Value<Ctx>,
|
args_ptr: *mut Value,
|
||||||
args_len: usize,
|
args_len: usize,
|
||||||
ctx: &mut Ctx,
|
ctx: &mut Ctx,
|
||||||
) {
|
) {
|
||||||
// TODO: Error Handling
|
// TODO: Error Handling
|
||||||
let args = core::ptr::slice_from_raw_parts_mut(args_ptr, args_len);
|
let args = core::ptr::slice_from_raw_parts_mut(args_ptr, args_len);
|
||||||
let args = unsafe { Box::from_raw(args) };
|
let args = unsafe { Box::from_raw(args) };
|
||||||
func.call(args.into_iter().collect(), ctx).unwrap();
|
func.call(args.into_iter().map(Ok), ctx).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub extern "C" fn helper_lookup_stack<Ctx: JITContext>(
|
pub extern "C" fn helper_lookup_stack<Ctx: JITContext>(
|
||||||
ctx: &Ctx,
|
ctx: &Ctx,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
ret: &mut MaybeUninit<Value<Ctx>>,
|
ret: &mut MaybeUninit<Value>,
|
||||||
) {
|
) {
|
||||||
ret.write(ctx.lookup_stack(offset).clone());
|
ret.write(ctx.lookup_stack(offset).clone());
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@ pub extern "C" fn helper_lookup_stack<Ctx: JITContext>(
|
|||||||
pub extern "C" fn helper_lookup_arg<Ctx: JITContext>(
|
pub extern "C" fn helper_lookup_arg<Ctx: JITContext>(
|
||||||
ctx: &Ctx,
|
ctx: &Ctx,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
ret: &mut MaybeUninit<Value<Ctx>>,
|
ret: &mut MaybeUninit<Value>,
|
||||||
) {
|
) {
|
||||||
ret.write(JITContext::lookup_arg(ctx, offset).clone());
|
ret.write(JITContext::lookup_arg(ctx, offset).clone());
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,7 @@ pub extern "C" fn helper_lookup<Ctx: JITContext>(
|
|||||||
ctx: &Ctx,
|
ctx: &Ctx,
|
||||||
sym_ptr: *const u8,
|
sym_ptr: *const u8,
|
||||||
sym_len: usize,
|
sym_len: usize,
|
||||||
ret: &mut MaybeUninit<Value<Ctx>>,
|
ret: &mut MaybeUninit<Value>,
|
||||||
) {
|
) {
|
||||||
// TODO: Error Handling
|
// TODO: Error Handling
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -57,8 +57,8 @@ pub extern "C" fn helper_lookup<Ctx: JITContext>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub extern "C" fn helper_select<Ctx: JITContext>(
|
pub extern "C" fn helper_select<Ctx: JITContext>(
|
||||||
val: &mut Value<Ctx>,
|
val: &mut Value,
|
||||||
path_ptr: *mut Value<Ctx>,
|
path_ptr: *mut Value,
|
||||||
path_len: usize,
|
path_len: usize,
|
||||||
) {
|
) {
|
||||||
let path = core::ptr::slice_from_raw_parts_mut(path_ptr, path_len);
|
let path = core::ptr::slice_from_raw_parts_mut(path_ptr, path_len);
|
||||||
@@ -71,10 +71,10 @@ pub extern "C" fn helper_select<Ctx: JITContext>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub extern "C" fn helper_select_with_default<Ctx: JITContext>(
|
pub extern "C" fn helper_select_with_default<Ctx: JITContext>(
|
||||||
val: &mut Value<Ctx>,
|
val: &mut Value,
|
||||||
path_ptr: *mut Value<Ctx>,
|
path_ptr: *mut Value,
|
||||||
path_len: usize,
|
path_len: usize,
|
||||||
default: NonNull<Value<Ctx>>,
|
default: NonNull<Value>,
|
||||||
) {
|
) {
|
||||||
let path = core::ptr::slice_from_raw_parts_mut(path_ptr, path_len);
|
let path = core::ptr::slice_from_raw_parts_mut(path_ptr, path_len);
|
||||||
let path = unsafe { Box::from_raw(path) };
|
let path = unsafe { Box::from_raw(path) };
|
||||||
@@ -88,14 +88,14 @@ pub extern "C" fn helper_select_with_default<Ctx: JITContext>(
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub extern "C" fn helper_eq<Ctx: JITContext>(lhs: &mut Value<Ctx>, rhs: &Value<Ctx>) {
|
pub extern "C" fn helper_eq<Ctx: JITContext>(lhs: &mut Value, rhs: &Value) {
|
||||||
lhs.eq(rhs);
|
lhs.eq(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe extern "C" fn helper_create_string<Ctx: JITContext>(
|
pub unsafe extern "C" fn helper_create_string<Ctx: JITContext>(
|
||||||
ptr: *const u8,
|
ptr: *const u8,
|
||||||
len: usize,
|
len: usize,
|
||||||
ret: &mut MaybeUninit<Value<Ctx>>,
|
ret: &mut MaybeUninit<Value>,
|
||||||
) {
|
) {
|
||||||
unsafe {
|
unsafe {
|
||||||
ret.write(Value::String(
|
ret.write(Value::String(
|
||||||
@@ -105,9 +105,9 @@ pub unsafe extern "C" fn helper_create_string<Ctx: JITContext>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe extern "C" fn helper_create_list<Ctx: JITContext>(
|
pub unsafe extern "C" fn helper_create_list<Ctx: JITContext>(
|
||||||
ptr: *mut Value<Ctx>,
|
ptr: *mut Value,
|
||||||
len: usize,
|
len: usize,
|
||||||
ret: &mut MaybeUninit<Value<Ctx>>,
|
ret: &mut MaybeUninit<Value>,
|
||||||
) {
|
) {
|
||||||
unsafe {
|
unsafe {
|
||||||
ret.write(Value::List(
|
ret.write(Value::List(
|
||||||
@@ -117,16 +117,16 @@ pub unsafe extern "C" fn helper_create_list<Ctx: JITContext>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe extern "C" fn helper_create_attrs<Ctx: JITContext>(
|
pub unsafe extern "C" fn helper_create_attrs<Ctx: JITContext>(
|
||||||
ret: &mut MaybeUninit<HashMap<String, Value<Ctx>>>,
|
ret: &mut MaybeUninit<HashMap<String, Value>>,
|
||||||
) {
|
) {
|
||||||
ret.write(HashMap::new());
|
ret.write(HashMap::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe extern "C" fn helper_push_attr<Ctx: JITContext>(
|
pub unsafe extern "C" fn helper_push_attr<Ctx: JITContext>(
|
||||||
attrs: &mut HashMap<String, Value<Ctx>>,
|
attrs: &mut HashMap<String, Value>,
|
||||||
sym_ptr: *const u8,
|
sym_ptr: *const u8,
|
||||||
sym_len: usize,
|
sym_len: usize,
|
||||||
val: NonNull<Value<Ctx>>,
|
val: NonNull<Value>,
|
||||||
) {
|
) {
|
||||||
unsafe {
|
unsafe {
|
||||||
attrs.insert(
|
attrs.insert(
|
||||||
@@ -137,8 +137,8 @@ pub unsafe extern "C" fn helper_push_attr<Ctx: JITContext>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe extern "C" fn helper_finalize_attrs<Ctx: JITContext>(
|
pub unsafe extern "C" fn helper_finalize_attrs<Ctx: JITContext>(
|
||||||
attrs: NonNull<HashMap<String, Value<Ctx>>>,
|
attrs: NonNull<HashMap<String, Value>>,
|
||||||
ret: &mut MaybeUninit<Value<Ctx>>,
|
ret: &mut MaybeUninit<Value>,
|
||||||
) {
|
) {
|
||||||
ret.write(Value::AttrSet(
|
ret.write(Value::AttrSet(
|
||||||
AttrSet::from(unsafe { attrs.read() }).into(),
|
AttrSet::from(unsafe { attrs.read() }).into(),
|
||||||
@@ -147,7 +147,7 @@ pub unsafe extern "C" fn helper_finalize_attrs<Ctx: JITContext>(
|
|||||||
|
|
||||||
pub unsafe extern "C" fn helper_enter_with<Ctx: JITContext>(
|
pub unsafe extern "C" fn helper_enter_with<Ctx: JITContext>(
|
||||||
ctx: &mut Ctx,
|
ctx: &mut Ctx,
|
||||||
namespace: NonNull<Value<Ctx>>,
|
namespace: NonNull<Value>,
|
||||||
) {
|
) {
|
||||||
ctx.enter_with(unsafe { namespace.read() }.unwrap_attr_set().into_inner());
|
ctx.enter_with(unsafe { namespace.read() }.unwrap_attr_set().into_inner());
|
||||||
}
|
}
|
||||||
@@ -157,9 +157,9 @@ pub unsafe extern "C" fn helper_exit_with<Ctx: JITContext>(ctx: &mut Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe extern "C" fn helper_alloc_array<Ctx: JITContext>(len: usize) -> *mut u8 {
|
pub unsafe extern "C" fn helper_alloc_array<Ctx: JITContext>(len: usize) -> *mut u8 {
|
||||||
unsafe { alloc(Layout::array::<Value<Ctx>>(len).unwrap()) }
|
unsafe { alloc(Layout::array::<Value>(len).unwrap()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub extern "C" fn helper_dbg<Ctx: JITContext>(value: &Value<Ctx>) {
|
pub extern "C" fn helper_dbg<Ctx: JITContext>(value: &Value) {
|
||||||
println!("{value:?}")
|
println!("{value:?}")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ pub use compile::JITCompile;
|
|||||||
use helpers::*;
|
use helpers::*;
|
||||||
|
|
||||||
pub trait JITContext: EvalContext + Sized {
|
pub trait JITContext: EvalContext + Sized {
|
||||||
fn lookup_stack(&self, offset: usize) -> &Value<Self>;
|
fn lookup_stack(&self, offset: usize) -> &Value;
|
||||||
fn lookup_arg(&self, offset: usize) -> &Value<Self>;
|
fn lookup_arg(&self, offset: usize) -> &Value;
|
||||||
fn enter_with(&mut self, namespace: Rc<HashMap<String, Value<Self>>>);
|
fn enter_with(&mut self, namespace: Rc<HashMap<String, Value>>);
|
||||||
fn exit_with(&mut self);
|
fn exit_with(&mut self);
|
||||||
}
|
}
|
||||||
|
|
||||||
type F<Ctx: JITContext> = unsafe extern "C" fn(*const Ctx, *mut Value<Ctx>);
|
type F<Ctx> = unsafe extern "C" fn(*const Ctx, *mut Value);
|
||||||
|
|
||||||
pub struct JITFunc<Ctx: JITContext> {
|
pub struct JITFunc<Ctx: JITContext> {
|
||||||
func: F<Ctx>,
|
func: F<Ctx>,
|
||||||
|
|||||||
@@ -24,12 +24,16 @@ ir! {
|
|||||||
Str,
|
Str,
|
||||||
Var,
|
Var,
|
||||||
Path,
|
Path,
|
||||||
|
PrimOp,
|
||||||
ExprRef(ExprId),
|
ExprRef(ExprId),
|
||||||
FuncRef(ExprId),
|
FuncRef(ExprId),
|
||||||
ArgRef(ArgIdx),
|
ArgRef(ArgIdx),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug)]
|
||||||
|
pub struct Builtins;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum LookupResult {
|
pub enum LookupResult {
|
||||||
Expr(ExprId),
|
Expr(ExprId),
|
||||||
Arg(ArgIdx),
|
Arg(ArgIdx),
|
||||||
@@ -38,21 +42,17 @@ pub enum LookupResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait ResolveContext {
|
pub trait ResolveContext {
|
||||||
fn new_dep(&mut self, expr: ExprId, dep: ExprId);
|
fn new_dep(&mut self, expr: &ExprId, dep: ExprId);
|
||||||
fn new_func(&mut self, body: ExprId, param: Param);
|
fn new_func(&mut self, body: &ExprId, param: Param);
|
||||||
fn resolve(&mut self, expr: ExprId) -> Result<()>;
|
fn resolve(&mut self, expr: &ExprId) -> Result<()>;
|
||||||
fn lookup(&self, name: &str) -> LookupResult;
|
fn lookup(&self, name: &str) -> LookupResult;
|
||||||
fn with_with_env<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> (bool, T);
|
fn with_with_env<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> (bool, T);
|
||||||
fn with_let_env<'a, T>(
|
fn with_let_env<'a, T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
bindings: impl IntoIterator<Item = (&'a String, &'a ExprId)>,
|
bindings: impl Iterator<Item = (&'a String, &'a ExprId)>,
|
||||||
f: impl FnOnce(&mut Self) -> T,
|
|
||||||
) -> T;
|
|
||||||
fn with_param_env<'a, T>(
|
|
||||||
&mut self,
|
|
||||||
ident: Option<&'a str>,
|
|
||||||
f: impl FnOnce(&mut Self) -> T,
|
f: impl FnOnce(&mut Self) -> T,
|
||||||
) -> T;
|
) -> T;
|
||||||
|
fn with_param_env<T>(&mut self, ident: Option<String>, f: impl FnOnce(&mut Self) -> T) -> T;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Resolve<Ctx: ResolveContext> {
|
pub trait Resolve<Ctx: ResolveContext> {
|
||||||
@@ -80,7 +80,7 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for hir::Hir {
|
|||||||
Var(x) => x.resolve(ctx),
|
Var(x) => x.resolve(ctx),
|
||||||
Path(x) => x.resolve(ctx),
|
Path(x) => x.resolve(ctx),
|
||||||
Let(x) => x.resolve(ctx),
|
Let(x) => x.resolve(ctx),
|
||||||
Arg(_) => todo!(),
|
Arg(_) => unsafe { Ok(Lir::ArgRef(ArgIdx::from(0))) },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,10 +90,10 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for AttrSet {
|
|||||||
if self.rec {
|
if self.rec {
|
||||||
todo!()
|
todo!()
|
||||||
} else {
|
} else {
|
||||||
for (_, &v) in self.stcs.iter() {
|
for (_, v) in self.stcs.iter() {
|
||||||
ctx.resolve(v)?;
|
ctx.resolve(v)?;
|
||||||
}
|
}
|
||||||
for &(k, v) in self.dyns.iter() {
|
for (k, v) in self.dyns.iter() {
|
||||||
ctx.resolve(k)?;
|
ctx.resolve(k)?;
|
||||||
ctx.resolve(v)?;
|
ctx.resolve(v)?;
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for AttrSet {
|
|||||||
|
|
||||||
impl<Ctx: ResolveContext> Resolve<Ctx> for List {
|
impl<Ctx: ResolveContext> Resolve<Ctx> for List {
|
||||||
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
for &item in self.items.iter() {
|
for item in self.items.iter() {
|
||||||
ctx.resolve(item)?;
|
ctx.resolve(item)?;
|
||||||
}
|
}
|
||||||
Ok(self.to_lir())
|
Ok(self.to_lir())
|
||||||
@@ -113,10 +113,10 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for List {
|
|||||||
|
|
||||||
impl<Ctx: ResolveContext> Resolve<Ctx> for HasAttr {
|
impl<Ctx: ResolveContext> Resolve<Ctx> for HasAttr {
|
||||||
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
ctx.resolve(self.lhs)?;
|
ctx.resolve(&self.lhs)?;
|
||||||
for attr in self.rhs.iter() {
|
for attr in self.rhs.iter() {
|
||||||
if let &Attr::Dynamic(expr) = attr {
|
if let Attr::Dynamic(expr) = attr {
|
||||||
ctx.resolve(expr)?;
|
ctx.resolve(&expr)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(self.to_lir())
|
Ok(self.to_lir())
|
||||||
@@ -125,28 +125,28 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for HasAttr {
|
|||||||
|
|
||||||
impl<Ctx: ResolveContext> Resolve<Ctx> for BinOp {
|
impl<Ctx: ResolveContext> Resolve<Ctx> for BinOp {
|
||||||
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
ctx.resolve(self.lhs)?;
|
ctx.resolve(&self.lhs)?;
|
||||||
ctx.resolve(self.rhs)?;
|
ctx.resolve(&self.rhs)?;
|
||||||
Ok(self.to_lir())
|
Ok(self.to_lir())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: ResolveContext> Resolve<Ctx> for UnOp {
|
impl<Ctx: ResolveContext> Resolve<Ctx> for UnOp {
|
||||||
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
ctx.resolve(self.rhs)?;
|
ctx.resolve(&self.rhs)?;
|
||||||
Ok(self.to_lir())
|
Ok(self.to_lir())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: ResolveContext> Resolve<Ctx> for Select {
|
impl<Ctx: ResolveContext> Resolve<Ctx> for Select {
|
||||||
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
ctx.resolve(self.expr)?;
|
ctx.resolve(&self.expr)?;
|
||||||
for attr in self.attrpath.iter() {
|
for attr in self.attrpath.iter() {
|
||||||
if let &Attr::Dynamic(expr) = attr {
|
if let Attr::Dynamic(expr) = attr {
|
||||||
ctx.resolve(expr)?;
|
ctx.resolve(&expr)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(expr) = self.default {
|
if let Some(ref expr) = self.default {
|
||||||
ctx.resolve(expr)?;
|
ctx.resolve(expr)?;
|
||||||
}
|
}
|
||||||
Ok(self.to_lir())
|
Ok(self.to_lir())
|
||||||
@@ -155,25 +155,25 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for Select {
|
|||||||
|
|
||||||
impl<Ctx: ResolveContext> Resolve<Ctx> for If {
|
impl<Ctx: ResolveContext> Resolve<Ctx> for If {
|
||||||
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
ctx.resolve(self.cond)?;
|
ctx.resolve(&self.cond)?;
|
||||||
ctx.resolve(self.consq)?;
|
ctx.resolve(&self.consq)?;
|
||||||
ctx.resolve(self.alter)?;
|
ctx.resolve(&self.alter)?;
|
||||||
Ok(self.to_lir())
|
Ok(self.to_lir())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.param.ident.as_deref(), |ctx| ctx.resolve(self.body))?;
|
ctx.with_param_env(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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)?;
|
||||||
for &arg in self.args.iter() {
|
for arg in self.args.iter() {
|
||||||
ctx.resolve(arg)?;
|
ctx.resolve(arg)?;
|
||||||
}
|
}
|
||||||
Ok(self.to_lir())
|
Ok(self.to_lir())
|
||||||
@@ -182,8 +182,8 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for Call {
|
|||||||
|
|
||||||
impl<Ctx: ResolveContext> Resolve<Ctx> for With {
|
impl<Ctx: ResolveContext> Resolve<Ctx> for With {
|
||||||
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
ctx.resolve(self.namespace)?;
|
ctx.resolve(&self.namespace)?;
|
||||||
let (env_used, res) = ctx.with_with_env(|ctx| ctx.resolve(self.expr));
|
let (env_used, res) = ctx.with_with_env(|ctx| ctx.resolve(&self.expr));
|
||||||
res?;
|
res?;
|
||||||
if env_used {
|
if env_used {
|
||||||
Ok(self.to_lir())
|
Ok(self.to_lir())
|
||||||
@@ -195,15 +195,15 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for With {
|
|||||||
|
|
||||||
impl<Ctx: ResolveContext> Resolve<Ctx> for Assert {
|
impl<Ctx: ResolveContext> Resolve<Ctx> for Assert {
|
||||||
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
ctx.resolve(self.assertion)?;
|
ctx.resolve(&self.assertion)?;
|
||||||
ctx.resolve(self.expr)?;
|
ctx.resolve(&self.expr)?;
|
||||||
Ok(self.to_lir())
|
Ok(self.to_lir())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: ResolveContext> Resolve<Ctx> for ConcatStrings {
|
impl<Ctx: ResolveContext> Resolve<Ctx> for ConcatStrings {
|
||||||
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
for &part in self.parts.iter() {
|
for part in self.parts.iter() {
|
||||||
ctx.resolve(part)?;
|
ctx.resolve(part)?;
|
||||||
}
|
}
|
||||||
Ok(self.to_lir())
|
Ok(self.to_lir())
|
||||||
@@ -227,7 +227,7 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for Var {
|
|||||||
|
|
||||||
impl<Ctx: ResolveContext> Resolve<Ctx> for Path {
|
impl<Ctx: ResolveContext> Resolve<Ctx> for Path {
|
||||||
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
ctx.resolve(self.expr)?;
|
ctx.resolve(&self.expr)?;
|
||||||
Ok(self.to_lir())
|
Ok(self.to_lir())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,10 +235,10 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for Path {
|
|||||||
impl<Ctx: ResolveContext> Resolve<Ctx> for hir::Let {
|
impl<Ctx: ResolveContext> Resolve<Ctx> for hir::Let {
|
||||||
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
ctx.with_let_env(self.bindings.iter(), |ctx| {
|
ctx.with_let_env(self.bindings.iter(), |ctx| {
|
||||||
for &id in self.bindings.values() {
|
for id in self.bindings.values() {
|
||||||
ctx.resolve(id)?;
|
ctx.resolve(id)?;
|
||||||
}
|
}
|
||||||
ctx.resolve(self.body)
|
ctx.resolve(&self.body)
|
||||||
})?;
|
})?;
|
||||||
Ok(Lir::ExprRef(self.body))
|
Ok(Lir::ExprRef(self.body))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
use convert_case::{Case, Casing};
|
use convert_case::{Case, Casing};
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
use quote::{format_ident, quote, ToTokens};
|
use quote::{ToTokens, format_ident, quote};
|
||||||
use syn::{
|
use syn::{
|
||||||
parse_macro_input, FnArg, Item, ItemConst, ItemFn, ItemMod, Pat, PatType, Type,
|
FnArg, Item, ItemFn, ItemMod, Pat, PatType, Type, Visibility, parse_macro_input,
|
||||||
Visibility,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn builtins_impl(input: TokenStream) -> TokenStream {
|
pub fn builtins_impl(input: TokenStream) -> TokenStream {
|
||||||
let item_mod = parse_macro_input!(input as ItemMod);
|
let item_mod = parse_macro_input!(input as ItemMod);
|
||||||
|
let mod_name = &item_mod.ident;
|
||||||
|
let visibility = &item_mod.vis;
|
||||||
|
|
||||||
let (_brace, items) = match item_mod.content.clone() {
|
let (_brace, items) = match item_mod.content {
|
||||||
Some(content) => content,
|
Some(content) => content,
|
||||||
None => {
|
None => {
|
||||||
return syn::Error::new_spanned(
|
return syn::Error::new_spanned(
|
||||||
@@ -23,63 +24,76 @@ pub fn builtins_impl(input: TokenStream) -> TokenStream {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut pub_item_mod: Vec<proc_macro2::TokenStream> = Vec::new();
|
let mut pub_item_mod: Vec<proc_macro2::TokenStream> = Vec::new();
|
||||||
let mut const_inserters = Vec::new();
|
let mut consts = Vec::new();
|
||||||
let mut global_inserters = Vec::new();
|
let mut global = Vec::new();
|
||||||
let mut scoped_inserters = Vec::new();
|
let mut scoped = Vec::new();
|
||||||
let mut wrappers = Vec::new();
|
let mut wrappers = Vec::new();
|
||||||
|
|
||||||
for item in &items {
|
for item in &items {
|
||||||
match item {
|
match item {
|
||||||
Item::Const(item_const) => {
|
Item::Const(item_const) => {
|
||||||
let inserter = generate_const_inserter(item_const);
|
let name_str = item_const
|
||||||
const_inserters.push(inserter);
|
.ident
|
||||||
pub_item_mod.push(quote! {
|
.to_string()
|
||||||
|
.from_case(Case::UpperSnake)
|
||||||
|
.to_case(Case::Camel);
|
||||||
|
let const_name = &item_const.ident;
|
||||||
|
consts.push(quote! { (#name_str, builtins::#const_name) });
|
||||||
|
pub_item_mod.push(
|
||||||
|
quote! {
|
||||||
pub #item_const
|
pub #item_const
|
||||||
}.into());
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Item::Fn(item_fn) => {
|
Item::Fn(item_fn) => {
|
||||||
let (inserter, wrapper) = match generate_fn_wrapper(item_fn) {
|
let (primop, wrapper) = match generate_primop_wrapper(item_fn) {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(e) => return e.to_compile_error().into(),
|
Err(e) => return e.to_compile_error().into(),
|
||||||
};
|
};
|
||||||
if matches!(item_fn.vis, Visibility::Public(_)) {
|
if matches!(item_fn.vis, Visibility::Public(_)) {
|
||||||
global_inserters.push(inserter);
|
global.push(primop);
|
||||||
pub_item_mod.push(quote! { #item_fn }.into());
|
pub_item_mod.push(quote! { #item_fn }.into());
|
||||||
} else {
|
} else {
|
||||||
scoped_inserters.push(inserter);
|
scoped.push(primop);
|
||||||
pub_item_mod.push(quote! {
|
pub_item_mod.push(
|
||||||
|
quote! {
|
||||||
pub #item_fn
|
pub #item_fn
|
||||||
}.into());
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
wrappers.push(wrapper);
|
wrappers.push(wrapper);
|
||||||
}
|
}
|
||||||
item => pub_item_mod.push(item.to_token_stream())
|
item => pub_item_mod.push(item.to_token_stream()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let consts_len = consts.len();
|
||||||
|
let global_len = global.len();
|
||||||
|
let scoped_len = scoped.len();
|
||||||
let output = quote! {
|
let output = quote! {
|
||||||
mod builtins {
|
#visibility mod #mod_name {
|
||||||
#(#pub_item_mod)*
|
#(#pub_item_mod)*
|
||||||
#(#wrappers)*
|
#(#wrappers)*
|
||||||
|
pub const CONSTS_LEN: usize = #consts_len;
|
||||||
|
pub const GLOBAL_LEN: usize = #global_len;
|
||||||
|
pub const SCOPED_LEN: usize = #scoped_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Builtins<Ctx: BuiltinsContext> {
|
pub struct Builtins<Ctx: BuiltinsContext> {
|
||||||
pub consts: ::std::vec::Vec<(String, ::nixjit_value::Const)>,
|
pub consts: [(&'static str, ::nixjit_value::Const); #mod_name::CONSTS_LEN],
|
||||||
pub global: ::std::vec::Vec<(String, fn(&mut Ctx, Vec<::nixjit_eval::Value<Ctx>>) -> ::nixjit_error::Result<::nixjit_eval::Value<Ctx>>)>,
|
pub global: [(&'static str, usize, fn(&mut Ctx, Vec<::nixjit_eval::Value>) -> ::nixjit_error::Result<::nixjit_eval::Value>); #mod_name::GLOBAL_LEN],
|
||||||
pub scoped: ::std::vec::Vec<(String, fn(&mut Ctx, Vec<::nixjit_eval::Value<Ctx>>) -> ::nixjit_error::Result<::nixjit_eval::Value<Ctx>>)>,
|
pub scoped: [(&'static str, usize, fn(&mut Ctx, Vec<::nixjit_eval::Value>) -> ::nixjit_error::Result<::nixjit_eval::Value>); #mod_name::SCOPED_LEN],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: BuiltinsContext> Builtins<Ctx> {
|
impl<Ctx: BuiltinsContext> Builtins<Ctx> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut consts = ::std::vec::Vec::new();
|
Self {
|
||||||
let mut global = ::std::vec::Vec::new();
|
consts: [#(#consts,)*],
|
||||||
let mut scoped = ::std::vec::Vec::new();
|
global: [#(#global,)*],
|
||||||
|
scoped: [#(#scoped,)*],
|
||||||
#(#const_inserters)*
|
}
|
||||||
#(#global_inserters)*
|
|
||||||
#(#scoped_inserters)*
|
|
||||||
|
|
||||||
Self { consts, global, scoped }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -87,26 +101,17 @@ pub fn builtins_impl(input: TokenStream) -> TokenStream {
|
|||||||
output.into()
|
output.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_const_inserter(
|
fn generate_primop_wrapper(
|
||||||
item_const: &ItemConst,
|
|
||||||
) -> proc_macro2::TokenStream {
|
|
||||||
let name_str = item_const.ident.to_string().from_case(Case::UpperSnake).to_case(Case::Camel);
|
|
||||||
let const_name = &item_const.ident;
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
consts.push((#name_str.to_string(), builtins::#const_name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_fn_wrapper(
|
|
||||||
item_fn: &ItemFn,
|
item_fn: &ItemFn,
|
||||||
) -> syn::Result<(proc_macro2::TokenStream, proc_macro2::TokenStream)> {
|
) -> syn::Result<(proc_macro2::TokenStream, proc_macro2::TokenStream)> {
|
||||||
let fn_name = &item_fn.sig.ident;
|
let fn_name = &item_fn.sig.ident;
|
||||||
let name_str = fn_name.to_string().from_case(Case::Snake).to_case(Case::Camel);
|
let name_str = fn_name
|
||||||
|
.to_string()
|
||||||
|
.from_case(Case::Snake)
|
||||||
|
.to_case(Case::Camel);
|
||||||
let wrapper_name = format_ident!("wrapper_{}", fn_name);
|
let wrapper_name = format_ident!("wrapper_{}", fn_name);
|
||||||
let mod_name = format_ident!("builtins");
|
let mod_name = format_ident!("builtins");
|
||||||
|
|
||||||
let is_pub = matches!(item_fn.vis, Visibility::Public(_));
|
|
||||||
let mut user_args = item_fn.sig.inputs.iter().peekable();
|
let mut user_args = item_fn.sig.inputs.iter().peekable();
|
||||||
|
|
||||||
let has_ctx = if let Some(FnArg::Typed(first_arg)) = user_args.peek() {
|
let has_ctx = if let Some(FnArg::Typed(first_arg)) = user_args.peek() {
|
||||||
@@ -185,15 +190,13 @@ fn generate_fn_wrapper(
|
|||||||
quote! { Ok(#fn_name(#call_args).into()) }
|
quote! { Ok(#fn_name(#call_args).into()) }
|
||||||
};
|
};
|
||||||
|
|
||||||
let fn_type = quote! { fn(&mut Ctx, Vec<::nixjit_eval::Value<Ctx>>) -> ::nixjit_error::Result<::nixjit_eval::Value<Ctx>> };
|
let arity = arg_names.len();
|
||||||
let inserter = if is_pub {
|
let fn_type = quote! { fn(&mut Ctx, Vec<::nixjit_eval::Value>) -> ::nixjit_error::Result<::nixjit_eval::Value> };
|
||||||
quote! { global.push((#name_str.to_string(), #mod_name::#wrapper_name as #fn_type)); }
|
let primop =
|
||||||
} else {
|
quote! { (#name_str, #arity, #mod_name::#wrapper_name as #fn_type) };
|
||||||
quote! { scoped.push((#name_str.to_string(), #mod_name::#wrapper_name as #fn_type)); }
|
|
||||||
};
|
|
||||||
|
|
||||||
let wrapper = quote! {
|
let wrapper = quote! {
|
||||||
pub fn #wrapper_name<Ctx: BuiltinsContext>(ctx: &mut Ctx, mut args: Vec<::nixjit_eval::Value<Ctx>>) -> ::nixjit_error::Result<::nixjit_eval::Value<Ctx>> {
|
pub fn #wrapper_name<Ctx: BuiltinsContext>(ctx: &mut Ctx, mut args: Vec<::nixjit_eval::Value>) -> ::nixjit_error::Result<::nixjit_eval::Value> {
|
||||||
if args.len() != #arg_count {
|
if args.len() != #arg_count {
|
||||||
return Err(::nixjit_error::Error::EvalError(format!("Function '{}' expects {} arguments, but received {}", #name_str, #arg_count, args.len())));
|
return Err(::nixjit_error::Error::EvalError(format!("Function '{}' expects {} arguments, but received {}", #name_str, #arg_count, args.len())));
|
||||||
}
|
}
|
||||||
@@ -203,5 +206,5 @@ fn generate_fn_wrapper(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((inserter, wrapper))
|
Ok((primop, wrapper))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user