feat: rec attrset
This commit is contained in:
@@ -23,7 +23,7 @@ mod value;
|
||||
/// A trait defining the context in which LIR expressions are evaluated.
|
||||
pub trait EvalContext: Sized {
|
||||
/// 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.
|
||||
fn with_with_env<T>(
|
||||
@@ -61,7 +61,7 @@ pub trait Evaluate<Ctx: EvalContext> {
|
||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ExprId {
|
||||
/// Evaluating an `ExprId` simply delegates to the context.
|
||||
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),
|
||||
Var(x) => x.eval(ctx),
|
||||
Path(x) => x.eval(ctx),
|
||||
ExprRef(expr) => ctx.eval(expr),
|
||||
FuncRef(func) => Ok(Value::Func(unsafe { func.clone() })),
|
||||
&ExprRef(expr) => ctx.eval(expr),
|
||||
&FuncRef(func) => Ok(Value::Func(func)),
|
||||
&ArgRef(idx) => Ok(ctx.lookup_arg(idx).clone()),
|
||||
&PrimOp(primop) => Ok(Value::PrimOp(primop)),
|
||||
}
|
||||
@@ -109,7 +109,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::AttrSet {
|
||||
let mut k = k.eval(ctx)?;
|
||||
k.coerce_to_string();
|
||||
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());
|
||||
Ok(result)
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
|
||||
use derive_more::Constructor;
|
||||
use hashbrown::hash_map::Entry;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use itertools::Itertools;
|
||||
|
||||
@@ -13,14 +14,13 @@ use nixjit_value::Symbol;
|
||||
use nixjit_value::{self as p, format_symbol};
|
||||
|
||||
use super::Value;
|
||||
use crate::EvalContext;
|
||||
|
||||
/// A wrapper around a `HashMap` representing a Nix attribute set.
|
||||
///
|
||||
/// It uses `#[repr(transparent)]` to ensure it has the same memory layout
|
||||
/// as `HashMap<String, Value>`.
|
||||
#[repr(transparent)]
|
||||
#[derive(Constructor, PartialEq)]
|
||||
#[derive(Clone, Constructor, PartialEq)]
|
||||
pub struct AttrSet {
|
||||
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 {
|
||||
fn from(data: HashMap<String, Value>) -> Self {
|
||||
Self { data }
|
||||
@@ -80,11 +72,17 @@ impl AttrSet {
|
||||
///
|
||||
/// This method currently uses `todo!()` and will panic if the attribute
|
||||
/// already exists, indicating that duplicate attribute handling is not yet implemented.
|
||||
pub fn push_attr(&mut self, sym: String, val: Value) {
|
||||
if self.data.get(&sym).is_some() {
|
||||
todo!()
|
||||
pub fn push_attr(&mut self, sym: String, val: Value) -> Result<()> {
|
||||
match self.data.entry(sym) {
|
||||
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.
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::EvalContext;
|
||||
///
|
||||
/// This struct captures the state of a function that has received some, but not
|
||||
/// all, of its expected arguments.
|
||||
#[derive(Debug, Constructor)]
|
||||
#[derive(Debug, Clone, Constructor)]
|
||||
pub struct FuncApp {
|
||||
/// The expression ID of the function body to be executed.
|
||||
pub body: ExprId,
|
||||
@@ -23,16 +23,6 @@ pub struct FuncApp {
|
||||
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 {
|
||||
/// Applies more arguments to a partially applied function.
|
||||
///
|
||||
@@ -51,7 +41,7 @@ impl FuncApp {
|
||||
let mut val;
|
||||
let mut args = core::mem::take(args);
|
||||
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;
|
||||
val = ret?;
|
||||
loop {
|
||||
@@ -63,13 +53,13 @@ impl FuncApp {
|
||||
};
|
||||
args.push(arg?);
|
||||
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;
|
||||
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));
|
||||
let (ret_args, ret) = ctx.with_args_env(func.args, |ctx| ctx.eval(func.body));
|
||||
args = ret_args;
|
||||
val = ret?;
|
||||
}
|
||||
|
||||
@@ -9,10 +9,9 @@ use nixjit_value::List as PubList;
|
||||
use nixjit_value::Value as PubValue;
|
||||
|
||||
use super::Value;
|
||||
use crate::EvalContext;
|
||||
|
||||
/// A wrapper around a `Vec<Value>` representing a Nix list.
|
||||
#[derive(Default)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct List {
|
||||
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 {
|
||||
fn from(value: T) -> Self {
|
||||
Self { data: value.into() }
|
||||
|
||||
@@ -43,14 +43,14 @@ pub use primop::*;
|
||||
/// JIT-compiled code. It uses `#[repr(C, u64)]` to ensure a predictable layout,
|
||||
/// with the discriminant serving as a type tag.
|
||||
#[repr(C, u64)]
|
||||
#[derive(IsVariant, TryUnwrap, Unwrap)]
|
||||
#[derive(IsVariant, Clone, TryUnwrap, Unwrap)]
|
||||
pub enum Value {
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
String(String),
|
||||
Null,
|
||||
Thunk(usize),
|
||||
Thunk(ExprId),
|
||||
AttrSet(Rc<AttrSet>),
|
||||
List(Rc<List>),
|
||||
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 {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
use Value::*;
|
||||
@@ -169,7 +148,7 @@ pub enum ValueAsRef<'v> {
|
||||
Bool(bool),
|
||||
String(&'v String),
|
||||
Null,
|
||||
Thunk(usize),
|
||||
Thunk(&'v ExprId),
|
||||
AttrSet(&'v AttrSet),
|
||||
List(&'v List),
|
||||
Catchable(&'v str),
|
||||
@@ -190,7 +169,7 @@ impl Value {
|
||||
Bool(x) => R::Bool(*x),
|
||||
String(x) => R::String(x),
|
||||
Null => R::Null,
|
||||
Thunk(x) => R::Thunk(*x),
|
||||
Thunk(x) => R::Thunk(x),
|
||||
AttrSet(x) => R::AttrSet(x),
|
||||
List(x) => R::List(x),
|
||||
Catchable(x) => R::Catchable(x),
|
||||
@@ -264,7 +243,7 @@ impl Value {
|
||||
ctx.call_primop(func.id, iter.collect::<Result<_>>()?)
|
||||
}
|
||||
}
|
||||
Func(expr) => {
|
||||
&mut Func(expr) => {
|
||||
let mut val;
|
||||
let mut args = Vec::with_capacity(iter.len());
|
||||
args.push(iter.next().unwrap()?);
|
||||
@@ -280,14 +259,14 @@ impl Value {
|
||||
};
|
||||
args.push(arg?);
|
||||
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;
|
||||
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));
|
||||
ctx.with_args_env(func.args, |ctx| ctx.eval(func.body));
|
||||
args = ret_args;
|
||||
val = ret?;
|
||||
}
|
||||
@@ -545,14 +524,14 @@ impl Value {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn coerce_to_string(&mut self) -> &mut Self {
|
||||
pub fn coerce_to_string(&mut self) -> Result<&mut Self> {
|
||||
use Value::*;
|
||||
if let String(_) = self {
|
||||
} else if let Catchable(_) = self {
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
self
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Converts the internal `Value` to its public-facing, serializable
|
||||
|
||||
Reference in New Issue
Block a user