feat: TODO
This commit is contained in:
@@ -12,9 +12,9 @@ use std::rc::Rc;
|
||||
use hashbrown::HashMap;
|
||||
|
||||
use nixjit_error::{Error, Result};
|
||||
use nixjit_ir::{self as ir, ExprId, PrimOpId, StackIdx};
|
||||
use nixjit_ir::{self as ir, ExprId, PrimOpId, SymId};
|
||||
use nixjit_lir as lir;
|
||||
use nixjit_value::{Const, format_symbol};
|
||||
use nixjit_value::format_symbol;
|
||||
|
||||
pub use crate::value::*;
|
||||
|
||||
@@ -22,12 +22,13 @@ mod value;
|
||||
|
||||
/// A trait defining the context in which LIR expressions are evaluated.
|
||||
pub trait EvalContext {
|
||||
fn eval_root(self, expr: ExprId) -> Result<Value>;
|
||||
fn eval(&mut self, id: ExprId) -> Result<Value>;
|
||||
|
||||
/// Evaluates an expression by its ID.
|
||||
fn eval(&mut self, expr: ExprId) -> Result<Value>;
|
||||
fn resolve(&mut self, id: ExprId) -> Result<ValueId>;
|
||||
|
||||
fn call(&mut self, func: ExprId, arg: Option<Value>, frame: StackFrame) -> Result<Value>;
|
||||
fn force(&mut self, id: ValueId) -> Result<Value>;
|
||||
|
||||
fn call(&mut self, func: ValueId, arg: Value) -> Result<Value>;
|
||||
|
||||
/// Enters a `with` scope for the duration of a closure's execution.
|
||||
fn with_with_env<T>(
|
||||
@@ -36,18 +37,14 @@ pub trait EvalContext {
|
||||
f: impl FnOnce(&mut Self) -> T,
|
||||
) -> T;
|
||||
|
||||
/// Looks up a stack slot on the current stack frame.
|
||||
fn lookup_stack(&self, idx: StackIdx) -> &Value;
|
||||
|
||||
fn capture_stack(&self) -> &StackFrame;
|
||||
|
||||
/// Looks up an identifier in the current `with` scope chain.
|
||||
fn lookup_with<'a>(&'a self, ident: &str) -> Option<&'a Value>;
|
||||
fn lookup_with<'a>(&'a self, ident: SymId) -> Option<&'a Value>;
|
||||
|
||||
/// Calls a primitive operation (builtin) by its ID.
|
||||
fn call_primop(&mut self, id: PrimOpId, args: Args) -> Result<Value>;
|
||||
|
||||
fn get_primop_arity(&self, id: PrimOpId) -> usize;
|
||||
fn new_sym(&mut self, sym: String) -> SymId;
|
||||
fn get_sym(&self, id: SymId) -> &str;
|
||||
}
|
||||
|
||||
/// A trait for types that can be evaluated within an `EvalContext`.
|
||||
@@ -83,18 +80,12 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for lir::Lir {
|
||||
Str(x) => x.eval(ctx),
|
||||
Var(x) => x.eval(ctx),
|
||||
Path(x) => x.eval(ctx),
|
||||
&StackRef(idx) => {
|
||||
let mut val = ctx.lookup_stack(idx).clone();
|
||||
val.force(ctx)?;
|
||||
Ok(val)
|
||||
}
|
||||
&ExprRef(expr) => ctx.eval(expr),
|
||||
&FuncRef(body) => Ok(Value::Closure(
|
||||
Closure::new(body, ctx.capture_stack().clone()).into(),
|
||||
)),
|
||||
&FuncRef(body) => ctx.resolve(body).map(Value::Closure),
|
||||
&Arg(_) => unreachable!(),
|
||||
&PrimOp(primop) => Ok(Value::PrimOp(primop)),
|
||||
&Thunk(id) => Ok(Value::Thunk(id)),
|
||||
&Thunk(id) => ctx.resolve(id).map(Value::Thunk),
|
||||
&StackRef(idx) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,15 +96,17 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::AttrSet {
|
||||
let mut attrs = AttrSet::new(
|
||||
self.stcs
|
||||
.iter()
|
||||
.map(|(k, v)| {
|
||||
.map(|(&k, &v)| {
|
||||
let eval_result = v.eval(ctx);
|
||||
Ok((k.clone(), eval_result?))
|
||||
Ok((k, eval_result?))
|
||||
})
|
||||
.collect::<Result<_>>()?,
|
||||
);
|
||||
for (k, v) in self.dyns.iter() {
|
||||
let v = v.eval(ctx)?;
|
||||
attrs.push_attr(k.eval(ctx)?.force_string_no_ctx()?, v)?;
|
||||
let sym = k.eval(ctx)?.force_string_no_ctx()?;
|
||||
let sym = ctx.new_sym(sym);
|
||||
attrs.push_attr(sym, v, ctx)?;
|
||||
}
|
||||
let result = Value::AttrSet(attrs.into());
|
||||
Ok(result)
|
||||
@@ -138,9 +131,14 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::HasAttr {
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||
use ir::Attr::*;
|
||||
let mut val = self.lhs.eval(ctx)?;
|
||||
val.has_attr(self.rhs.iter().map(|attr| match attr {
|
||||
Str(ident) => Ok(Value::String(ident.clone())),
|
||||
Dynamic(expr) => expr.eval(ctx),
|
||||
val.has_attr(self.rhs.iter().map(|attr| {
|
||||
match attr {
|
||||
&Str(ident) => Ok(ident),
|
||||
Dynamic(expr) => expr
|
||||
.eval(ctx)?
|
||||
.force_string_no_ctx()
|
||||
.map(|sym| ctx.new_sym(sym)),
|
||||
}
|
||||
}))?;
|
||||
Ok(val)
|
||||
}
|
||||
@@ -165,9 +163,9 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::BinOp {
|
||||
}
|
||||
Mul => lhs.mul(rhs)?,
|
||||
Div => lhs.div(rhs)?,
|
||||
Eq => Value::eq(&mut lhs, rhs),
|
||||
Eq => lhs.eq(rhs),
|
||||
Neq => {
|
||||
Value::eq(&mut lhs, rhs);
|
||||
lhs.eq(rhs);
|
||||
let _ = lhs.not();
|
||||
}
|
||||
Lt => lhs.lt(rhs)?,
|
||||
@@ -182,12 +180,12 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::BinOp {
|
||||
}
|
||||
Geq => {
|
||||
lhs.lt(rhs)?;
|
||||
let _ = lhs.not()?;
|
||||
let _ = lhs.not();
|
||||
}
|
||||
And => lhs.and(rhs)?,
|
||||
Or => lhs.or(rhs)?,
|
||||
Impl => {
|
||||
let _ = lhs.not();
|
||||
lhs.not()?;
|
||||
lhs.or(rhs)?;
|
||||
}
|
||||
Con => lhs.concat(rhs)?,
|
||||
@@ -226,12 +224,11 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Select {
|
||||
use ir::Attr::*;
|
||||
let mut val = self.expr.eval(ctx)?;
|
||||
for attr in self.attrpath.iter() {
|
||||
let name_val;
|
||||
let name = match attr {
|
||||
Str(name) => name,
|
||||
&Str(name) => name,
|
||||
Dynamic(expr) => {
|
||||
name_val = expr.eval(ctx)?;
|
||||
&*name_val.force_string_no_ctx()?
|
||||
let sym = expr.eval(ctx)?.force_string_no_ctx()?;
|
||||
ctx.new_sym(sym)
|
||||
}
|
||||
};
|
||||
if let Some(default) = self.default {
|
||||
@@ -338,11 +335,9 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Var {
|
||||
/// Evaluates a `Var` by looking it up in the `with` scope chain.
|
||||
/// This is for variables that could not be resolved statically.
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||
ctx.lookup_with(&self.sym)
|
||||
.ok_or_else(|| {
|
||||
Error::eval_error(format!("undefined variable '{}'", format_symbol(&self.sym)))
|
||||
})
|
||||
.map(|val| val.clone())
|
||||
ctx.lookup_with(self.sym).cloned().ok_or_else(|| {
|
||||
Error::eval_error(format!("undefined variable '{}'", format_symbol(ctx.get_sym(self.sym))))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use hashbrown::hash_map::Entry;
|
||||
use itertools::Itertools;
|
||||
|
||||
use nixjit_error::{Error, Result};
|
||||
use nixjit_ir::ExprId;
|
||||
use nixjit_ir::{ExprId, SymId};
|
||||
use nixjit_value::{self as p, format_symbol};
|
||||
|
||||
use crate::EvalContext;
|
||||
@@ -24,7 +24,7 @@ use super::Value;
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Constructor)]
|
||||
pub struct AttrSet {
|
||||
data: HashMap<String, Value>,
|
||||
data: HashMap<SymId, Value>,
|
||||
}
|
||||
|
||||
impl Debug for AttrSet {
|
||||
@@ -33,23 +33,23 @@ impl Debug for AttrSet {
|
||||
write!(f, "{{ ")?;
|
||||
for (k, v) in self.data.iter() {
|
||||
match v {
|
||||
List(_) => write!(f, "{} = [ ... ]; ", format_symbol(k))?,
|
||||
AttrSet(_) => write!(f, "{} = {{ ... }}; ", format_symbol(k))?,
|
||||
v => write!(f, "{} = {v:?}; ", format_symbol(k))?,
|
||||
List(_) => write!(f, "{:?} = [ ... ]; ", k)?,
|
||||
AttrSet(_) => write!(f, "{:?} = {{ ... }}; ", k)?,
|
||||
v => write!(f, "{:?} = {v:?}; ", k)?,
|
||||
}
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HashMap<String, Value>> for AttrSet {
|
||||
fn from(data: HashMap<String, Value>) -> Self {
|
||||
impl From<HashMap<SymId, Value>> for AttrSet {
|
||||
fn from(data: HashMap<SymId, Value>) -> Self {
|
||||
Self { data }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for AttrSet {
|
||||
type Target = HashMap<String, Value>;
|
||||
type Target = HashMap<SymId, Value>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
@@ -64,16 +64,16 @@ impl AttrSet {
|
||||
}
|
||||
|
||||
/// Inserts an attribute, overwriting any existing attribute with the same name.
|
||||
pub fn push_attr_force(&mut self, sym: String, val: Value) {
|
||||
pub fn push_attr_force(&mut self, sym: SymId, val: Value) {
|
||||
self.data.insert(sym, val);
|
||||
}
|
||||
|
||||
/// Inserts an attribute, returns an error if the attribute is already defined.
|
||||
pub fn push_attr(&mut self, sym: String, val: Value) -> Result<()> {
|
||||
pub fn push_attr(&mut self, sym: SymId, val: Value, ctx: &mut impl EvalContext) -> Result<()> {
|
||||
match self.data.entry(sym) {
|
||||
Entry::Occupied(occupied) => Err(Error::eval_error(format!(
|
||||
"attribute '{}' already defined",
|
||||
format_symbol(occupied.key())
|
||||
format_symbol(ctx.get_sym(*occupied.key()))
|
||||
))),
|
||||
Entry::Vacant(vacant) => {
|
||||
vacant.insert(val);
|
||||
@@ -82,29 +82,29 @@ impl AttrSet {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select(&self, name: &str, ctx: &mut impl EvalContext) -> Result<Value> {
|
||||
pub fn select(&self, name: SymId, ctx: &mut impl EvalContext) -> Result<Value> {
|
||||
self.data
|
||||
.get(name)
|
||||
.get(&name)
|
||||
.cloned()
|
||||
.map(|attr| match attr {
|
||||
Value::Thunk(id) => ctx.eval(id),
|
||||
Value::Thunk(id) => ctx.force(id),
|
||||
val => Ok(val),
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
Error::eval_error(format!("attribute '{}' not found", format_symbol(name)))
|
||||
Error::eval_error(format!("attribute '{}' not found", format_symbol(ctx.get_sym(name))))
|
||||
})?
|
||||
}
|
||||
|
||||
pub fn select_or(
|
||||
&self,
|
||||
name: &str,
|
||||
name: SymId,
|
||||
default: ExprId,
|
||||
ctx: &mut impl EvalContext,
|
||||
) -> Result<Value> {
|
||||
self.data
|
||||
.get(name)
|
||||
.get(&name)
|
||||
.map(|attr| match attr {
|
||||
&Value::Thunk(id) => ctx.eval(id),
|
||||
&Value::Thunk(id) => ctx.force(id),
|
||||
val => Ok(val.clone()),
|
||||
})
|
||||
.unwrap_or_else(|| ctx.eval(default))
|
||||
@@ -113,19 +113,19 @@ impl AttrSet {
|
||||
/// Checks if an attribute path exists within the set.
|
||||
pub fn has_attr(
|
||||
&self,
|
||||
mut path: impl DoubleEndedIterator<Item = Result<Value>>,
|
||||
mut path: impl DoubleEndedIterator<Item = Result<SymId>>,
|
||||
) -> Result<Value> {
|
||||
let mut data = &self.data;
|
||||
let last = path.nth_back(0).unwrap();
|
||||
for item in path {
|
||||
let Some(Value::AttrSet(attrs)) = data.get(&item.unwrap().force_string_no_ctx()?)
|
||||
let Some(Value::AttrSet(attrs)) = data.get(&item?)
|
||||
else {
|
||||
return Ok(Value::Bool(false));
|
||||
};
|
||||
data = attrs.as_inner();
|
||||
}
|
||||
Ok(Value::Bool(
|
||||
data.get(&last.unwrap().force_string_no_ctx()?).is_some(),
|
||||
data.get(&last?).is_some(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -138,24 +138,18 @@ impl AttrSet {
|
||||
}
|
||||
|
||||
/// Returns a reference to the inner `HashMap`.
|
||||
pub fn as_inner(&self) -> &HashMap<String, Value> {
|
||||
pub fn as_inner(&self) -> &HashMap<SymId, Value> {
|
||||
&self.data
|
||||
}
|
||||
|
||||
/// Converts an `Rc<AttrSet>` to an `Rc<HashMap<String, Value>>` without allocation.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This is safe because `AttrSet` is `#[repr(transparent)]`.
|
||||
pub fn into_inner(self: Rc<Self>) -> Rc<HashMap<String, Value>> {
|
||||
// SAFETY: This is safe because `AttrSet` is `#[repr(transparent)]` over
|
||||
// `HashMap<String, Value>`, so `Rc<Self>` has the same layout as
|
||||
// `Rc<HashMap<String, Value>>`.
|
||||
unsafe { core::mem::transmute(self) }
|
||||
}
|
||||
|
||||
/// Creates an `AttrSet` from a `HashMap`.
|
||||
pub fn from_inner(data: HashMap<String, Value>) -> Self {
|
||||
Self { data }
|
||||
}
|
||||
|
||||
/// Performs a deep equality comparison between two `AttrSet`s.
|
||||
///
|
||||
/// It recursively compares the contents of both sets, ensuring that both keys
|
||||
@@ -171,11 +165,11 @@ impl AttrSet {
|
||||
}
|
||||
|
||||
/// Converts the `AttrSet` to its public-facing representation.
|
||||
pub fn to_public(self) -> p::Value {
|
||||
pub fn to_public(self, ctx: &mut impl EvalContext) -> p::Value {
|
||||
p::Value::AttrSet(p::AttrSet::new(
|
||||
self.data
|
||||
.into_iter()
|
||||
.map(|(sym, value)| (sym.into(), value.to_public()))
|
||||
.map(|(sym, value)| (ctx.get_sym(sym).into(), value.to_public(ctx)))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
//! Defines the runtime representation of a partially applied function.
|
||||
use std::rc::Rc;
|
||||
|
||||
use derive_more::Constructor;
|
||||
|
||||
use nixjit_error::Result;
|
||||
use nixjit_ir::ExprId;
|
||||
|
||||
use super::Value;
|
||||
use crate::EvalContext;
|
||||
|
||||
pub type StackFrame = smallvec::SmallVec<[Value; 5]>;
|
||||
|
||||
#[derive(Debug, Clone, Constructor)]
|
||||
pub struct Closure {
|
||||
pub body: ExprId,
|
||||
pub frame: StackFrame,
|
||||
}
|
||||
|
||||
impl Closure {
|
||||
pub fn call<Ctx: EvalContext>(
|
||||
self: Rc<Self>,
|
||||
arg: Option<Value>,
|
||||
ctx: &mut Ctx,
|
||||
) -> Result<Value> {
|
||||
let Self { body: func, frame } = Rc::unwrap_or_clone(self);
|
||||
ctx.call(func, arg, frame)
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,7 @@ impl List {
|
||||
self.data
|
||||
.get(idx)
|
||||
.map(|elem| match elem {
|
||||
&Value::Thunk(id) => ctx.eval(id),
|
||||
&Value::Thunk(id) => ctx.force(id),
|
||||
val => Ok(val.clone()),
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
@@ -93,11 +93,11 @@ impl List {
|
||||
}
|
||||
|
||||
/// Converts the `List` to its public-facing representation.
|
||||
pub fn to_public(&self) -> PubValue {
|
||||
pub fn to_public(&self, ctx: &mut impl EvalContext) -> PubValue {
|
||||
PubValue::List(PubList::new(
|
||||
self.data
|
||||
.iter()
|
||||
.map(|value| value.clone().to_public())
|
||||
.map(|value| value.clone().to_public(ctx))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use nixjit_ir::ExprId;
|
||||
use nixjit_ir::PrimOpId;
|
||||
|
||||
use nixjit_error::{Error, Result};
|
||||
use nixjit_ir::SymId;
|
||||
use nixjit_value::Const;
|
||||
use nixjit_value::Value as PubValue;
|
||||
use replace_with::replace_with_and_return;
|
||||
@@ -21,13 +22,11 @@ use smallvec::smallvec;
|
||||
use crate::EvalContext;
|
||||
|
||||
mod attrset;
|
||||
mod closure;
|
||||
mod list;
|
||||
mod primop;
|
||||
mod string;
|
||||
|
||||
pub use attrset::AttrSet;
|
||||
pub use closure::*;
|
||||
pub use list::List;
|
||||
pub use primop::*;
|
||||
|
||||
@@ -45,14 +44,12 @@ pub enum Value {
|
||||
Bool(bool) = Self::BOOL,
|
||||
String(String) = Self::STRING,
|
||||
Null = Self::NULL,
|
||||
Thunk(ExprId) = Self::THUNK,
|
||||
ClosureThunk(Rc<Closure>) = Self::CLOSURE_THUNK,
|
||||
Thunk(ValueId) = Self::THUNK,
|
||||
AttrSet(Rc<AttrSet>) = Self::ATTRSET,
|
||||
List(Rc<List>) = Self::LIST,
|
||||
PrimOp(PrimOpId) = Self::PRIMOP,
|
||||
PrimOpApp(Rc<PrimOpApp>) = Self::PRIMOP_APP,
|
||||
Closure(Rc<Closure>) = Self::CLOSURE,
|
||||
Blackhole,
|
||||
Closure(ValueId) = Self::CLOSURE,
|
||||
}
|
||||
|
||||
impl Debug for Value {
|
||||
@@ -67,11 +64,9 @@ impl Debug for Value {
|
||||
AttrSet(x) => write!(f, "{x:?}"),
|
||||
List(x) => write!(f, "{x:?}"),
|
||||
Thunk(thunk) => write!(f, "<THUNK {thunk:?}>"),
|
||||
ClosureThunk(_) => write!(f, "<THUNK>"),
|
||||
Closure(func) => write!(f, "<LAMBDA-APP {:?}>", func.body),
|
||||
Closure(func) => write!(f, "<LAMBDA-APP {:?}>", func),
|
||||
PrimOp(_) => write!(f, "<PRIMOP>"),
|
||||
PrimOpApp(_) => write!(f, "<PRIMOP-APP>"),
|
||||
Blackhole => write!(f, "<BLACKHOLE>"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,13 +124,11 @@ impl Value {
|
||||
String(_) => "string",
|
||||
Null => "null",
|
||||
Thunk(_) => "thunk",
|
||||
ClosureThunk(_) => "thunk",
|
||||
AttrSet(_) => "set",
|
||||
List(_) => "list",
|
||||
PrimOp(_) => "lambda",
|
||||
PrimOpApp(_) => "lambda",
|
||||
Closure(..) => "lambda",
|
||||
Blackhole => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,8 +141,7 @@ impl Value {
|
||||
self,
|
||||
|| Value::Null,
|
||||
|val| match val {
|
||||
Value::Thunk(id) => map(ctx.eval(id)),
|
||||
Value::ClosureThunk(thunk) => map(thunk.call(None, ctx)),
|
||||
Value::Thunk(id) => map(ctx.force(id)),
|
||||
val => (Ok(()), val),
|
||||
},
|
||||
)
|
||||
@@ -170,23 +162,9 @@ impl Value {
|
||||
self,
|
||||
|| Null,
|
||||
|func| match func {
|
||||
PrimOp(id) => {
|
||||
let arity = ctx.get_primop_arity(id);
|
||||
if arity == 1 {
|
||||
map(ctx.call_primop(id, smallvec![arg]))
|
||||
} else {
|
||||
(
|
||||
Ok(()),
|
||||
Value::PrimOpApp(Rc::new(self::PrimOpApp::new(
|
||||
arity - 1,
|
||||
id,
|
||||
smallvec![arg],
|
||||
))),
|
||||
)
|
||||
}
|
||||
}
|
||||
PrimOpApp(func) => map(func.call(arg, ctx)),
|
||||
Closure(func) => map(func.call(Some(arg), ctx)),
|
||||
PrimOp(id) => map(ctx.call_primop(id, smallvec![arg])),
|
||||
PrimOpApp(primop) => map(primop.call(arg, ctx)),
|
||||
Closure(func) => map(ctx.call(func, arg)),
|
||||
_ => (
|
||||
Err(Error::eval_error(
|
||||
"attempt to call something which is not a function but ...".to_string(),
|
||||
@@ -240,10 +218,7 @@ impl Value {
|
||||
}
|
||||
|
||||
pub fn eq(&mut self, other: Self) {
|
||||
use Value::Bool;
|
||||
*self = match (&*self, other) {
|
||||
(s, other) => Bool(s.eq_impl(&other)),
|
||||
};
|
||||
*self = Value::Bool(self.eq_impl(&other));
|
||||
}
|
||||
|
||||
pub fn lt(&mut self, other: Self) -> Result<()> {
|
||||
@@ -378,7 +353,7 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select(&mut self, name: &str, ctx: &mut impl EvalContext) -> Result<()> {
|
||||
pub fn select(&mut self, name: SymId, ctx: &mut impl EvalContext) -> Result<()> {
|
||||
use Value::*;
|
||||
let val = match self {
|
||||
AttrSet(attrs) => attrs.select(name, ctx),
|
||||
@@ -393,7 +368,7 @@ impl Value {
|
||||
|
||||
pub fn select_or<Ctx: EvalContext>(
|
||||
&mut self,
|
||||
name: &str,
|
||||
name: SymId,
|
||||
default: ExprId,
|
||||
ctx: &mut Ctx,
|
||||
) -> Result<()> {
|
||||
@@ -411,7 +386,7 @@ impl Value {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn has_attr(&mut self, path: impl DoubleEndedIterator<Item = Result<Value>>) -> Result<()> {
|
||||
pub fn has_attr(&mut self, path: impl DoubleEndedIterator<Item = Result<SymId>>) -> Result<()> {
|
||||
use Value::*;
|
||||
if let AttrSet(attrs) = self {
|
||||
let val = attrs.has_attr(path)?;
|
||||
@@ -436,22 +411,46 @@ impl Value {
|
||||
|
||||
/// Converts the internal `Value` to its public-facing, serializable
|
||||
/// representation from the `nixjit_value` crate.
|
||||
pub fn to_public(self) -> PubValue {
|
||||
pub fn to_public(self, ctx: &mut impl EvalContext) -> PubValue {
|
||||
use Value::*;
|
||||
match self {
|
||||
AttrSet(attrs) => Rc::unwrap_or_clone(attrs).to_public(),
|
||||
List(list) => Rc::unwrap_or_clone(list.clone()).to_public(),
|
||||
AttrSet(attrs) => Rc::unwrap_or_clone(attrs).to_public(ctx),
|
||||
List(list) => Rc::unwrap_or_clone(list.clone()).to_public(ctx),
|
||||
Int(x) => PubValue::Const(Const::Int(x)),
|
||||
Float(x) => PubValue::Const(Const::Float(x)),
|
||||
Bool(x) => PubValue::Const(Const::Bool(x)),
|
||||
String(x) => PubValue::String(x),
|
||||
Null => PubValue::Const(Const::Null),
|
||||
Thunk(_) => PubValue::Thunk,
|
||||
ClosureThunk(_) => PubValue::Thunk,
|
||||
PrimOp(_) => PubValue::PrimOp,
|
||||
PrimOpApp(_) => PubValue::PrimOpApp,
|
||||
Closure(..) => PubValue::Func,
|
||||
Blackhole => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
|
||||
pub struct ValueId(usize);
|
||||
|
||||
impl ValueId {
|
||||
/// Returns the raw `usize` index.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller is responsible for using this index correctly and not causing out-of-bounds access.
|
||||
#[inline(always)]
|
||||
pub unsafe fn raw(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Creates an `ExprId` from a raw `usize` index.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that the provided index is valid for the expression table.
|
||||
#[inline(always)]
|
||||
pub unsafe fn from_raw(id: usize) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@ pub type Args = smallvec::SmallVec<[Value; 2]>;
|
||||
/// all, of its required arguments.
|
||||
#[derive(Debug, Clone, Constructor)]
|
||||
pub struct PrimOpApp {
|
||||
/// The number of remaining arguments the primop expects.
|
||||
arity: usize,
|
||||
/// The unique ID of the primop.
|
||||
id: PrimOpId,
|
||||
/// The arguments that have already been applied.
|
||||
@@ -27,20 +25,9 @@ pub struct PrimOpApp {
|
||||
}
|
||||
|
||||
impl PrimOpApp {
|
||||
/// Applies more arguments to a partially applied primop.
|
||||
///
|
||||
/// If enough arguments are provided to satisfy the primop's arity, it is
|
||||
/// executed. Otherwise, it returns a new `PrimOpApp` with the combined
|
||||
/// arguments.
|
||||
pub fn call(self: Rc<Self>, arg: Value, ctx: &mut impl EvalContext) -> Result<Value> {
|
||||
let mut primop = Rc::unwrap_or_clone(self);
|
||||
if primop.arity == 1 {
|
||||
primop.args.push(arg);
|
||||
ctx.call_primop(primop.id, primop.args)
|
||||
} else {
|
||||
primop.args.push(arg);
|
||||
primop.arity -= 1;
|
||||
Ok(Value::PrimOpApp(primop.into()))
|
||||
}
|
||||
let PrimOpApp { id, mut args } = Rc::unwrap_or_clone(self);
|
||||
args.push(arg);
|
||||
ctx.call_primop(id, args)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user