1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use super::{Condition, Expression, ExpressionError};
use asexp::Sexp;
use std::fmt::Debug;

/// A boolean condition evaluates to either `true` or `false`.
#[derive(Debug, Clone, PartialEq)]
pub enum Cond<E: Expression> {
    True,
    False,
    Not(Box<Cond<E>>),
    And(Box<Cond<E>>, Box<Cond<E>>),
    Or(Box<Cond<E>>, Box<Cond<E>>),

    /// If two expressions are equal
    Equal(Box<E>, Box<E>),

    Less(Box<E>, Box<E>),
    Greater(Box<E>, Box<E>),

    LessEqual(Box<E>, Box<E>),
    GreaterEqual(Box<E>, Box<E>),
}

impl<E: Expression> Condition for Cond<E> {
    type Expr = E;
    fn evaluate(
        &self,
        variables: &[<Self::Expr as Expression>::Element],
    ) -> Result<bool, ExpressionError> {
        Ok(match *self {
            Cond::True => true,
            Cond::False => false,
            Cond::Not(ref c) => !c.evaluate(variables)?,
            Cond::And(ref c1, ref c2) => c1.evaluate(variables)? && c2.evaluate(variables)?,
            Cond::Or(ref c1, ref c2) => c1.evaluate(variables)? || c2.evaluate(variables)?,
            Cond::Equal(ref e1, ref e2) => e1.evaluate(variables)? == e2.evaluate(variables)?,
            Cond::Less(ref e1, ref e2) => e1.evaluate(variables)? < e2.evaluate(variables)?,
            Cond::Greater(ref e1, ref e2) => e1.evaluate(variables)? > e2.evaluate(variables)?,
            Cond::LessEqual(ref e1, ref e2) => e1.evaluate(variables)? <= e2.evaluate(variables)?,
            Cond::GreaterEqual(ref e1, ref e2) => {
                e1.evaluate(variables)? >= e2.evaluate(variables)?
            }
        })
    }
}

impl<'a, E, T> Into<Sexp> for &'a Cond<E>
where
    E: Expression<Element = T>,
    &'a E: Into<Sexp>,
    T: Debug + Copy + Clone + PartialEq + PartialOrd,
{
    fn into(self) -> Sexp {
        match self {
            &Cond::True => Sexp::from("true"),
            &Cond::False => Sexp::from("false"),
            &Cond::Not(ref a) => Sexp::from(("not", Into::<Sexp>::into(a.as_ref()))),
            &Cond::And(ref a, ref b) => Sexp::from((
                "and",
                Into::<Sexp>::into(a.as_ref()),
                Into::<Sexp>::into(b.as_ref()),
            )),
            &Cond::Or(ref a, ref b) => Sexp::from((
                "or",
                Into::<Sexp>::into(a.as_ref()),
                Into::<Sexp>::into(b.as_ref()),
            )),
            &Cond::Equal(ref a, ref b) => Sexp::from((
                "==",
                Into::<Sexp>::into(a.as_ref()),
                Into::<Sexp>::into(b.as_ref()),
            )),
            &Cond::Less(ref a, ref b) => Sexp::from((
                "<",
                Into::<Sexp>::into(a.as_ref()),
                Into::<Sexp>::into(b.as_ref()),
            )),
            &Cond::Greater(ref a, ref b) => Sexp::from((
                ">",
                Into::<Sexp>::into(a.as_ref()),
                Into::<Sexp>::into(b.as_ref()),
            )),
            &Cond::LessEqual(ref a, ref b) => Sexp::from((
                "<=",
                Into::<Sexp>::into(a.as_ref()),
                Into::<Sexp>::into(b.as_ref()),
            )),
            &Cond::GreaterEqual(ref a, ref b) => Sexp::from((
                ">=",
                Into::<Sexp>::into(a.as_ref()),
                Into::<Sexp>::into(b.as_ref()),
            )),
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::{cond::Cond, Condition, Expression, ExpressionError};

    #[derive(Debug, Clone, PartialEq, PartialOrd)]
    struct ConstNum(f32);

    impl Expression for ConstNum {
        type Element = f32;
        fn evaluate(&self, _variables: &[Self::Element]) -> Result<Self::Element, ExpressionError> {
            Ok(self.0)
        }
    }

    #[test]
    fn test_condition() {
        let no_vars: &[f32] = &[];

        let cond = Cond::Greater(Box::new(ConstNum(0.1)), Box::new(ConstNum(0.2)));
        assert_eq!(Ok(false), cond.evaluate(no_vars));

        let cond = Cond::Not(Box::new(Cond::Greater(
            Box::new(ConstNum(0.1)),
            Box::new(ConstNum(0.2)),
        )));
        assert_eq!(Ok(true), cond.evaluate(no_vars));
    }
}