feat: stack var (WIP)
This commit is contained in:
@@ -12,7 +12,7 @@ use std::rc::Rc;
|
||||
use hashbrown::HashMap;
|
||||
|
||||
use nixjit_error::{Error, Result};
|
||||
use nixjit_ir::{self as ir, ArgIdx, ExprId, PrimOpId};
|
||||
use nixjit_ir::{self as ir, ExprId, PrimOpId, StackIdx};
|
||||
use nixjit_lir as lir;
|
||||
use nixjit_value::{Const, format_symbol};
|
||||
|
||||
@@ -21,10 +21,14 @@ pub use crate::value::*;
|
||||
mod value;
|
||||
|
||||
/// A trait defining the context in which LIR expressions are evaluated.
|
||||
pub trait EvalContext: Sized {
|
||||
pub trait EvalContext {
|
||||
fn eval_root(self, expr: ExprId) -> Result<Value>;
|
||||
|
||||
|
||||
/// Evaluates an expression by its ID.
|
||||
fn eval(&mut self, expr: ExprId) -> Result<Value>;
|
||||
|
||||
fn call(&mut self, func: ExprId, arg: Option<Value>, frame: StackFrame) -> Result<Value>;
|
||||
/// Enters a `with` scope for the duration of a closure's execution.
|
||||
fn with_with_env<T>(
|
||||
&mut self,
|
||||
@@ -32,27 +36,18 @@ pub trait EvalContext: Sized {
|
||||
f: impl FnOnce(&mut Self) -> T,
|
||||
) -> T;
|
||||
|
||||
/// Pushes a new set of arguments onto the stack for a function call.
|
||||
fn with_args_env<T>(
|
||||
&mut self,
|
||||
args: Vec<Value>,
|
||||
f: impl FnOnce(&mut Self) -> T,
|
||||
) -> (Vec<Value>, T);
|
||||
|
||||
/// Looks up a stack slot on the current stack frame.
|
||||
fn lookup_stack<'a>(&'a self, idx: usize) -> &'a Value;
|
||||
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>;
|
||||
|
||||
/// Looks up a function argument by its index on the current stack frame.
|
||||
fn lookup_arg<'a>(&'a self, idx: ArgIdx) -> &'a Value;
|
||||
|
||||
/// Pops the current stack frame, returning the arguments.
|
||||
fn pop_frame(&mut self) -> Vec<Value>;
|
||||
|
||||
/// Calls a primitive operation (builtin) by its ID.
|
||||
fn call_primop(&mut self, id: PrimOpId, args: Vec<Value>) -> Result<Value>;
|
||||
fn call_primop(&mut self, id: PrimOpId, args: Args) -> Result<Value>;
|
||||
|
||||
fn get_primop_arity(&self, id: PrimOpId) -> usize;
|
||||
}
|
||||
|
||||
/// A trait for types that can be evaluated within an `EvalContext`.
|
||||
@@ -88,10 +83,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) => Ok(ctx.lookup_stack(idx).clone()),
|
||||
&ExprRef(expr) => ctx.eval(expr),
|
||||
&FuncRef(func) => Ok(Value::Func(func)),
|
||||
&ArgRef(idx) => Ok(ctx.lookup_arg(idx).clone()),
|
||||
&FuncRef(body) => Ok(Value::Closure(Closure::new(body, ctx.capture_stack().clone()).into())),
|
||||
&Arg(_) => unreachable!(),
|
||||
&PrimOp(primop) => Ok(Value::PrimOp(primop)),
|
||||
&Thunk(id) => Ok(Value::Thunk(id)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,10 +106,8 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::AttrSet {
|
||||
.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 v = v.eval(ctx)?;
|
||||
attrs.push_attr(k.eval(ctx)?.force_string_no_ctx()?, v)?;
|
||||
}
|
||||
let result = Value::AttrSet(attrs.into());
|
||||
Ok(result)
|
||||
@@ -137,11 +132,9 @@ 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(Value::String(ident.clone())),
|
||||
Dynamic(expr) => expr.eval(ctx),
|
||||
}))?;
|
||||
Ok(val)
|
||||
}
|
||||
@@ -155,7 +148,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::BinOp {
|
||||
if matches!((&self.kind, &lhs), (And, Value::Bool(false))) {
|
||||
return Ok(Value::Bool(false));
|
||||
} else if matches!((&self.kind, &lhs), (Or, Value::Bool(true))) {
|
||||
return Ok(Value::Bool(true))
|
||||
return Ok(Value::Bool(true));
|
||||
}
|
||||
let mut rhs = self.rhs.eval(ctx)?;
|
||||
match self.kind {
|
||||
@@ -193,9 +186,9 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::BinOp {
|
||||
}
|
||||
Con => lhs.concat(rhs)?,
|
||||
Upd => lhs.update(rhs)?,
|
||||
PipeL => lhs.call(core::iter::once(Ok(rhs)), ctx)?,
|
||||
PipeL => lhs.call(rhs, ctx)?,
|
||||
PipeR => {
|
||||
rhs.call(core::iter::once(Ok(lhs)), ctx)?;
|
||||
rhs.call(lhs, ctx)?;
|
||||
lhs = rhs;
|
||||
}
|
||||
}
|
||||
@@ -226,32 +219,20 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Select {
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||
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()
|
||||
}
|
||||
})
|
||||
}))?;
|
||||
for attr in self.attrpath.iter() {
|
||||
let name_val;
|
||||
let name = match attr {
|
||||
Str(name) => name,
|
||||
Dynamic(expr) => {
|
||||
name_val = expr.eval(ctx)?;
|
||||
&*name_val.force_string_no_ctx()?
|
||||
}
|
||||
};
|
||||
if let Some(default) = self.default {
|
||||
val.select_or(name, default, ctx)
|
||||
} else {
|
||||
val.select(name, ctx)
|
||||
}?
|
||||
}
|
||||
Ok(val)
|
||||
}
|
||||
@@ -260,9 +241,9 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Select {
|
||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::If {
|
||||
/// Evaluates an `If` by evaluating the condition and then either the consequence or the alternative.
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||
let cond = self.cond.eval(ctx)?;
|
||||
let cond = cond.as_ref().try_unwrap_bool().map_err(|_| {
|
||||
Error::EvalError(format!(
|
||||
let cond = &self.cond.eval(ctx)?;
|
||||
let &cond = cond.try_into().map_err(|_| {
|
||||
Error::eval_error(format!(
|
||||
"if-condition must be a boolean, but got {}",
|
||||
cond.typename()
|
||||
))
|
||||
@@ -277,11 +258,9 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::If {
|
||||
}
|
||||
|
||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Call {
|
||||
/// Evaluates a `Call` by evaluating the function and its arguments, then performing the call.
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||
let mut func = self.func.eval(ctx)?;
|
||||
let ctx_mut = unsafe { &mut *(ctx as *mut Ctx) };
|
||||
func.call(self.args.iter().map(|arg| arg.eval(ctx)), ctx_mut)?;
|
||||
func.call(self.arg.eval(ctx)?, ctx)?;
|
||||
Ok(func)
|
||||
}
|
||||
}
|
||||
@@ -296,7 +275,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::With {
|
||||
namespace
|
||||
.try_unwrap_attr_set()
|
||||
.map_err(|_| {
|
||||
Error::EvalError(format!("'with' expects a set, but got {}", typename))
|
||||
Error::eval_error(format!("'with' expects a set, but got {}", typename))
|
||||
})?
|
||||
.into_inner(),
|
||||
|ctx| self.expr.eval(ctx),
|
||||
@@ -308,9 +287,9 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Assert {
|
||||
/// Evaluates an `Assert` by evaluating the condition. If true, it evaluates and
|
||||
/// returns the body; otherwise, it returns an error.
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||
let cond = self.assertion.eval(ctx)?;
|
||||
let cond = cond.as_ref().try_unwrap_bool().map_err(|_| {
|
||||
Error::EvalError(format!(
|
||||
let cond = &self.assertion.eval(ctx)?;
|
||||
let &cond = cond.try_into().map_err(|_| {
|
||||
Error::eval_error(format!(
|
||||
"assertion condition must be a boolean, but got {}",
|
||||
cond.typename()
|
||||
))
|
||||
@@ -318,7 +297,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Assert {
|
||||
if cond {
|
||||
self.expr.eval(ctx)
|
||||
} else {
|
||||
Err(Error::Catchable("assertion failed".into()))
|
||||
Err(Error::catchable("assertion failed".into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -329,7 +308,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::ConcatStrings {
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||
let mut buf = String::new();
|
||||
for part in self.parts.iter() {
|
||||
buf.push_str(part.eval(ctx)?.coerce_to_string()?.as_ref().unwrap_string());
|
||||
buf.push_str(&part.eval(ctx)?.force_string_no_ctx()?);
|
||||
}
|
||||
Ok(Value::String(buf))
|
||||
}
|
||||
@@ -361,7 +340,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Var {
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||
ctx.lookup_with(&self.sym)
|
||||
.ok_or_else(|| {
|
||||
Error::EvalError(format!("undefined variable '{}'", format_symbol(&self.sym)))
|
||||
Error::eval_error(format!("undefined variable '{}'", format_symbol(&self.sym)))
|
||||
})
|
||||
.map(|val| val.clone())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user