chore: comment
This commit is contained in:
@@ -1,3 +1,12 @@
|
||||
//! This module defines the core traits and logic for evaluating the LIR.
|
||||
//!
|
||||
//! The central components are:
|
||||
//! - `EvalContext`: A trait that defines the environment and operations needed for evaluation.
|
||||
//! It manages the evaluation stack, scopes, and primop calls.
|
||||
//! - `Evaluate`: A trait implemented by LIR nodes to define how they are evaluated.
|
||||
//! - `Value`: An enum representing all possible values during evaluation. This is an
|
||||
//! internal representation, distinct from the public-facing `nixjit_value::Value`.
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
@@ -5,37 +14,59 @@ use hashbrown::HashMap;
|
||||
use nixjit_error::{Error, Result};
|
||||
use nixjit_ir::{self as ir, ArgIdx, ExprId, PrimOpId};
|
||||
use nixjit_lir as lir;
|
||||
use nixjit_value::{Const, Symbol};
|
||||
use nixjit_value::{Const, Symbol, format_symbol};
|
||||
|
||||
pub use crate::value::*;
|
||||
|
||||
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>;
|
||||
|
||||
/// Enters a `with` scope for the duration of a closure's execution.
|
||||
fn with_with_env<T>(
|
||||
&mut self,
|
||||
namespace: Rc<HashMap<String, Value>>,
|
||||
f: impl FnOnce(&mut Self) -> T,
|
||||
) -> T;
|
||||
fn with_args_env<T>(&mut self, args: Vec<Value>, f: impl FnOnce(&mut Self) -> T) -> (Vec<Value>, 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 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>;
|
||||
}
|
||||
|
||||
/// A trait for types that can be evaluated within an `EvalContext`.
|
||||
pub trait Evaluate<Ctx: EvalContext> {
|
||||
/// Performs the evaluation.
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value>;
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx: EvalContext> Evaluate<Ctx> for lir::Lir {
|
||||
/// Evaluates an LIR node by dispatching to the specific implementation for its variant.
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||
use lir::Lir::*;
|
||||
match self {
|
||||
@@ -63,6 +94,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for lir::Lir {
|
||||
}
|
||||
|
||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::AttrSet {
|
||||
/// Evaluates an `AttrSet` by evaluating all its static and dynamic attributes.
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||
let mut attrs = AttrSet::new(
|
||||
self.stcs
|
||||
@@ -79,24 +111,26 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::AttrSet {
|
||||
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())
|
||||
let result = Value::AttrSet(attrs.into());
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::List {
|
||||
/// Evaluates a `List` by evaluating all its items.
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||
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())
|
||||
let result = Value::List(List::from(items).into());
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::HasAttr {
|
||||
/// Evaluates a `HasAttr` by evaluating the LHS and the attribute path, then performing the check.
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||
use ir::Attr::*;
|
||||
let mut val = self.lhs.eval(ctx)?;
|
||||
@@ -110,12 +144,12 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::HasAttr {
|
||||
}
|
||||
})
|
||||
}))?;
|
||||
let result = val.ok();
|
||||
Ok(result.unwrap())
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::BinOp {
|
||||
/// Evaluates a `BinOp` by evaluating the LHS and RHS, then performing the operation.
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||
use ir::BinOpKind::*;
|
||||
let mut lhs = self.lhs.eval(ctx)?;
|
||||
@@ -166,6 +200,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::BinOp {
|
||||
}
|
||||
|
||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::UnOp {
|
||||
/// Evaluates a `UnOp` by evaluating the RHS and performing the operation.
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||
use ir::UnOpKind::*;
|
||||
let mut rhs = self.rhs.eval(ctx)?;
|
||||
@@ -182,6 +217,8 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::UnOp {
|
||||
}
|
||||
|
||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Select {
|
||||
/// Evaluates a `Select` by evaluating the expression, the path, and the default value (if any),
|
||||
/// then performing the selection.
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||
use ir::Attr::*;
|
||||
let mut val = self.expr.eval(ctx)?;
|
||||
@@ -212,18 +249,20 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Select {
|
||||
})
|
||||
}))?;
|
||||
}
|
||||
let result = val.ok();
|
||||
Ok(result.unwrap())
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
// 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 ...")))?;
|
||||
let cond = cond.as_ref().try_unwrap_bool().map_err(|_| {
|
||||
Error::EvalError(format!(
|
||||
"if-condition must be a boolean, but got {}",
|
||||
cond.typename()
|
||||
))
|
||||
})?;
|
||||
|
||||
if cond {
|
||||
self.consq.eval(ctx)
|
||||
@@ -234,27 +273,27 @@ 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)?;
|
||||
// FIXME: ?
|
||||
let ctx_mut = unsafe { &mut *(ctx as *mut Ctx) };
|
||||
func.call(
|
||||
self.args
|
||||
.iter()
|
||||
.map(|arg| arg.eval(ctx)),
|
||||
ctx_mut,
|
||||
)?;
|
||||
Ok(func.ok().unwrap())
|
||||
func.call(self.args.iter().map(|arg| arg.eval(ctx)), ctx_mut)?;
|
||||
Ok(func)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::With {
|
||||
/// Evaluates a `With` by evaluating the namespace, entering a `with` scope,
|
||||
/// and then evaluating the body.
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||
let namespace = self.namespace.eval(ctx)?;
|
||||
let typename = namespace.typename();
|
||||
ctx.with_with_env(
|
||||
namespace
|
||||
.try_unwrap_attr_set()
|
||||
.map_err(|_| Error::EvalError(format!("expected a set but found ...")))?
|
||||
.map_err(|_| {
|
||||
Error::EvalError(format!("'with' expects a set, but got {}", typename))
|
||||
})?
|
||||
.into_inner(),
|
||||
|ctx| self.expr.eval(ctx),
|
||||
)
|
||||
@@ -262,20 +301,27 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::With {
|
||||
}
|
||||
|
||||
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
|
||||
.try_unwrap_bool()
|
||||
.map_err(|_| Error::EvalError(format!("expected a boolean but found ...")))?;
|
||||
let cond = cond.as_ref().try_unwrap_bool().map_err(|_| {
|
||||
Error::EvalError(format!(
|
||||
"assertion condition must be a boolean, but got {}",
|
||||
cond.typename()
|
||||
))
|
||||
})?;
|
||||
if cond {
|
||||
self.expr.eval(ctx)
|
||||
} else {
|
||||
Err(Error::EvalError("assertion failed".to_string()))
|
||||
Ok(Value::Catchable("assertion failed".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::ConcatStrings {
|
||||
/// Evaluates a `ConcatStrings` by evaluating each part, coercing it to a string,
|
||||
/// and then concatenating the results.
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||
let mut parts = self
|
||||
.parts
|
||||
@@ -283,7 +329,7 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::ConcatStrings {
|
||||
.map(|part| {
|
||||
let mut part = part.eval(ctx)?;
|
||||
part.coerce_to_string();
|
||||
part.ok()
|
||||
Ok(part)
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?
|
||||
.into_iter();
|
||||
@@ -292,44 +338,44 @@ impl<Ctx: EvalContext> Evaluate<Ctx> for ir::ConcatStrings {
|
||||
a.concat_string(b);
|
||||
a
|
||||
});
|
||||
Ok(result.ok().unwrap())
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Str {
|
||||
/// Evaluates a `Str` literal into a `Value::String`.
|
||||
fn eval(&self, _: &mut Ctx) -> Result<Value> {
|
||||
let result = Value::String(self.val.clone()).ok();
|
||||
Ok(result.unwrap())
|
||||
Ok(Value::String(self.val.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Const {
|
||||
/// Evaluates a `Const` literal into its corresponding `Value` variant.
|
||||
fn eval(&self, _: &mut Ctx) -> Result<Value> {
|
||||
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())
|
||||
};
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
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::EvalError(format!(
|
||||
"variable {} not found",
|
||||
Symbol::from(self.sym.clone())
|
||||
))
|
||||
Error::EvalError(format!("undefined variable '{}'", format_symbol(&self.sym)))
|
||||
})
|
||||
.map(|val| val.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Path {
|
||||
/// Evaluates a `Path`. (Currently a TODO).
|
||||
fn eval(&self, ctx: &mut Ctx) -> Result<Value> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user