sim-expr-tree-calc 0.2.0

Bounded incremental calculation of ordinary SIM expressions and values.
Documentation
use sim_kernel::{
    Cx, Demand, Error as KernelError, EvalPolicy, Expr, Phase, PreparedArgs, Symbol, Value,
    object::RawArgs,
};

pub const EXPR_TREE_REF: &str = "expr-tree/ref";

pub struct ExprTreeRefPolicy<P> {
    inner: P,
}

impl<P> ExprTreeRefPolicy<P> {
    pub fn new(inner: P) -> Self {
        Self { inner }
    }

    pub fn inner(&self) -> &P {
        &self.inner
    }
}

impl<P: EvalPolicy> EvalPolicy for ExprTreeRefPolicy<P> {
    fn name(&self) -> &'static str {
        "expr-tree-ref"
    }

    fn allow_macro_expansion(&self, phase: Phase) -> bool {
        self.inner.allow_macro_expansion(phase)
    }

    fn prepare_call_args(
        &self,
        cx: &mut Cx,
        raw: RawArgs,
        demands: &[Demand],
    ) -> sim_kernel::Result<PreparedArgs> {
        self.inner.prepare_call_args(cx, raw, demands)
    }

    fn force(&self, cx: &mut Cx, value: Value, demand: Demand) -> sim_kernel::Result<Value> {
        self.inner.force(cx, value, demand)
    }

    fn eval_expr(&self, cx: &mut Cx, expr: Expr) -> sim_kernel::Result<Value> {
        self.inner.eval_expr(cx, expr)
    }

    fn resolve_unbound_symbol(&self, cx: &mut Cx, symbol: Symbol) -> sim_kernel::Result<Value> {
        cx.factory().string(format!("expr-tree/ref:{symbol}"))
    }

    fn resolve_unbound_call(
        &self,
        cx: &mut Cx,
        operator: Symbol,
        args: Vec<Expr>,
    ) -> sim_kernel::Result<Value> {
        if operator.to_string() == EXPR_TREE_REF {
            let Some(reference) = args.first() else {
                return Err(KernelError::Eval(
                    "expr-tree/ref requires a reference".to_owned(),
                ));
            };
            return cx
                .factory()
                .string(format!("expr-tree/ref:{}", expr_text(reference)));
        }
        self.inner.resolve_unbound_call(cx, operator, args)
    }
}

fn expr_text(expr: &Expr) -> String {
    match expr {
        Expr::String(value) => value.clone(),
        Expr::Symbol(symbol) => symbol.to_string(),
        other => format!("{other:?}"),
    }
}