ergotree_interpreter/eval/
coll_exists.rs

1use ergotree_ir::mir::coll_exists::Exists;
2use ergotree_ir::mir::constant::TryExtractInto;
3use ergotree_ir::mir::value::Value;
4
5use crate::eval::env::Env;
6use crate::eval::Context;
7use crate::eval::EvalError;
8use crate::eval::Evaluable;
9
10impl Evaluable for Exists {
11    fn eval<'ctx>(
12        &self,
13        env: &mut Env<'ctx>,
14        ctx: &Context<'ctx>,
15    ) -> Result<Value<'ctx>, EvalError> {
16        let input_v = self.input.eval(env, ctx)?;
17        let condition_v = self.condition.eval(env, ctx)?;
18        let input_v_clone = input_v.clone();
19        let mut condition_call = |arg: Value<'ctx>| match &condition_v {
20            Value::Lambda(func_value) => {
21                let func_arg = func_value.args.first().ok_or_else(|| {
22                    EvalError::NotFound(
23                        "Exists: evaluated condition has empty arguments list".to_string(),
24                    )
25                })?;
26                let orig_val = env.get(func_arg.idx).cloned();
27                env.insert(func_arg.idx, arg);
28                let res = func_value.body.eval(env, ctx);
29                if let Some(orig_val) = orig_val {
30                    env.insert(func_arg.idx, orig_val);
31                } else {
32                    env.remove(&func_arg.idx);
33                }
34                res
35            }
36            _ => Err(EvalError::UnexpectedValue(format!(
37                "expected Exists::condition to be Value::FuncValue got: {0:?}",
38                input_v_clone
39            ))),
40        };
41        let normalized_input_vals: Vec<Value> = match input_v {
42            Value::Coll(coll) => {
43                if coll.elem_tpe() != &*self.elem_tpe {
44                    return Err(EvalError::UnexpectedValue(format!(
45                        "expected Exists input element type to be {0:?}, got: {1:?}",
46                        self.elem_tpe,
47                        coll.elem_tpe()
48                    )));
49                };
50                Ok(coll.as_vec())
51            }
52            _ => Err(EvalError::UnexpectedValue(format!(
53                "expected Map input to be Value::Coll, got: {0:?}",
54                input_v
55            ))),
56        }?;
57
58        for item in normalized_input_vals {
59            let res = condition_call(item)?.try_extract_into::<bool>()?;
60            if res {
61                return Ok(true.into());
62            }
63        }
64        Ok(false.into())
65    }
66}
67
68#[allow(clippy::unwrap_used)]
69#[cfg(test)]
70mod tests {
71
72    use crate::eval::tests::eval_out_wo_ctx;
73
74    use super::*;
75
76    use ergotree_ir::mir::bin_op::BinOp;
77    use ergotree_ir::mir::bin_op::RelationOp;
78    use ergotree_ir::mir::expr::Expr;
79    use ergotree_ir::mir::func_value::FuncArg;
80    use ergotree_ir::mir::func_value::FuncValue;
81    use ergotree_ir::mir::val_use::ValUse;
82    use ergotree_ir::types::stype::SType;
83
84    fn check(coll: Vec<i32>) {
85        let body: Expr = BinOp {
86            kind: RelationOp::Le.into(),
87            left: Box::new(Expr::Const(1i32.into())),
88            right: Box::new(
89                ValUse {
90                    val_id: 1.into(),
91                    tpe: SType::SBox,
92                }
93                .into(),
94            ),
95        }
96        .into();
97        let expr: Expr = Exists::new(
98            coll.clone().into(),
99            FuncValue::new(
100                vec![FuncArg {
101                    idx: 1.into(),
102                    tpe: SType::SInt,
103                }],
104                body,
105            )
106            .into(),
107        )
108        .unwrap()
109        .into();
110        assert_eq!(
111            eval_out_wo_ctx::<bool>(&expr),
112            coll.iter().any(|it| 1 <= *it)
113        );
114    }
115
116    #[test]
117    fn eval_emty_coll() {
118        check(Vec::<i32>::new());
119    }
120
121    #[test]
122    fn eval_true() {
123        check(vec![1, 2]);
124    }
125
126    #[test]
127    fn eval_false() {
128        check(vec![2, 2]);
129    }
130}