use std::sync::Arc;
use crate::{
Symbol,
env::Cx,
error::{Error, Result},
eval::{Demand, LazyThunkObject, PreparedArgs, ThunkObject},
expr::Expr,
object::RawArgs,
value::Value,
};
use super::protocol::Phase;
pub trait EvalPolicy: Send + Sync {
fn name(&self) -> &'static str;
fn allow_macro_expansion(&self, _phase: Phase) -> bool {
false
}
fn prepare_call_args(
&self,
cx: &mut Cx,
raw: RawArgs,
demands: &[Demand],
) -> Result<PreparedArgs>;
fn force(&self, cx: &mut Cx, value: Value, demand: Demand) -> Result<Value>;
fn eval_expr(&self, cx: &mut Cx, expr: Expr) -> Result<Value>;
fn resolve_unbound_symbol(&self, cx: &mut Cx, symbol: Symbol) -> Result<Value> {
cx.factory().symbol(symbol)
}
fn resolve_unbound_call(
&self,
cx: &mut Cx,
operator: Symbol,
args: Vec<Expr>,
) -> Result<Value> {
cx.factory().expr(Expr::Call {
operator: Box::new(Expr::Symbol(operator)),
args,
})
}
}
pub type EvalPolicyRef = Arc<dyn EvalPolicy>;
pub struct NoopEvalPolicy;
impl EvalPolicy for NoopEvalPolicy {
fn name(&self) -> &'static str {
"noop"
}
fn prepare_call_args(
&self,
cx: &mut Cx,
raw: RawArgs,
_demands: &[Demand],
) -> Result<PreparedArgs> {
let values = raw
.into_exprs()
.into_iter()
.map(|expr| cx.factory().expr(expr))
.collect::<Result<Vec<_>>>()?;
Ok(PreparedArgs::new(values))
}
fn force(&self, _cx: &mut Cx, value: Value, _demand: Demand) -> Result<Value> {
Ok(value)
}
fn eval_expr(&self, _cx: &mut Cx, _expr: Expr) -> Result<Value> {
Err(Error::Eval(
"noop eval policy cannot evaluate expressions".to_owned(),
))
}
}
pub struct EagerPolicy;
impl EvalPolicy for EagerPolicy {
fn name(&self) -> &'static str {
"eager"
}
fn allow_macro_expansion(&self, phase: Phase) -> bool {
matches!(
phase,
Phase::Read | Phase::Expand | Phase::Compile | Phase::Eval
)
}
fn prepare_call_args(
&self,
cx: &mut Cx,
raw: RawArgs,
_demands: &[Demand],
) -> Result<PreparedArgs> {
let values = raw
.into_exprs()
.into_iter()
.map(|expr| cx.eval_expr(expr))
.collect::<Result<Vec<_>>>()?;
Ok(PreparedArgs::new(values))
}
fn force(&self, cx: &mut Cx, value: Value, demand: Demand) -> Result<Value> {
crate::eval::force_default(cx, value, demand)
}
fn eval_expr(&self, cx: &mut Cx, expr: Expr) -> Result<Value> {
crate::eval::eval_expr_default(cx, expr)
}
}
pub struct LazyPolicy;
impl EvalPolicy for LazyPolicy {
fn name(&self) -> &'static str {
"lazy"
}
fn allow_macro_expansion(&self, phase: Phase) -> bool {
matches!(
phase,
Phase::Read | Phase::Expand | Phase::Compile | Phase::Eval
)
}
fn prepare_call_args(
&self,
cx: &mut Cx,
raw: RawArgs,
_demands: &[Demand],
) -> Result<PreparedArgs> {
lazy_args(cx, raw)
}
fn force(&self, cx: &mut Cx, value: Value, demand: Demand) -> Result<Value> {
crate::eval::force_default(cx, value, demand)
}
fn eval_expr(&self, cx: &mut Cx, expr: Expr) -> Result<Value> {
crate::eval::eval_expr_default(cx, expr)
}
}
pub struct NeedPolicy;
impl EvalPolicy for NeedPolicy {
fn name(&self) -> &'static str {
"lazy-by-need"
}
fn allow_macro_expansion(&self, phase: Phase) -> bool {
matches!(
phase,
Phase::Read | Phase::Expand | Phase::Compile | Phase::Eval
)
}
fn prepare_call_args(
&self,
cx: &mut Cx,
raw: RawArgs,
_demands: &[Demand],
) -> Result<PreparedArgs> {
need_args(cx, raw)
}
fn force(&self, cx: &mut Cx, value: Value, demand: Demand) -> Result<Value> {
crate::eval::force_default(cx, value, demand)
}
fn eval_expr(&self, cx: &mut Cx, expr: Expr) -> Result<Value> {
crate::eval::eval_expr_default(cx, expr)
}
}
pub struct StrictByShapePolicy;
impl EvalPolicy for StrictByShapePolicy {
fn name(&self) -> &'static str {
"strict-by-shape"
}
fn allow_macro_expansion(&self, phase: Phase) -> bool {
matches!(
phase,
Phase::Read | Phase::Expand | Phase::Compile | Phase::Eval
)
}
fn prepare_call_args(
&self,
cx: &mut Cx,
raw: RawArgs,
demands: &[Demand],
) -> Result<PreparedArgs> {
let env = cx.env().clone();
let values = raw
.into_exprs()
.into_iter()
.enumerate()
.map(|(index, expr)| {
let demand = demands.get(index).copied().unwrap_or(Demand::Value);
match demand {
Demand::Never | Demand::Expr => Ok(Value::from_arc(Arc::new(
LazyThunkObject::new(expr, env.clone()),
))),
Demand::Value | Demand::Bool | Demand::Class(_) | Demand::Shape(_) => {
cx.eval_expr(expr)
}
}
})
.collect::<Result<Vec<_>>>()?;
Ok(PreparedArgs::new(values))
}
fn force(&self, cx: &mut Cx, value: Value, demand: Demand) -> Result<Value> {
crate::eval::force_default(cx, value, demand)
}
fn eval_expr(&self, cx: &mut Cx, expr: Expr) -> Result<Value> {
crate::eval::eval_expr_default(cx, expr)
}
}
pub struct HybridPolicy;
impl EvalPolicy for HybridPolicy {
fn name(&self) -> &'static str {
"hybrid"
}
fn allow_macro_expansion(&self, phase: Phase) -> bool {
matches!(
phase,
Phase::Read | Phase::Expand | Phase::Compile | Phase::Eval
)
}
fn prepare_call_args(
&self,
cx: &mut Cx,
raw: RawArgs,
demands: &[Demand],
) -> Result<PreparedArgs> {
let values = raw
.into_exprs()
.into_iter()
.enumerate()
.map(|(index, expr)| {
let demand = demands.get(index).copied().unwrap_or(Demand::Value);
match demand {
Demand::Never | Demand::Expr => cx.factory().expr(expr),
Demand::Value | Demand::Bool | Demand::Class(_) | Demand::Shape(_) => {
cx.eval_expr(expr)
}
}
})
.collect::<Result<Vec<_>>>()?;
Ok(PreparedArgs::new(values))
}
fn force(&self, cx: &mut Cx, value: Value, demand: Demand) -> Result<Value> {
crate::eval::force_default(cx, value, demand)
}
fn eval_expr(&self, cx: &mut Cx, expr: Expr) -> Result<Value> {
crate::eval::eval_expr_default(cx, expr)
}
}
fn lazy_args(cx: &mut Cx, raw: RawArgs) -> Result<PreparedArgs> {
let env = cx.env().clone();
let values = raw
.into_exprs()
.into_iter()
.map(|expr| {
Ok(Value::from_arc(Arc::new(LazyThunkObject::new(
expr,
env.clone(),
))))
})
.collect::<Result<Vec<_>>>()?;
Ok(PreparedArgs::new(values))
}
fn need_args(cx: &mut Cx, raw: RawArgs) -> Result<PreparedArgs> {
let env = cx.env().clone();
let values = raw
.into_exprs()
.into_iter()
.map(|expr| {
Ok(Value::from_arc(Arc::new(ThunkObject::new(
expr,
env.clone(),
))))
})
.collect::<Result<Vec<_>>>()?;
Ok(PreparedArgs::new(values))
}