feat: rec attrset
This commit is contained in:
@@ -143,8 +143,7 @@ fn test_attrs() {
|
|||||||
"rec { a = 1; b = a; }",
|
"rec { a = 1; b = a; }",
|
||||||
attrs! {
|
attrs! {
|
||||||
symbol!("a") => int!(1),
|
symbol!("a") => int!(1),
|
||||||
// symbol!("b") => int!(1)
|
symbol!("b") => int!(1)
|
||||||
symbol!("b") => thunk!()
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
test_expr("{ a = 1; }.a", int!(1));
|
test_expr("{ a = 1; }.a", int!(1));
|
||||||
|
|||||||
@@ -27,11 +27,28 @@ pub mod builtins {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn import<Ctx: BuiltinsContext>(ctx: &mut Ctx, path: Value) -> Result<Value> {
|
pub fn import(ctx: &mut impl BuiltinsContext, path: Value) -> Result<Value> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn elem_at(list: Value, idx: Value) -> Result<Value> {
|
fn elem_at(list: Value, idx: Value) -> Result<Value> {
|
||||||
|
let list = list
|
||||||
|
.try_unwrap_list()
|
||||||
|
.map_err(|_| Error::EvalError("expected a list but found ...".to_string()))?;
|
||||||
|
let idx = idx
|
||||||
|
.try_unwrap_int()
|
||||||
|
.map_err(|_| Error::EvalError("expected a int but found ...".to_string()))?;
|
||||||
|
list.get(idx as usize)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
Error::EvalError(format!(
|
||||||
|
"'builtins.elemAt' called with index {idx} on a list of size {}",
|
||||||
|
list.len()
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn elem(elem: Value, list: Value) -> Result<Value> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,16 +72,6 @@ impl Ir {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
unsafe fn unwrap_lir_ref_unchecked(&self) -> &Lir {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
if let Self::Lir(lir) = self {
|
if let Self::Lir(lir) = self {
|
||||||
@@ -214,8 +204,8 @@ impl Context {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
let root = root.tree().expr().unwrap().downgrade(&mut self)?;
|
let root = root.tree().expr().unwrap().downgrade(&mut self)?;
|
||||||
self.resolve(&root)?;
|
self.resolve(root)?;
|
||||||
Ok(EvalContext::eval(&mut self, &root)?.to_public(&mut HashSet::new()))
|
Ok(EvalContext::eval(&mut self, root)?.to_public(&mut HashSet::new()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +213,7 @@ impl DowngradeContext for Context {
|
|||||||
fn new_expr(&mut self, expr: Hir) -> ExprId {
|
fn new_expr(&mut self, expr: Hir) -> ExprId {
|
||||||
let id = unsafe { ExprId::from(self.irs.len()) };
|
let id = unsafe { ExprId::from(self.irs.len()) };
|
||||||
self.irs.push(Ir::Hir(expr).into());
|
self.irs.push(Ir::Hir(expr).into());
|
||||||
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
|
||||||
@@ -234,9 +224,9 @@ impl DowngradeContext for Context {
|
|||||||
f(&self.irs[idx].borrow().unwrap_hir_ref_unchecked(), self)
|
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 {
|
fn with_expr_mut<T>(&mut self, id: ExprId, f: impl FnOnce(&mut Hir, &mut Self) -> T) -> T {
|
||||||
unsafe {
|
unsafe {
|
||||||
let idx = id.clone().raw();
|
let idx = id.raw();
|
||||||
let self_mut = &mut *(self as *mut Self);
|
let self_mut = &mut *(self as *mut Self);
|
||||||
f(
|
f(
|
||||||
&mut self
|
&mut self
|
||||||
@@ -253,11 +243,12 @@ impl DowngradeContext for Context {
|
|||||||
impl ResolveContext for Context {
|
impl ResolveContext for Context {
|
||||||
fn lookup(&self, name: &str) -> LookupResult {
|
fn lookup(&self, name: &str) -> LookupResult {
|
||||||
let mut arg_idx = 0;
|
let mut arg_idx = 0;
|
||||||
|
let mut has_with = false;
|
||||||
for scope in self.scopes.iter().rev() {
|
for scope in self.scopes.iter().rev() {
|
||||||
match scope {
|
match scope {
|
||||||
Scope::Let(scope) => {
|
Scope::Let(scope) => {
|
||||||
if let Some(expr) = scope.get(name) {
|
if let Some(&expr) = scope.get(name) {
|
||||||
return LookupResult::Expr(unsafe { expr.clone() });
|
return LookupResult::Expr(expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Scope::Arg(ident) => {
|
Scope::Arg(ident) => {
|
||||||
@@ -266,15 +257,19 @@ impl ResolveContext for Context {
|
|||||||
}
|
}
|
||||||
arg_idx += 1;
|
arg_idx += 1;
|
||||||
}
|
}
|
||||||
Scope::With => return LookupResult::Unknown,
|
Scope::With => has_with = true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LookupResult::NotFound
|
if has_with {
|
||||||
|
LookupResult::Unknown
|
||||||
|
} else {
|
||||||
|
LookupResult::NotFound
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_dep(&mut self, expr: &ExprId, dep: ExprId) {
|
fn new_dep(&mut self, expr: ExprId, dep: ExprId) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let expr = expr.clone().raw();
|
let expr = expr.raw();
|
||||||
let dep = dep.raw();
|
let dep = dep.raw();
|
||||||
let expr = *self.nodes.get_unchecked(expr);
|
let expr = *self.nodes.get_unchecked(expr);
|
||||||
let dep = *self.nodes.get_unchecked(dep);
|
let dep = *self.nodes.get_unchecked(dep);
|
||||||
@@ -282,9 +277,9 @@ impl ResolveContext for Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve(&mut self, expr: &ExprId) -> Result<()> {
|
fn resolve(&mut self, expr: ExprId) -> Result<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let idx = expr.clone().raw();
|
let idx = expr.raw();
|
||||||
let self_mut = &mut *(self as *mut Self);
|
let self_mut = &mut *(self as *mut Self);
|
||||||
replace_with_and_return(
|
replace_with_and_return(
|
||||||
&mut *self.irs.get_unchecked(idx).borrow_mut(),
|
&mut *self.irs.get_unchecked(idx).borrow_mut(),
|
||||||
@@ -294,7 +289,9 @@ impl ResolveContext for Context {
|
|||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
|ir| {
|
|ir| {
|
||||||
let hir = ir.unwrap_hir_unchecked();
|
let Ir::Hir(hir) = ir else {
|
||||||
|
return (Ok(()), ir);
|
||||||
|
};
|
||||||
match hir.resolve(self_mut) {
|
match hir.resolve(self_mut) {
|
||||||
Ok(lir) => (Ok(()), Ir::Lir(lir)),
|
Ok(lir) => (Ok(()), Ir::Lir(lir)),
|
||||||
Err(err) => (
|
Err(err) => (
|
||||||
@@ -310,8 +307,8 @@ impl ResolveContext for Context {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_func(&mut self, body: &ExprId, param: Param) {
|
fn new_func(&mut self, body: ExprId, param: Param) {
|
||||||
self.funcs.insert(unsafe { body.clone() }, param);
|
self.funcs.insert(body, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_let_env<'a, T>(
|
fn with_let_env<'a, T>(
|
||||||
@@ -321,7 +318,7 @@ impl ResolveContext for Context {
|
|||||||
) -> 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(), unsafe { expr.clone() });
|
scope.insert(name.clone(), *expr);
|
||||||
}
|
}
|
||||||
self.scopes.push(Scope::Let(scope));
|
self.scopes.push(Scope::Let(scope));
|
||||||
let res = f(self);
|
let res = f(self);
|
||||||
@@ -347,8 +344,8 @@ impl ResolveContext for Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EvalContext for Context {
|
impl EvalContext for Context {
|
||||||
fn eval(&mut self, expr: &ExprId) -> Result<nixjit_eval::Value> {
|
fn eval(&mut self, expr: ExprId) -> Result<nixjit_eval::Value> {
|
||||||
let idx = unsafe { expr.clone().raw() };
|
let idx = unsafe { expr.raw() };
|
||||||
let lir = unsafe {
|
let lir = unsafe {
|
||||||
&*(self
|
&*(self
|
||||||
.irs
|
.irs
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ mod value;
|
|||||||
/// A trait defining the context in which LIR expressions are evaluated.
|
/// A trait defining the context in which LIR expressions are evaluated.
|
||||||
pub trait EvalContext: Sized {
|
pub trait EvalContext: Sized {
|
||||||
/// Evaluates an expression by its ID.
|
/// Evaluates an expression by its ID.
|
||||||
fn eval(&mut self, expr: &ExprId) -> Result<Value>;
|
fn eval(&mut self, expr: ExprId) -> Result<Value>;
|
||||||
|
|
||||||
/// Enters a `with` scope for the duration of a closure's execution.
|
/// Enters a `with` scope for the duration of a closure's execution.
|
||||||
fn with_with_env<T>(
|
fn with_with_env<T>(
|
||||||
@@ -61,7 +61,7 @@ pub trait Evaluate<Ctx: EvalContext> {
|
|||||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ExprId {
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ExprId {
|
||||||
/// Evaluating an `ExprId` simply delegates to the context.
|
/// Evaluating an `ExprId` simply delegates to the context.
|
||||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||||
ctx.eval(self)
|
ctx.eval(*self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,8 +85,8 @@ 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) => ctx.eval(expr),
|
&ExprRef(expr) => ctx.eval(expr),
|
||||||
FuncRef(func) => Ok(Value::Func(unsafe { func.clone() })),
|
&FuncRef(func) => Ok(Value::Func(func)),
|
||||||
&ArgRef(idx) => Ok(ctx.lookup_arg(idx).clone()),
|
&ArgRef(idx) => Ok(ctx.lookup_arg(idx).clone()),
|
||||||
&PrimOp(primop) => Ok(Value::PrimOp(primop)),
|
&PrimOp(primop) => Ok(Value::PrimOp(primop)),
|
||||||
}
|
}
|
||||||
@@ -109,7 +109,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::AttrSet {
|
|||||||
let mut k = k.eval(ctx)?;
|
let mut k = k.eval(ctx)?;
|
||||||
k.coerce_to_string();
|
k.coerce_to_string();
|
||||||
let v_eval_result = v.eval(ctx)?;
|
let v_eval_result = v.eval(ctx)?;
|
||||||
attrs.push_attr(k.unwrap_string(), v_eval_result);
|
attrs.push_attr(k.unwrap_string(), v_eval_result)?;
|
||||||
}
|
}
|
||||||
let result = Value::AttrSet(attrs.into());
|
let result = Value::AttrSet(attrs.into());
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use std::fmt::Debug;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use derive_more::Constructor;
|
use derive_more::Constructor;
|
||||||
|
use hashbrown::hash_map::Entry;
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
@@ -13,14 +14,13 @@ use nixjit_value::Symbol;
|
|||||||
use nixjit_value::{self as p, format_symbol};
|
use nixjit_value::{self as p, format_symbol};
|
||||||
|
|
||||||
use super::Value;
|
use super::Value;
|
||||||
use crate::EvalContext;
|
|
||||||
|
|
||||||
/// A wrapper around a `HashMap` representing a Nix attribute set.
|
/// A wrapper around a `HashMap` representing a Nix attribute set.
|
||||||
///
|
///
|
||||||
/// It uses `#[repr(transparent)]` to ensure it has the same memory layout
|
/// It uses `#[repr(transparent)]` to ensure it has the same memory layout
|
||||||
/// as `HashMap<String, Value>`.
|
/// as `HashMap<String, Value>`.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Constructor, PartialEq)]
|
#[derive(Clone, Constructor, PartialEq)]
|
||||||
pub struct AttrSet {
|
pub struct AttrSet {
|
||||||
data: HashMap<String, Value>,
|
data: HashMap<String, Value>,
|
||||||
}
|
}
|
||||||
@@ -40,14 +40,6 @@ impl Debug for AttrSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for AttrSet {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
AttrSet {
|
|
||||||
data: self.data.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<HashMap<String, Value>> for AttrSet {
|
impl From<HashMap<String, Value>> for AttrSet {
|
||||||
fn from(data: HashMap<String, Value>) -> Self {
|
fn from(data: HashMap<String, Value>) -> Self {
|
||||||
Self { data }
|
Self { data }
|
||||||
@@ -80,11 +72,17 @@ impl AttrSet {
|
|||||||
///
|
///
|
||||||
/// This method currently uses `todo!()` and will panic if the attribute
|
/// This method currently uses `todo!()` and will panic if the attribute
|
||||||
/// already exists, indicating that duplicate attribute handling is not yet implemented.
|
/// already exists, indicating that duplicate attribute handling is not yet implemented.
|
||||||
pub fn push_attr(&mut self, sym: String, val: Value) {
|
pub fn push_attr(&mut self, sym: String, val: Value) -> Result<()> {
|
||||||
if self.data.get(&sym).is_some() {
|
match self.data.entry(sym) {
|
||||||
todo!()
|
Entry::Occupied(occupied) => Err(Error::EvalError(format!(
|
||||||
|
"attribute '{}' already defined",
|
||||||
|
format_symbol(occupied.key())
|
||||||
|
))),
|
||||||
|
Entry::Vacant(vacant) => {
|
||||||
|
vacant.insert(val);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.data.insert(sym, val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs a deep selection of an attribute from a nested set.
|
/// Performs a deep selection of an attribute from a nested set.
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use crate::EvalContext;
|
|||||||
///
|
///
|
||||||
/// This struct captures the state of a function that has received some, but not
|
/// This struct captures the state of a function that has received some, but not
|
||||||
/// all, of its expected arguments.
|
/// all, of its expected arguments.
|
||||||
#[derive(Debug, Constructor)]
|
#[derive(Debug, Clone, Constructor)]
|
||||||
pub struct FuncApp {
|
pub struct FuncApp {
|
||||||
/// The expression ID of the function body to be executed.
|
/// The expression ID of the function body to be executed.
|
||||||
pub body: ExprId,
|
pub body: ExprId,
|
||||||
@@ -23,16 +23,6 @@ pub struct FuncApp {
|
|||||||
pub frame: Vec<Value>,
|
pub frame: Vec<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for FuncApp {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
body: unsafe { self.body.clone() },
|
|
||||||
args: self.args.clone(),
|
|
||||||
frame: self.frame.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FuncApp {
|
impl FuncApp {
|
||||||
/// Applies more arguments to a partially applied function.
|
/// Applies more arguments to a partially applied function.
|
||||||
///
|
///
|
||||||
@@ -51,7 +41,7 @@ impl FuncApp {
|
|||||||
let mut val;
|
let mut val;
|
||||||
let mut args = core::mem::take(args);
|
let mut args = core::mem::take(args);
|
||||||
args.push(iter.next().unwrap()?);
|
args.push(iter.next().unwrap()?);
|
||||||
let (ret_args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(expr));
|
let (ret_args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(*expr));
|
||||||
args = ret_args;
|
args = ret_args;
|
||||||
val = ret?;
|
val = ret?;
|
||||||
loop {
|
loop {
|
||||||
@@ -63,13 +53,13 @@ impl FuncApp {
|
|||||||
};
|
};
|
||||||
args.push(arg?);
|
args.push(arg?);
|
||||||
if let Value::Func(expr) = val {
|
if let Value::Func(expr) = val {
|
||||||
let (ret_args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(&expr));
|
let (ret_args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(expr));
|
||||||
args = ret_args;
|
args = ret_args;
|
||||||
val = ret?;
|
val = ret?;
|
||||||
} else if let Value::FuncApp(func) = val {
|
} else if let Value::FuncApp(func) = val {
|
||||||
let mut func = Rc::unwrap_or_clone(func);
|
let mut func = Rc::unwrap_or_clone(func);
|
||||||
func.args.push(args.pop().unwrap());
|
func.args.push(args.pop().unwrap());
|
||||||
let (ret_args, ret) = ctx.with_args_env(func.args, |ctx| ctx.eval(&func.body));
|
let (ret_args, ret) = ctx.with_args_env(func.args, |ctx| ctx.eval(func.body));
|
||||||
args = ret_args;
|
args = ret_args;
|
||||||
val = ret?;
|
val = ret?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,9 @@ use nixjit_value::List as PubList;
|
|||||||
use nixjit_value::Value as PubValue;
|
use nixjit_value::Value as PubValue;
|
||||||
|
|
||||||
use super::Value;
|
use super::Value;
|
||||||
use crate::EvalContext;
|
|
||||||
|
|
||||||
/// A wrapper around a `Vec<Value>` representing a Nix list.
|
/// A wrapper around a `Vec<Value>` representing a Nix list.
|
||||||
#[derive(Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct List {
|
pub struct List {
|
||||||
data: Vec<Value>,
|
data: Vec<Value>,
|
||||||
}
|
}
|
||||||
@@ -27,14 +26,6 @@ impl Debug for List {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for List {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
data: self.data.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Into<Vec<Value>>> From<T> for List {
|
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() }
|
||||||
|
|||||||
@@ -43,14 +43,14 @@ pub use primop::*;
|
|||||||
/// JIT-compiled code. It uses `#[repr(C, u64)]` to ensure a predictable layout,
|
/// JIT-compiled code. It uses `#[repr(C, u64)]` to ensure a predictable layout,
|
||||||
/// with the discriminant serving as a type tag.
|
/// with the discriminant serving as a type tag.
|
||||||
#[repr(C, u64)]
|
#[repr(C, u64)]
|
||||||
#[derive(IsVariant, TryUnwrap, Unwrap)]
|
#[derive(IsVariant, Clone, TryUnwrap, Unwrap)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Int(i64),
|
Int(i64),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
String(String),
|
String(String),
|
||||||
Null,
|
Null,
|
||||||
Thunk(usize),
|
Thunk(ExprId),
|
||||||
AttrSet(Rc<AttrSet>),
|
AttrSet(Rc<AttrSet>),
|
||||||
List(Rc<List>),
|
List(Rc<List>),
|
||||||
Catchable(String),
|
Catchable(String),
|
||||||
@@ -81,27 +81,6 @@ impl Debug for Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Value {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
use Value::*;
|
|
||||||
match self {
|
|
||||||
AttrSet(attrs) => AttrSet(attrs.clone()),
|
|
||||||
List(list) => List(list.clone()),
|
|
||||||
Catchable(catchable) => Catchable(catchable.clone()),
|
|
||||||
&Int(x) => Int(x),
|
|
||||||
&Float(x) => Float(x),
|
|
||||||
&Bool(x) => Bool(x),
|
|
||||||
String(x) => String(x.clone()),
|
|
||||||
Null => Null,
|
|
||||||
&Thunk(expr) => Thunk(expr),
|
|
||||||
&PrimOp(primop) => PrimOp(primop),
|
|
||||||
PrimOpApp(primop) => PrimOpApp(primop.clone()),
|
|
||||||
Func(expr) => Func(unsafe { expr.clone() }),
|
|
||||||
FuncApp(func) => FuncApp(func.clone()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for Value {
|
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::*;
|
||||||
@@ -169,7 +148,7 @@ pub enum ValueAsRef<'v> {
|
|||||||
Bool(bool),
|
Bool(bool),
|
||||||
String(&'v String),
|
String(&'v String),
|
||||||
Null,
|
Null,
|
||||||
Thunk(usize),
|
Thunk(&'v ExprId),
|
||||||
AttrSet(&'v AttrSet),
|
AttrSet(&'v AttrSet),
|
||||||
List(&'v List),
|
List(&'v List),
|
||||||
Catchable(&'v str),
|
Catchable(&'v str),
|
||||||
@@ -190,7 +169,7 @@ impl Value {
|
|||||||
Bool(x) => R::Bool(*x),
|
Bool(x) => R::Bool(*x),
|
||||||
String(x) => R::String(x),
|
String(x) => R::String(x),
|
||||||
Null => R::Null,
|
Null => R::Null,
|
||||||
Thunk(x) => R::Thunk(*x),
|
Thunk(x) => R::Thunk(x),
|
||||||
AttrSet(x) => R::AttrSet(x),
|
AttrSet(x) => R::AttrSet(x),
|
||||||
List(x) => R::List(x),
|
List(x) => R::List(x),
|
||||||
Catchable(x) => R::Catchable(x),
|
Catchable(x) => R::Catchable(x),
|
||||||
@@ -264,7 +243,7 @@ impl Value {
|
|||||||
ctx.call_primop(func.id, iter.collect::<Result<_>>()?)
|
ctx.call_primop(func.id, iter.collect::<Result<_>>()?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Func(expr) => {
|
&mut Func(expr) => {
|
||||||
let mut val;
|
let mut val;
|
||||||
let mut args = Vec::with_capacity(iter.len());
|
let mut args = Vec::with_capacity(iter.len());
|
||||||
args.push(iter.next().unwrap()?);
|
args.push(iter.next().unwrap()?);
|
||||||
@@ -280,14 +259,14 @@ impl Value {
|
|||||||
};
|
};
|
||||||
args.push(arg?);
|
args.push(arg?);
|
||||||
if let Value::Func(expr) = val {
|
if let Value::Func(expr) = val {
|
||||||
let (ret_args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(&expr));
|
let (ret_args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(expr));
|
||||||
args = ret_args;
|
args = ret_args;
|
||||||
val = ret?;
|
val = ret?;
|
||||||
} else if let Value::FuncApp(func) = val {
|
} else if let Value::FuncApp(func) = val {
|
||||||
let mut func = Rc::unwrap_or_clone(func);
|
let mut func = Rc::unwrap_or_clone(func);
|
||||||
func.args.push(args.pop().unwrap());
|
func.args.push(args.pop().unwrap());
|
||||||
let (ret_args, ret) =
|
let (ret_args, ret) =
|
||||||
ctx.with_args_env(func.args, |ctx| ctx.eval(&func.body));
|
ctx.with_args_env(func.args, |ctx| ctx.eval(func.body));
|
||||||
args = ret_args;
|
args = ret_args;
|
||||||
val = ret?;
|
val = ret?;
|
||||||
}
|
}
|
||||||
@@ -545,14 +524,14 @@ impl Value {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn coerce_to_string(&mut self) -> &mut Self {
|
pub fn coerce_to_string(&mut self) -> Result<&mut Self> {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
if let String(_) = self {
|
if let String(_) = self {
|
||||||
} else if let Catchable(_) = self {
|
} else if let Catchable(_) = self {
|
||||||
} else {
|
} else {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
self
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts the internal `Value` to its public-facing, serializable
|
/// Converts the internal `Value` to its public-facing, serializable
|
||||||
|
|||||||
@@ -41,7 +41,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The `ir!` macro generates the `Hir` enum and related structs and traits.
|
// The `ir!` macro generates the `Hir` enum and related structs and traits.
|
||||||
@@ -128,7 +128,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()
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
//! They are helpers to the main `Downgrade` trait implementations.
|
//! They are helpers to the main `Downgrade` trait implementations.
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
use hashbrown::hash_map::Entry;
|
||||||
|
|
||||||
|
use nixjit_value::format_symbol;
|
||||||
use rnix::ast;
|
use rnix::ast;
|
||||||
|
|
||||||
use nixjit_error::{Error, Result};
|
use nixjit_error::{Error, Result};
|
||||||
@@ -128,19 +130,22 @@ pub fn downgrade_inherit(
|
|||||||
// 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: unsafe { expr.clone() },
|
expr,
|
||||||
attrpath: vec![Attr::Str(ident.clone())],
|
attrpath: vec![Attr::Str(ident.clone())],
|
||||||
default: None,
|
default: None,
|
||||||
}
|
}
|
||||||
.to_hir()
|
.to_hir()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if stcs.insert(ident, ctx.new_expr(expr)).is_some() {
|
match stcs.entry(ident) {
|
||||||
// TODO: Handle or error on duplicate attribute definitions.
|
Entry::Occupied(occupied) => return Err(Error::EvalError(format!(
|
||||||
todo!()
|
"attribute '{}' already defined",
|
||||||
}
|
format_symbol(occupied.key())
|
||||||
|
))),
|
||||||
|
Entry::Vacant(vacant) => vacant.insert(ctx.new_expr(expr))
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,20 +20,10 @@ use nixjit_value::Const as PubConst;
|
|||||||
///
|
///
|
||||||
/// Using a newtype wrapper like this prevents accidentally mixing up different kinds of indices.
|
/// Using a newtype wrapper like this prevents accidentally mixing up different kinds of indices.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct ExprId(usize);
|
pub struct ExprId(usize);
|
||||||
|
|
||||||
impl ExprId {
|
impl ExprId {
|
||||||
/// Creates a clone of the `ExprId`.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
/// This is a shallow copy of the index. The caller must ensure that the lifetime
|
|
||||||
/// and validity of the expression being referenced are handled correctly.
|
|
||||||
#[inline(always)]
|
|
||||||
pub unsafe fn clone(&self) -> Self {
|
|
||||||
Self(self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the raw `usize` index.
|
/// Returns the raw `usize` index.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
|||||||
@@ -64,18 +64,18 @@ pub enum LookupResult {
|
|||||||
/// scopes, dependencies, and the resolution of expressions themselves.
|
/// scopes, dependencies, and the resolution of expressions themselves.
|
||||||
pub trait ResolveContext {
|
pub trait ResolveContext {
|
||||||
/// Records a dependency of one expression on another.
|
/// Records a dependency of one expression on another.
|
||||||
fn new_dep(&mut self, expr: &ExprId, dep: ExprId);
|
fn new_dep(&mut self, expr: ExprId, dep: ExprId);
|
||||||
|
|
||||||
/// Creates a new function, associating a parameter specification with a body expression.
|
/// Creates a new function, associating a parameter specification with a body expression.
|
||||||
fn new_func(&mut self, body: &ExprId, param: Param);
|
fn new_func(&mut self, body: ExprId, param: Param);
|
||||||
|
|
||||||
/// Triggers the resolution of a given expression.
|
/// Triggers the resolution of a given expression.
|
||||||
fn resolve(&mut self, expr: &ExprId) -> Result<()>;
|
fn resolve(&mut self, expr: ExprId) -> Result<()>;
|
||||||
|
|
||||||
/// Looks up a variable by name in the current scope.
|
/// Looks up a variable by name in the current scope.
|
||||||
fn lookup(&self, name: &str) -> LookupResult;
|
fn lookup(&self, name: &str) -> LookupResult;
|
||||||
|
|
||||||
/// Enters a `with` scope for the duration of a closure's execution.
|
/// Enters a `with` scope for the duration of a closure.
|
||||||
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);
|
||||||
|
|
||||||
/// Enters a `let` scope with a given set of bindings for the duration of a closure.
|
/// Enters a `let` scope with a given set of bindings for the duration of a closure.
|
||||||
@@ -125,19 +125,26 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for hir::Hir {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves an `AttrSet`. If it's recursive, resolution is more complex (and currently a TODO).
|
/// Resolves an `AttrSet` by resolving all key and value expressions.
|
||||||
/// Otherwise, it resolves all key and value expressions.
|
|
||||||
impl<Ctx: ResolveContext> Resolve<Ctx> for AttrSet {
|
impl<Ctx: ResolveContext> Resolve<Ctx> for AttrSet {
|
||||||
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
if self.rec {
|
if self.rec {
|
||||||
// TODO: Implement resolution for recursive attribute sets.
|
ctx.with_let_env(self.stcs.iter(), |ctx| {
|
||||||
// This requires setting up a recursive scope where attributes can refer to each other.
|
for &id in self.stcs.values() {
|
||||||
todo!()
|
ctx.resolve(id)?;
|
||||||
|
}
|
||||||
|
for &(k, v) in self.dyns.iter() {
|
||||||
|
ctx.resolve(k)?;
|
||||||
|
ctx.resolve(v)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
Ok(self.to_lir())
|
||||||
} 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)?;
|
||||||
}
|
}
|
||||||
@@ -149,7 +156,7 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for AttrSet {
|
|||||||
/// Resolves a `List` by resolving each of its items.
|
/// Resolves a `List` by resolving each of its items.
|
||||||
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())
|
||||||
@@ -159,9 +166,9 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for List {
|
|||||||
/// Resolves a `HasAttr` expression by resolving the LHS and any dynamic attributes in the path.
|
/// Resolves a `HasAttr` expression by resolving the LHS and any dynamic attributes in the path.
|
||||||
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)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,8 +179,8 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for HasAttr {
|
|||||||
/// Resolves a `BinOp` by resolving its left and right hand sides.
|
/// Resolves a `BinOp` by resolving its left and right hand sides.
|
||||||
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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,7 +188,7 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for BinOp {
|
|||||||
/// Resolves a `UnOp` by resolving its right hand side.
|
/// Resolves a `UnOp` by resolving its right hand side.
|
||||||
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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,13 +197,13 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for UnOp {
|
|||||||
/// attributes in the path, and the default value if it exists.
|
/// attributes in the path, and the default value if it exists.
|
||||||
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(ref expr) = self.default {
|
if let Some(expr) = self.default {
|
||||||
ctx.resolve(expr)?;
|
ctx.resolve(expr)?;
|
||||||
}
|
}
|
||||||
Ok(self.to_lir())
|
Ok(self.to_lir())
|
||||||
@@ -206,9 +213,9 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for Select {
|
|||||||
/// Resolves an `If` expression by resolving the condition, consequence, and alternative.
|
/// Resolves an `If` expression by resolving the condition, consequence, and alternative.
|
||||||
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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,8 +224,8 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for If {
|
|||||||
/// It then registers the function with the context.
|
/// It then registers the function with the context.
|
||||||
impl<Ctx: ResolveContext> Resolve<Ctx> for Func {
|
impl<Ctx: ResolveContext> Resolve<Ctx> for Func {
|
||||||
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
ctx.with_param_env(self.param.ident.clone(), |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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,8 +233,8 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for Func {
|
|||||||
/// Resolves a `Call` by resolving the function and all of its arguments.
|
/// Resolves a `Call` by resolving the function and all of its arguments.
|
||||||
impl<Ctx: ResolveContext> Resolve<Ctx> for Call {
|
impl<Ctx: ResolveContext> Resolve<Ctx> for Call {
|
||||||
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
ctx.resolve(&self.func)?;
|
ctx.resolve(self.func)?;
|
||||||
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())
|
||||||
@@ -238,8 +245,8 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for Call {
|
|||||||
/// The body is resolved within a special "with" scope.
|
/// The body is resolved within a special "with" scope.
|
||||||
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?;
|
||||||
// Optimization: if the `with` environment was not actually used by any variable
|
// Optimization: if the `with` environment was not actually used by any variable
|
||||||
// lookup in the body, we can elide the `With` node entirely.
|
// lookup in the body, we can elide the `With` node entirely.
|
||||||
@@ -254,8 +261,8 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for With {
|
|||||||
/// Resolves an `Assert` by resolving the assertion condition and the body.
|
/// Resolves an `Assert` by resolving the assertion condition and the body.
|
||||||
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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,7 +270,7 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for Assert {
|
|||||||
/// Resolves a `ConcatStrings` by resolving each part.
|
/// Resolves a `ConcatStrings` by resolving each part.
|
||||||
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())
|
||||||
@@ -289,7 +296,7 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for Var {
|
|||||||
/// Resolves a `Path` by resolving the underlying expression that defines the path's content.
|
/// Resolves a `Path` by resolving the underlying expression that defines the path's content.
|
||||||
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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -299,10 +306,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)
|
||||||
})?;
|
})?;
|
||||||
// The `let` expression itself evaluates to its body.
|
// The `let` expression itself evaluates to its body.
|
||||||
Ok(Lir::ExprRef(self.body))
|
Ok(Lir::ExprRef(self.body))
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ use convert_case::{Case, Casing};
|
|||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
use quote::{ToTokens, format_ident, quote};
|
use quote::{ToTokens, format_ident, quote};
|
||||||
use syn::{FnArg, Item, ItemFn, ItemMod, Pat, PatType, Type, Visibility, parse_macro_input};
|
use syn::{
|
||||||
|
FnArg, Item, ItemFn, ItemMod, Pat, PatIdent, PatType, Type, Visibility, parse_macro_input,
|
||||||
|
};
|
||||||
|
|
||||||
/// The implementation of the `#[builtins]` macro.
|
/// The implementation of the `#[builtins]` macro.
|
||||||
pub fn builtins_impl(input: TokenStream) -> TokenStream {
|
pub fn builtins_impl(input: TokenStream) -> TokenStream {
|
||||||
@@ -144,9 +146,15 @@ fn generate_primop_wrapper(
|
|||||||
|
|
||||||
// Check if the first argument is a context `&mut Ctx`.
|
// Check if the first argument is a context `&mut Ctx`.
|
||||||
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() {
|
||||||
if let Type::Reference(_) = *first_arg.ty {
|
if let (Type::Reference(_), Pat::Ident(PatIdent { ident, .. })) =
|
||||||
user_args.next(); // Consume the context argument
|
(&*first_arg.ty, &*first_arg.pat)
|
||||||
true
|
{
|
||||||
|
if ident == "ctx" {
|
||||||
|
user_args.next();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -163,17 +171,7 @@ fn generate_primop_wrapper(
|
|||||||
|
|
||||||
// Generate code to unpack and convert arguments from the `Vec<Value>`.
|
// Generate code to unpack and convert arguments from the `Vec<Value>`.
|
||||||
let arg_unpacks = arg_pats.iter().enumerate().map(|(i, arg)| {
|
let arg_unpacks = arg_pats.iter().enumerate().map(|(i, arg)| {
|
||||||
let arg_name = match &arg {
|
let arg_name = format_ident!("_arg{}", i, span = Span::call_site());
|
||||||
FnArg::Typed(PatType { pat, .. }) => {
|
|
||||||
if let Pat::Ident(pat_ident) = &**pat {
|
|
||||||
pat_ident.ident.clone()
|
|
||||||
} else {
|
|
||||||
// Create a placeholder name if the pattern is not a simple ident.
|
|
||||||
format_ident!("arg{}", i, span = Span::call_site())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => format_ident!("arg{}", i, span = Span::call_site()),
|
|
||||||
};
|
|
||||||
let arg_ty = match &arg {
|
let arg_ty = match &arg {
|
||||||
FnArg::Typed(PatType { ty, .. }) => ty,
|
FnArg::Typed(PatType { ty, .. }) => ty,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@@ -190,12 +188,8 @@ fn generate_primop_wrapper(
|
|||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, arg)| match &arg {
|
.map(|(i, arg)| match &arg {
|
||||||
FnArg::Typed(PatType { pat, .. }) => {
|
FnArg::Typed(PatType { .. }) => {
|
||||||
if let Pat::Ident(pat_ident) = &**pat {
|
format_ident!("_arg{}", i, span = Span::call_site())
|
||||||
pat_ident.ident.clone()
|
|
||||||
} else {
|
|
||||||
format_ident!("arg{}", i, span = Span::call_site())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user