refactor: reduce coupling

This commit is contained in:
2025-07-28 21:37:27 +08:00
parent 78e3c5a26e
commit 7afb2a7b1c
53 changed files with 2964 additions and 3444 deletions

View File

@@ -0,0 +1,15 @@
[package]
name = "nixjit_eval"
version = "0.1.0"
edition = "2024"
[dependencies]
derive_more = { version = "2.0", features = ["full"] }
hashbrown = "0.15"
itertools = "0.14"
replace_with = "0.1"
nixjit_error = { path = "../nixjit_error" }
nixjit_ir = { path = "../nixjit_ir" }
nixjit_lir = { path = "../nixjit_lir" }
nixjit_value = { path = "../nixjit_value" }

View File

@@ -0,0 +1,308 @@
use std::rc::Rc;
use hashbrown::HashMap;
use nixjit_error::{Error, Result};
use nixjit_ir::{self as ir, ExprId};
use nixjit_lir as lir;
use nixjit_value::{Const, Symbol};
pub use crate::value::*;
mod value;
pub trait EvalContext: Sized {
fn eval(&mut self, expr: ExprId) -> Result<Value<Self>>;
fn with_with_env<T>(
&mut self,
namespace: Rc<HashMap<String, Value<Self>>>,
f: impl FnOnce(&mut Self) -> T,
) -> T;
fn with_args_env<T>(
&mut self,
args: Vec<Value<Self>>,
f: impl FnOnce(&mut Self) -> T,
) -> (Vec<Value<Self>>, T);
fn lookup_with<'a>(&'a self, ident: &str) -> Option<&'a Value<Self>>;
fn pop_frame(&mut self) -> Vec<Value<Self>>;
}
pub trait Evaluate<Ctx: EvalContext> {
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>>;
}
impl<Ctx: EvalContext> Evaluate<Ctx> for ExprId {
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
ctx.eval(*self)
}
}
impl<Ctx: EvalContext> Evaluate<Ctx> for lir::Lir {
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
todo!()
}
}
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::AttrSet {
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
let mut attrs = AttrSet::new(
self.stcs
.iter()
.map(|(k, v)| {
let eval_result = v.eval(ctx);
Ok((k.clone(), eval_result?))
})
.collect::<Result<_>>()?,
);
for (k, v) in self.dyns.iter() {
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);
}
let result = Value::AttrSet(attrs.into()).ok();
Ok(result.unwrap())
}
}
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::List {
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
let items = self
.items
.iter()
.map(|val| val.eval(ctx))
.collect::<Result<Vec<_>>>()?;
let result = Value::List(List::from(items).into()).ok();
Ok(result.unwrap())
}
}
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::HasAttr {
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
use ir::Attr::*;
let mut val = self.lhs.eval(ctx)?;
val.has_attr(self.rhs.iter().map(|attr| {
Ok(match attr {
Str(ident) => ident.clone(),
Dynamic(expr) => {
let mut val = expr.eval(ctx)?;
val.coerce_to_string();
val.unwrap_string()
}
})
}))?;
let result = val.ok();
Ok(result.unwrap())
}
}
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::BinOp {
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
use ir::BinOpKind::*;
let mut lhs = self.lhs.eval(ctx)?;
let mut rhs = self.rhs.eval(ctx)?;
match self.kind {
Add => lhs.add(rhs),
Sub => {
rhs.neg();
lhs.add(rhs);
}
Mul => lhs.mul(rhs),
Div => lhs.div(rhs)?,
Eq => Value::eq(&mut lhs, &rhs),
Neq => {
Value::eq(&mut lhs, &rhs);
lhs.not();
}
Lt => lhs.lt(rhs),
Gt => {
rhs.lt(lhs);
lhs = rhs;
}
Leq => {
rhs.lt(lhs);
rhs.not();
lhs = rhs;
}
Geq => {
lhs.lt(rhs);
lhs.not();
}
And => lhs.and(rhs),
Or => lhs.or(rhs),
Impl => {
lhs.not();
lhs.or(rhs);
}
Con => lhs.concat(rhs),
Upd => lhs.update(rhs),
PipeL => lhs.call(vec![rhs], ctx)?,
PipeR => {
rhs.call(vec![lhs], ctx)?;
lhs = rhs;
}
}
Ok(lhs)
}
}
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::UnOp {
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
use ir::UnOpKind::*;
let mut rhs = self.rhs.eval(ctx)?;
match self.kind {
Neg => {
rhs.neg();
}
Not => {
rhs.not();
}
};
Ok(rhs)
}
}
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Select {
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
use ir::Attr::*;
let mut val = self.expr.eval(ctx)?;
if let Some(default) = &self.default {
let default = default.eval(ctx)?;
val.select_with_default(
self.attrpath.iter().map(|attr| {
Ok(match attr {
Str(ident) => ident.clone(),
Dynamic(expr) => {
let mut val = expr.eval(ctx)?;
val.coerce_to_string();
val.unwrap_string()
}
})
}),
default,
)?;
} else {
val.select(self.attrpath.iter().map(|attr| {
Ok(match attr {
Str(ident) => ident.clone(),
Dynamic(expr) => {
let mut val = expr.eval(ctx)?;
val.coerce_to_string();
val.unwrap_string()
}
})
}))?;
}
let result = val.ok();
Ok(result.unwrap())
}
}
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::If {
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
// TODO: Error Handling
let cond = self.cond.eval(ctx)?;
let cond = cond
.try_unwrap_bool()
.map_err(|_| Error::EvalError(format!("expected a boolean but found ...")))?;
if cond {
self.consq.eval(ctx)
} else {
self.alter.eval(ctx)
}
}
}
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Call {
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
let mut func = self.func.eval(ctx)?;
func.call(
self.args
.iter()
.map(|arg| arg.eval(ctx))
.collect::<Result<_>>()?,
ctx,
)?;
Ok(func.ok().unwrap())
}
}
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::With {
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
let namespace = self.namespace.eval(ctx)?;
ctx.with_with_env(
namespace
.try_unwrap_attr_set()
.map_err(|_| Error::EvalError(format!("expected a set but found ...")))?
.into_inner(),
|ctx| self.expr.eval(ctx),
)
}
}
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Assert {
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
todo!()
}
}
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::ConcatStrings {
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
let mut parts = self
.parts
.iter()
.map(|part| {
let mut part = part.eval(ctx)?;
part.coerce_to_string();
part.ok()
})
.collect::<Result<Vec<_>>>()?
.into_iter();
let init = parts.next().unwrap();
let result = parts.fold(init, |mut a, b| {
a.concat_string(b);
a
});
Ok(result.ok().unwrap())
}
}
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Str {
fn eval(&self, _: &mut Ctx) -> Result<Value<Ctx>> {
let result = Value::String(self.val.clone()).ok();
Ok(result.unwrap())
}
}
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Const {
fn eval(&self, _: &mut Ctx) -> Result<Value<Ctx>> {
let result = match self.val {
Const::Null => Value::Null,
Const::Int(x) => Value::Int(x),
Const::Float(x) => Value::Float(x),
Const::Bool(x) => Value::Bool(x),
}
.ok();
Ok(result.unwrap())
}
}
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Var {
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
ctx.lookup_with(&self.sym)
.ok_or_else(|| {
Error::EvalError(format!(
"variable {} not found",
Symbol::from(self.sym.clone())
))
})
.map(|val| val.clone())
}
}
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Path {
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
todo!()
}
}

