ergotree_interpreter/eval/
coll_exists.rs1use 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}