expression/
cond.rs

1use super::{Condition, Expression, ExpressionError};
2use asexp::Sexp;
3use std::fmt::Debug;
4
5/// A boolean condition evaluates to either `true` or `false`.
6#[derive(Debug, Clone, PartialEq)]
7pub enum Cond<E: Expression> {
8    True,
9    False,
10    Not(Box<Cond<E>>),
11    And(Box<Cond<E>>, Box<Cond<E>>),
12    Or(Box<Cond<E>>, Box<Cond<E>>),
13
14    /// If two expressions are equal
15    Equal(Box<E>, Box<E>),
16
17    Less(Box<E>, Box<E>),
18    Greater(Box<E>, Box<E>),
19
20    LessEqual(Box<E>, Box<E>),
21    GreaterEqual(Box<E>, Box<E>),
22}
23
24impl<E: Expression> Condition for Cond<E> {
25    type Expr = E;
26    fn evaluate(
27        &self,
28        variables: &[<Self::Expr as Expression>::Element],
29    ) -> Result<bool, ExpressionError> {
30        Ok(match *self {
31            Cond::True => true,
32            Cond::False => false,
33            Cond::Not(ref c) => !c.evaluate(variables)?,
34            Cond::And(ref c1, ref c2) => c1.evaluate(variables)? && c2.evaluate(variables)?,
35            Cond::Or(ref c1, ref c2) => c1.evaluate(variables)? || c2.evaluate(variables)?,
36            Cond::Equal(ref e1, ref e2) => e1.evaluate(variables)? == e2.evaluate(variables)?,
37            Cond::Less(ref e1, ref e2) => e1.evaluate(variables)? < e2.evaluate(variables)?,
38            Cond::Greater(ref e1, ref e2) => e1.evaluate(variables)? > e2.evaluate(variables)?,
39            Cond::LessEqual(ref e1, ref e2) => e1.evaluate(variables)? <= e2.evaluate(variables)?,
40            Cond::GreaterEqual(ref e1, ref e2) => {
41                e1.evaluate(variables)? >= e2.evaluate(variables)?
42            }
43        })
44    }
45}
46
47impl<'a, E, T> Into<Sexp> for &'a Cond<E>
48where
49    E: Expression<Element = T>,
50    &'a E: Into<Sexp>,
51    T: Debug + Copy + Clone + PartialEq + PartialOrd,
52{
53    fn into(self) -> Sexp {
54        match self {
55            &Cond::True => Sexp::from("true"),
56            &Cond::False => Sexp::from("false"),
57            &Cond::Not(ref a) => Sexp::from(("not", Into::<Sexp>::into(a.as_ref()))),
58            &Cond::And(ref a, ref b) => Sexp::from((
59                "and",
60                Into::<Sexp>::into(a.as_ref()),
61                Into::<Sexp>::into(b.as_ref()),
62            )),
63            &Cond::Or(ref a, ref b) => Sexp::from((
64                "or",
65                Into::<Sexp>::into(a.as_ref()),
66                Into::<Sexp>::into(b.as_ref()),
67            )),
68            &Cond::Equal(ref a, ref b) => Sexp::from((
69                "==",
70                Into::<Sexp>::into(a.as_ref()),
71                Into::<Sexp>::into(b.as_ref()),
72            )),
73            &Cond::Less(ref a, ref b) => Sexp::from((
74                "<",
75                Into::<Sexp>::into(a.as_ref()),
76                Into::<Sexp>::into(b.as_ref()),
77            )),
78            &Cond::Greater(ref a, ref b) => Sexp::from((
79                ">",
80                Into::<Sexp>::into(a.as_ref()),
81                Into::<Sexp>::into(b.as_ref()),
82            )),
83            &Cond::LessEqual(ref a, ref b) => Sexp::from((
84                "<=",
85                Into::<Sexp>::into(a.as_ref()),
86                Into::<Sexp>::into(b.as_ref()),
87            )),
88            &Cond::GreaterEqual(ref a, ref b) => Sexp::from((
89                ">=",
90                Into::<Sexp>::into(a.as_ref()),
91                Into::<Sexp>::into(b.as_ref()),
92            )),
93        }
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use crate::{cond::Cond, Condition, Expression, ExpressionError};
100
101    #[derive(Debug, Clone, PartialEq, PartialOrd)]
102    struct ConstNum(f32);
103
104    impl Expression for ConstNum {
105        type Element = f32;
106        fn evaluate(&self, _variables: &[Self::Element]) -> Result<Self::Element, ExpressionError> {
107            Ok(self.0)
108        }
109    }
110
111    #[test]
112    fn test_condition() {
113        let no_vars: &[f32] = &[];
114
115        let cond = Cond::Greater(Box::new(ConstNum(0.1)), Box::new(ConstNum(0.2)));
116        assert_eq!(Ok(false), cond.evaluate(no_vars));
117
118        let cond = Cond::Not(Box::new(Cond::Greater(
119            Box::new(ConstNum(0.1)),
120            Box::new(ConstNum(0.2)),
121        )));
122        assert_eq!(Ok(true), cond.evaluate(no_vars));
123    }
124}