View File

@@ -0,0 +1,148 @@
use core::ops::Deref;
use std::rc::Rc;
use std::fmt::Debug;
use derive_more::Constructor;
use hashbrown::{HashMap, HashSet};
use itertools::Itertools;
use nixjit_error::{Error, Result};
use nixjit_value as p;
use nixjit_value::Symbol;
use super::Value;
use crate::EvalContext;
#[repr(transparent)]
#[derive(Constructor, PartialEq)]
pub struct AttrSet<Ctx: EvalContext> {
data: HashMap<String, Value<Ctx>>,
}
impl<Ctx: EvalContext> Debug for AttrSet<Ctx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Value::*;
write!(f, "{{ ")?;
for (k, v) in self.data.iter() {
match v {
List(_) => write!(f, "{k:?} = [ ... ]; ")?,
AttrSet(_) => write!(f, "{k:?} = {{ ... }}; ")?,
v => write!(f, "{k:?} = {v:?}; ")?,
}
}
write!(f, "}}")
}
}
impl<Ctx: EvalContext> Clone for AttrSet<Ctx> {
fn clone(&self) -> Self {
AttrSet {
data: self.data.clone(),
}
}
}
impl<Ctx: EvalContext> From<HashMap<String, Value<Ctx>>> for AttrSet<Ctx> {
fn from(data: HashMap<String, Value<Ctx>>) -> Self {
Self { data }
}
}
impl<Ctx: EvalContext> Deref for AttrSet<Ctx> {
type Target = HashMap<String, Value<Ctx>>;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<Ctx: EvalContext> AttrSet<Ctx> {
pub fn with_capacity(cap: usize) -> Self {
AttrSet {
data: HashMap::with_capacity(cap),
}
}
pub fn push_attr_force(&mut self, sym: String, val: Value<Ctx>) {
self.data.insert(sym, val);
}
pub fn push_attr(&mut self, sym: String, val: Value<Ctx>) {
if self.data.get(&sym).is_some() {
todo!()
}
self.data.insert(sym, val);
}
pub fn select(
&self,
mut path: impl DoubleEndedIterator<Item = Result<String>>,
) -> Result<Value<Ctx>> {
let mut data = &self.data;
let last = path.nth_back(0).unwrap();
for item in path {
let item = item?;
let Some(Value::AttrSet(attrs)) = data.get(&item) else {
return Err(Error::EvalError(format!(
"attribute '{}' not found",
Symbol::from(item)
)));
};
data = attrs.as_inner();
}
let last = last?;
data.get(&last).cloned().ok_or_else(|| {
Error::EvalError(format!("attribute '{}' not found", Symbol::from(last)))
})
}
pub fn has_attr(
&self,
mut path: impl DoubleEndedIterator<Item = Result<String>>,
) -> Result<bool> {
let mut data = &self.data;
let last = path.nth_back(0).unwrap();
for item in path {
let Some(Value::AttrSet(attrs)) = data.get(&item?) else {
return Ok(false);
};
data = attrs.as_inner();
}
Ok(data.get(&last?).is_some())
}
pub fn update(&mut self, other: &Self) {
for (k, v) in other.data.iter() {
self.push_attr_force(k.clone(), v.clone())
}
}
pub fn as_inner(&self) -> &HashMap<String, Value<Ctx>> {
&self.data
}
pub fn into_inner(self: Rc<Self>) -> Rc<HashMap<String, Value<Ctx>>> {
unsafe { core::mem::transmute(self) }
}
pub fn from_inner(data: HashMap<String, Value<Ctx>>) -> Self {
Self { data }
}
pub fn eq_impl(&self, other: &Self) -> bool {
self.data.iter().len() == other.data.iter().len()
&& std::iter::zip(
self.data.iter().sorted_by(|(a, _), (b, _)| a.cmp(b)),
other.data.iter().sorted_by(|(a, _), (b, _)| a.cmp(b)),
)
.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 {
p::Value::AttrSet(p::AttrSet::new(
self.data
.iter()
.map(|(sym, value)| (sym.as_str().into(), value.to_public(ctx, seen)))
.collect(),
))
}
}

View File

@@ -0,0 +1,47 @@
use std::rc::Rc;
use derive_more::Constructor;
use nixjit_error::Result;
use nixjit_ir::ExprId;
use super::Value;
use crate::EvalContext;
#[derive(Debug, Constructor)]
pub struct FuncApp<Ctx: EvalContext> {
pub body: ExprId,
pub args: Vec<Value<Ctx>>,
pub frame: Vec<Value<Ctx>>,
}
impl<Ctx: EvalContext> Clone for FuncApp<Ctx> {
fn clone(&self) -> Self {
Self {
body: self.body,
args: self.args.clone(),
frame: self.frame.clone(),
}
}
}
impl<Ctx: EvalContext> FuncApp<Ctx> {
pub fn call(
self: &mut Rc<Self>,
new_args: Vec<Value<Ctx>>,
ctx: &mut Ctx,
) -> Result<Value<Ctx>> {
let FuncApp { body: expr, args, frame } = Rc::make_mut(self);
args.extend(new_args);
let (args, ret) = ctx.with_args_env(core::mem::take(args), |ctx| ctx.eval(*expr));
let mut ret = ret?;
if let Value::Func(expr) = ret {
let frame = ctx.pop_frame();
ret = Value::FuncApp(FuncApp::new(expr, args, frame).into());
} else if let Value::FuncApp(func) = &mut ret {
todo!();
let func = Rc::make_mut(func);
}
ret.ok()
}
}

View File

@@ -0,0 +1,86 @@
use std::ops::Deref;
use std::fmt::Debug;
use hashbrown::HashSet;
use nixjit_value::List as PubList;
use nixjit_value::Value as PubValue;
use super::Value;
use crate::EvalContext;
#[derive(Default)]
pub struct List<Ctx: EvalContext> {
data: Vec<Value<Ctx>>,
}
impl<Ctx: EvalContext> Debug for List<Ctx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[ ")?;
for v in self.data.iter() {
write!(f, "{v:?} ")?;
}
write!(f, "]")
}
}
impl<Ctx: EvalContext> Clone for List<Ctx> {
fn clone(&self) -> Self {
Self {
data: self.data.clone(),
}
}
}
impl<Ctx: EvalContext, T: Into<Vec<Value<Ctx>>>> From<T> for List<Ctx> {
fn from(value: T) -> Self {
Self { data: value.into() }
}
}
impl<Ctx: EvalContext> Deref for List<Ctx> {
type Target = [Value<Ctx>];
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<Ctx: EvalContext> List<Ctx> {
pub fn new() -> Self {
List { data: Vec::new() }
}
pub fn with_capacity(cap: usize) -> Self {
List {
data: Vec::with_capacity(cap),
}
}
pub fn push(&mut self, elem: Value<Ctx>) {
self.data.push(elem);
}
pub fn concat(&mut self, other: &Self) {
for elem in other.data.iter() {
self.data.push(elem.clone());
}
}
pub fn into_inner(self) -> Vec<Value<Ctx>> {
self.data
}
pub fn eq_impl(&self, other: &Self) -> bool {
self.len() == other.len()
&& 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 {
PubValue::List(PubList::new(
self.data
.iter()
.map(|value| value.clone().to_public(engine, seen))
.collect(),
))
}
}

View File

@@ -0,0 +1,516 @@
use std::hash::Hash;
use std::rc::Rc;
use std::fmt::{write, Debug};
use derive_more::TryUnwrap;
use derive_more::{IsVariant, Unwrap};
use func::FuncApp;
use hashbrown::HashSet;
use nixjit_ir::ExprId;
use replace_with::replace_with_or_abort;
use nixjit_error::{Error, Result};
use nixjit_value::Const;
use nixjit_value::Value as PubValue;
use crate::EvalContext;
mod attrset;
mod func;
mod list;
mod primop;
mod string;
pub use attrset::*;
pub use list::List;
pub use primop::*;
#[repr(C, u64)]
#[derive(IsVariant, TryUnwrap, Unwrap)]
pub enum Value<Ctx: EvalContext> {
Int(i64),
Float(f64),
Bool(bool),
String(String),
Null,
Thunk(usize),
AttrSet(Rc<AttrSet<Ctx>>),
List(Rc<List<Ctx>>),
Catchable(String),
PrimOp(Rc<PrimOp<Ctx>>),
PrimOpApp(Rc<PrimOpApp<Ctx>>),
Func(ExprId),
FuncApp(Rc<FuncApp<Ctx>>),
}
impl<Ctx: EvalContext> Debug for Value<Ctx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Value::*;
match self {
Int(x) => write!(f, "{x}"),
Float(x) => write!(f, "{x}"),
Bool(x) => write!(f, "{x}"),
Null => write!(f, "null"),
String(x) => write!(f, "{x}"),
AttrSet(x) => write!(f, "{x:?}"),
List(x) => write!(f, "{x:?}"),
Catchable(x) => write!(f, "{x}"),
Thunk(thunk) => write!(f, "<THUNK {thunk:?}>"),
Func(func) => write!(f, "<LAMBDA {func:?}>"),
FuncApp(func) => write!(f, "<LAMBDA-APP {:?}>", func.body),
PrimOp(primop) => write!(f, "<PRIMOP {}>", primop.name),
PrimOpApp(primop) => write!(f, "<PRIMOP-APP {}>", primop.name),
}
}
}
impl<Ctx: EvalContext> Clone for Value<Ctx> {
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.clone()),
PrimOpApp(primop) => PrimOpApp(primop.clone()),
Func(expr) => Func(*expr),
FuncApp(func) => FuncApp(func.clone()),
}
}
}
impl<Ctx: EvalContext> Hash for Value<Ctx> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
use Value::*;
std::mem::discriminant(self).hash(state);
match self {
AttrSet(x) => Rc::as_ptr(x).hash(state),
List(x) => x.as_ptr().hash(state),
_ => 0.hash(state),
}
}
}
impl<Ctx: EvalContext> Value<Ctx> {
pub const INT: u64 = 0;
pub const FLOAT: u64 = 1;
pub const BOOL: u64 = 2;
pub const STRING: u64 = 3;
pub const NULL: u64 = 4;
pub const THUNK: u64 = 5;
pub const ATTRSET: u64 = 6;
pub const LIST: u64 = 7;
pub const CATCHABLE: u64 = 8;
pub const PRIMOP: u64 = 9;
pub const PARTIAL_PRIMOP: u64 = 10;
pub const FUNC: u64 = 11;
pub const PARTIAL_FUNC: u64 = 12;
fn eq_impl(&self, other: &Self) -> bool {
use Value::*;
match (self, other) {
(Bool(a), Bool(b)) => a == b,
(Int(a), Int(b)) => a == b,
(Float(a), Float(b)) => a == b,
(Int(a), Float(b)) => *a as f64 == *b,
(Float(a), Int(b)) => *b as f64 == *a,
(String(a), String(b)) => a.as_str().eq(b.as_str()),
(Null, Null) => true,
(AttrSet(a), AttrSet(b)) => a.eq_impl(b),
(List(a), List(b)) => a.eq_impl(b),
_ => false,
}
}
}
impl<Ctx: EvalContext> PartialEq for Value<Ctx> {
fn eq(&self, other: &Self) -> bool {
use Value::*;
match (self, other) {
(AttrSet(a), AttrSet(b)) => Rc::as_ptr(a).eq(&Rc::as_ptr(b)),
(List(a), List(b)) => a.as_ptr().eq(&b.as_ptr()),
_ => false,
}
}
}
impl<Ctx: EvalContext> Eq for Value<Ctx> {}
#[derive(IsVariant, Unwrap, Clone)]
pub enum ValueAsRef<'v, Ctx: EvalContext> {
Int(i64),
Float(f64),
Bool(bool),
String(&'v String),
Null,
Thunk(usize),
AttrSet(&'v AttrSet<Ctx>),
List(&'v List<Ctx>),
Catchable(&'v str),
PrimOp(&'v PrimOp<Ctx>),
PartialPrimOp(&'v PrimOpApp<Ctx>),
Func(ExprId),
PartialFunc(&'v FuncApp<Ctx>),
}
impl<Ctx: EvalContext> Value<Ctx> {
pub fn as_ref(&self) -> ValueAsRef<'_, Ctx> {
use Value::*;
use ValueAsRef as R;
match self {
Int(x) => R::Int(*x),
Float(x) => R::Float(*x),
Bool(x) => R::Bool(*x),
String(x) => R::String(x),
Null => R::Null,
Thunk(x) => R::Thunk(*x),
AttrSet(x) => R::AttrSet(x),
List(x) => R::List(x),
Catchable(x) => R::Catchable(x),
PrimOp(x) => R::PrimOp(x),
PrimOpApp(x) => R::PartialPrimOp(x),
Func(x) => R::Func(*x),
FuncApp(x) => R::PartialFunc(x),
}
}
}
impl<Ctx: EvalContext> Value<Ctx> {
pub fn ok(self) -> Result<Self> {
Ok(self)
}
pub fn typename(&self) -> &'static str {
use Value::*;
match self {
Int(_) => "int",
Float(_) => "float",
Bool(_) => "bool",
String(_) => "string",
Null => "null",
Thunk(_) => "thunk",
AttrSet(_) => "set",
List(_) => "list",
Catchable(_) => unreachable!(),
PrimOp(_) => "lambda",
PrimOpApp(_) => "lambda",
Func(_) => "lambda",
FuncApp(..) => "lambda",
}
}
pub fn callable(&self) -> bool {
match self {
Value::PrimOp(_) | Value::PrimOpApp(_) | Value::Func(_) => true,
Value::AttrSet(_) => todo!(),
_ => false,
}
}
pub fn call(&mut self, args: Vec<Self>, ctx: &mut Ctx) -> Result<()> {
use Value::*;
for arg in args.iter() {
if matches!(arg, Value::Catchable(_)) {
*self = arg.clone();
return Ok(());
}
}
*self = match self {
PrimOp(func) => func.call(args, ctx),
PrimOpApp(func) => func.call(args, ctx),
FuncApp(func) => func.call(args, ctx),
&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()
}
Catchable(_) => return Ok(()),
other => todo!("{}", other.typename()),
}?;
Ok(())
}
pub fn not(&mut self) {
use Value::*;
*self = match &*self {
Bool(bool) => Bool(!bool),
Value::Catchable(_) => return,
_ => todo!(),
}
}
pub fn and(&mut self, other: Self) {
use Value::*;
*self = match (&*self, other) {
(Bool(a), Bool(b)) => Bool(*a && b),
(Value::Catchable(_), _) => return,
(_, x @ Value::Catchable(_)) => x,
_ => todo!(),
}
}
pub fn or(&mut self, other: Self) {
use Value::*;
*self = match (&*self, other) {
(Bool(a), Bool(b)) => Bool(*a || b),
(Value::Catchable(_), _) => return,
(_, x @ Value::Catchable(_)) => x,
_ => todo!(),
}
}
pub fn eq(&mut self, other: &Self) {
use Value::Bool;
*self = match (&*self, other) {
(Value::Catchable(_), _) => return,
(_, x @ Value::Catchable(_)) => x.clone(),
(s, other) => Bool(s.eq_impl(other)),
};
}
pub fn lt(&mut self, other: Self) {
use Value::*;
*self = Bool(match (&*self, other) {
(Int(a), Int(b)) => *a < b,
(Int(a), Float(b)) => (*a as f64) < b,
(Float(a), Int(b)) => *a < b as f64,
(Float(a), Float(b)) => *a < b,
(String(a), String(b)) => a.as_str() < b.as_str(),
(Value::Catchable(_), _) => return,
(_, x @ Value::Catchable(_)) => {
*self = x;
return;
}
_ => todo!(),
})
}
pub fn neg(&mut self) {
use Value::*;
*self = match &*self {
Int(int) => Int(-int),
Float(float) => Float(-float),
Value::Catchable(_) => return,
_ => todo!(),
}
}
pub fn add(&mut self, other: Self) {
use Value::*;
replace_with_or_abort(self, |a| match (a, other) {
(Int(a), Int(b)) => Int(a + b),
(Int(a), Float(b)) => Float(a as f64 + b),
(Float(a), Int(b)) => Float(a + b as f64),
(Float(a), Float(b)) => Float(a + b),
(String(mut a), String(b)) => {
a.push_str(&b);
String(a)
}
(a @ Value::Catchable(_), _) => a,
(_, x @ Value::Catchable(_)) => x,
_ => todo!(),
});
}
pub fn mul(&mut self, other: Self) {
use Value::*;
*self = match (&*self, other) {
(Int(a), Int(b)) => Int(a * b),
(Int(a), Float(b)) => Float(*a as f64 * b),
(Float(a), Int(b)) => Float(a * b as f64),
(Float(a), Float(b)) => Float(a * b),
(Value::Catchable(_), _) => return,
(_, x @ Value::Catchable(_)) => x,
_ => todo!(),
}
}
pub fn div(&mut self, other: Self) -> Result<()> {
use Value::*;
*self = match (&*self, other) {
(_, Int(0)) => return Err(Error::EvalError("division by zero".to_string())),
(_, Float(0.)) => {
return Err(Error::EvalError("division by zero".to_string()));
}
(Int(a), Int(b)) => Int(a / b),
(Int(a), Float(b)) => Float(*a as f64 / b),
(Float(a), Int(b)) => Float(a / b as f64),
(Float(a), Float(b)) => Float(a / b),
(Catchable(_), _) => return Ok(()),
(_, x @ Catchable(_)) => x,
_ => todo!(),
};
Ok(())
}
pub fn concat_string(&mut self, mut other: Self) -> &mut Self {
use Value::*;
match (self.coerce_to_string(), other.coerce_to_string()) {
(String(a), String(b)) => {
a.push_str(b.as_str());
}
(_, Catchable(_)) => *self = other,
(Catchable(_), _) => (),
_ => todo!(),
}
self
}
pub fn push(&mut self, elem: Self) -> &mut Self {
use Value::*;
if let List(list) = self {
Rc::make_mut(list).push(elem);
} else if let Catchable(_) = self {
} else if let Catchable(_) = elem {
*self = elem;
} else {
todo!()
}
self
}
pub fn concat(&mut self, other: Self) {
use Value::*;
if let x @ Catchable(_) = other {
*self = x;
return;
}
match (self, other) {
(List(a), List(b)) => {
Rc::make_mut(a).concat(&b);
}
(Catchable(_), _) => (),
_ => todo!(),
}
}
pub fn push_attr(&mut self, sym: String, val: Self) -> &mut Self {
use Value::*;
if let AttrSet(attrs) = self {
Rc::make_mut(attrs).push_attr(sym, val);
} else if let Catchable(_) = self {
} else if let Catchable(_) = val {
*self = val
} else {
todo!()
}
self
}
pub fn update(&mut self, other: Self) {
use Value::*;
if let x @ Catchable(_) = other {
*self = x;
return;
}
match (self, other) {
(AttrSet(a), AttrSet(b)) => {
Rc::make_mut(a).update(&b);
}
(Catchable(_), _) => (),
_ => todo!(),
}
}
pub fn select(
&mut self,
path: impl DoubleEndedIterator<Item = Result<String>>,
) -> Result<&mut Self> {
use Value::*;
let val = match self {
AttrSet(attrs) => attrs.select(path),
Catchable(_) => return Ok(self),
_ => Err(Error::EvalError(format!(
"can not select from {:?}",
self.typename()
))),
}?;
*self = val;
Ok(self)
}
pub fn select_with_default(
&mut self,
path: impl DoubleEndedIterator<Item = Result<String>>,
default: Self,
) -> Result<&mut Self> {
use Value::*;
let val = match self {
AttrSet(attrs) => attrs.select(path).unwrap_or(default),
Catchable(_) => return Ok(self),
_ => {
return Err(Error::EvalError(format!(
"can not select from {:?}",
self.typename()
)));
}
};
*self = val;
Ok(self)
}
pub fn has_attr(
&mut self,
path: impl DoubleEndedIterator<Item = Result<String>>,
) -> Result<()> {
use Value::*;
if let AttrSet(attrs) = self {
let val = Bool(attrs.has_attr(path)?);
*self = val;
} else if let Catchable(_) = self {
} else {
*self = Bool(false);
}
Ok(())
}
pub fn coerce_to_string(&mut self) -> &mut Self {
use Value::*;
if let String(_) = self {
} else if let Catchable(_) = self {
} else {
todo!()
}
self
}
pub fn to_public(&self, ctx: &Ctx, seen: &mut HashSet<Value<Ctx>>) -> PubValue {
use Value::*;
if seen.contains(self) {
return PubValue::Repeated;
}
match self {
AttrSet(attrs) => {
seen.insert(self.clone());
attrs.to_public(ctx, seen)
}
List(list) => {
seen.insert(self.clone());
list.to_public(ctx, seen)
}
Catchable(catchable) => PubValue::Catchable(catchable.clone().into()),
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.clone()),
Null => PubValue::Const(Const::Null),
Thunk(_) => PubValue::Thunk,
PrimOp(primop) => PubValue::PrimOp(primop.name),
PrimOpApp(primop) => PubValue::PrimOpApp(primop.name),
Func(_) => PubValue::Func,
FuncApp(..) => PubValue::Func,
}
}
}

View File

@@ -0,0 +1,75 @@
use std::rc::Rc;
use derive_more::Constructor;
use nixjit_error::Result;
use super::Value;
use crate::EvalContext;
#[derive(Debug, Clone, Constructor)]
pub struct PrimOp<Ctx: EvalContext> {
pub name: &'static str,
arity: usize,
func: fn(Vec<Value<Ctx>>, &Ctx) -> Result<Value<Ctx>>,
}
impl<Ctx: EvalContext> PrimOp<Ctx> {
pub fn call(&self, args: Vec<Value<Ctx>>, ctx: &Ctx) -> Result<Value<Ctx>> {
if args.len() > self.arity {
todo!()
}
if self.arity > args.len() {
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() {
todo!()
}
let func = self.func;
let Some(ret) = ({
let self_mut = Rc::make_mut(self);
self_mut.arity -= args.len();
self_mut.args.extend(args);
if self_mut.arity == 0 {
Some(func(std::mem::take(&mut self_mut.args), ctx))
} else {
None
}
}) else {
return Value::PrimOpApp(self.clone()).ok();
};
ret
}
}

View File

@@ -0,0 +1,27 @@
// TODO: Contextful String
pub struct StringContext {
context: Vec<()>,
}
impl StringContext {
pub fn new() -> StringContext {
StringContext {
context: Vec::new(),
}
}
}
pub struct ContextfulString {
string: String,
context: StringContext,
}
impl ContextfulString {
pub fn new(string: String) -> ContextfulString {
ContextfulString {
string,
context: StringContext::new(),
}
}
}