use std::sync::Arc;
use crate::eval::environment::Environment;
use crate::eval::error::{EvalError, EvalResult};
use crate::eval::timestamp::next_truncation_boundary;
use crate::reverse_eval::domain::Constraint;
use crate::value::Value;
use hamelin_lib::func::def::ParameterBinding;
use hamelin_lib::tree::ast::expression::{Expression, ExpressionKind};
use hamelin_lib::tree::typed_ast::expression::{
TypedApply, TypedExpression, TypedExpressionKind, TypedTsTrunc,
};
pub fn reverse_eval(
expr: &TypedExpression,
output_constraint: Constraint,
env: &Environment,
) -> EvalResult<Option<Constraint>> {
if let Constraint::Empty = output_constraint {
return Ok(None);
}
match &expr.kind {
TypedExpressionKind::Leaf => reverse_eval_leaf(&expr.ast, output_constraint),
TypedExpressionKind::FieldReference(_) => {
Ok(Some(output_constraint))
}
TypedExpressionKind::Apply(apply) => reverse_eval_apply(apply, output_constraint, env),
TypedExpressionKind::VariantIndexAccess(_) => Err(EvalError::NotImplemented {
feature: "Reverse evaluation for VariantIndexAccess".to_string(),
}),
TypedExpressionKind::Cast(_) => Err(EvalError::NotImplemented {
feature: "Reverse evaluation for Cast expressions".to_string(),
}),
TypedExpressionKind::TsTrunc(ts_trunc) => {
reverse_eval_ts_trunc(ts_trunc, output_constraint, env)
}
_ => Err(EvalError::NotImplemented {
feature: format!("Reverse evaluation for {:?}", expr.kind),
}),
}
}
fn reverse_eval_leaf(
expr: &Arc<Expression>,
output_constraint: Constraint,
) -> EvalResult<Option<Constraint>> {
match &expr.kind {
ExpressionKind::FieldReference(_col_ref) => {
Ok(Some(output_constraint))
}
ExpressionKind::IntLiteral(lit) => {
let value = Value::Int(lit.int);
if output_constraint.is_satisfied_by(&value) {
Ok(None) } else {
Err(EvalError::execution("Constraint is unsatisfiable"))
}
}
ExpressionKind::BooleanLiteral(lit) => {
let value = Value::Boolean(lit.value);
if output_constraint.is_satisfied_by(&value) {
Ok(None)
} else {
Err(EvalError::execution("Constraint is unsatisfiable"))
}
}
ExpressionKind::StringLiteral(lit) => {
let value = Value::String(lit.value.clone());
if output_constraint.is_satisfied_by(&value) {
Ok(None)
} else {
Err(EvalError::execution("Constraint is unsatisfiable"))
}
}
ExpressionKind::ScientificLiteral(lit) => {
let value = Value::Double(lit.value);
if output_constraint.is_satisfied_by(&value) {
Ok(None)
} else {
Err(EvalError::execution("Constraint is unsatisfiable"))
}
}
ExpressionKind::DoubleLiteral(lit) => {
let value = Value::Double(lit.value);
if output_constraint.is_satisfied_by(&value) {
Ok(None)
} else {
Err(EvalError::execution("Constraint is unsatisfiable"))
}
}
ExpressionKind::NullLiteral(_) => {
let value = Value::Null;
if output_constraint.is_satisfied_by(&value) {
Ok(None)
} else {
Err(EvalError::execution("Constraint is unsatisfiable"))
}
}
_ => Err(EvalError::NotImplemented {
feature: format!("Reverse evaluation for leaf expression: {:?}", expr.kind),
}),
}
}
fn reverse_eval_apply(
apply: &TypedApply,
output_constraint: Constraint,
env: &Environment,
) -> EvalResult<Option<Constraint>> {
let func_def = &apply.function_def;
let (eval_params, variable_param) = evaluate_constant_params(&apply.parameter_binding, env)?;
match variable_param {
Some(variable_expr) => {
let type_id = func_def.type_id();
let input_constraint = env
.registry()
.reverse(type_id, &output_constraint, eval_params)
.ok_or_else(|| {
EvalError::execution(format!(
"Function '{}' has no reverse evaluation implementation",
func_def.name()
))
})??;
reverse_eval(variable_expr, input_constraint, env)
}
None => Ok(None), }
}
fn reverse_eval_ts_trunc(
ts_trunc: &TypedTsTrunc,
output_constraint: Constraint,
env: &Environment,
) -> EvalResult<Option<Constraint>> {
match output_constraint {
Constraint::Empty => Ok(None),
Constraint::Universal => {
reverse_eval(&ts_trunc.expression, Constraint::Universal, env)
}
Constraint::Equals(Value::Timestamp(truncated_ts)) => {
let next =
next_truncation_boundary(&truncated_ts, &ts_trunc.unit, ts_trunc.multiplier)?;
let input_constraint = Constraint::Range {
min: Some(truncated_ts.into()),
max: Some(next.into()),
};
reverse_eval(&ts_trunc.expression, input_constraint, env)
}
Constraint::Equals(_) => {
Err(EvalError::execution(
"TsTrunc output must be a timestamp value",
))
}
Constraint::Range { min, max } => {
match (min, max) {
(Some(Value::Timestamp(min_ts)), Some(Value::Timestamp(max_ts))) => {
let max_next =
next_truncation_boundary(&max_ts, &ts_trunc.unit, ts_trunc.multiplier)?;
let input_constraint = Constraint::Range {
min: Some(min_ts.clone().into()),
max: Some(max_next.into()),
};
reverse_eval(&ts_trunc.expression, input_constraint, env)
}
(Some(Value::Timestamp(min_ts)), None) => {
let input_constraint = Constraint::Range {
min: Some(min_ts.clone().into()),
max: None,
};
reverse_eval(&ts_trunc.expression, input_constraint, env)
}
(None, Some(Value::Timestamp(max_ts))) => {
let max_next =
next_truncation_boundary(&max_ts, &ts_trunc.unit, ts_trunc.multiplier)?;
let input_constraint = Constraint::Range {
min: None,
max: Some(max_next.into()),
};
reverse_eval(&ts_trunc.expression, input_constraint, env)
}
_ => {
Err(EvalError::execution(
"TsTrunc range bounds must be timestamps",
))
}
}
}
}
}
fn evaluate_constant_params<'a>(
params: &'a ParameterBinding<Arc<TypedExpression>>,
env: &Environment,
) -> EvalResult<(ParameterBinding<Option<Value>>, Option<&'a TypedExpression>)> {
use crate::eval::evaluator::eval;
let variable_exprs: Vec<&TypedExpression> = params
.iter()
.filter(|expr| eval(expr, env).is_err())
.map(|expr| expr.as_ref())
.collect();
if variable_exprs.len() > 1 {
return Err(EvalError::execution(
"Multiple parameters with variables not supported in reverse evaluation",
));
}
let eval_params = params.clone().map(|expr| eval(&expr, env).ok());
Ok((eval_params, variable_exprs.first().copied()))
}