secel/
evaluator.rs

1//! Evaluator implementation.
2
3use crate::ast::AstNode;
4use crate::errors::Result;
5use crate::values::Value;
6use std::collections::HashMap;
7
8/// Type alias for the key that indexes values.
9pub type IndexKey = u8;
10
11/// Type alias for the index of values.
12pub type IndexedValues = HashMap<IndexKey, Value>;
13
14/// Type alias for the evaluator of the expression.
15pub type Evaluator = Box<dyn Fn(&IndexedValues) -> Value>;
16
17/// Builds an expression evaluator for given [AstNode].
18pub fn build_evaluator(node: &AstNode) -> Result<Evaluator> {
19  match node {
20    AstNode::And(lhs, rhs) => build_and(lhs, rhs),
21    AstNode::Eq(lhs, rhs) => build_eq(lhs, rhs),
22    AstNode::Ge(lhs, rhs) => build_ge(lhs, rhs),
23    AstNode::Gt(lhs, rhs) => build_gt(lhs, rhs),
24    AstNode::If(mhs, lhs, rhs) => build_if(mhs, lhs, rhs),
25    AstNode::Le(lhs, rhs) => build_le(lhs, rhs),
26    AstNode::Lt(lhs, rhs) => build_lt(lhs, rhs),
27    AstNode::Nq(lhs, rhs) => build_nq(lhs, rhs),
28    AstNode::Null => build_null(),
29    AstNode::Number(mhs) => build_number(*mhs),
30    AstNode::Or(lhs, rhs) => build_or(lhs, rhs),
31  }
32}
33
34/// Builds an evaluator for `and` operator.
35fn build_and(lhs: &AstNode, rhs: &AstNode) -> Result<Evaluator> {
36  let lhe = build_evaluator(lhs)?;
37  let rhe = build_evaluator(rhs)?;
38  Ok(Box::new(move |iv: &IndexedValues| {
39    if let Value::Bool(lhv) = lhe(iv) {
40      if let Value::Bool(rhv) = rhe(iv) {
41        return Value::Bool(lhv && rhv);
42      }
43    }
44    Value::Null
45  }))
46}
47
48/// Builds an evaluator for `=` operator.
49fn build_eq(lhs: &AstNode, rhs: &AstNode) -> Result<Evaluator> {
50  let lhe = build_evaluator(lhs)?;
51  let rhe = build_evaluator(rhs)?;
52  Ok(Box::new(move |iv: &IndexedValues| match lhe(iv) {
53    Value::Number(lhv) => match rhe(iv) {
54      Value::Number(rhv) => Value::Bool(lhv == rhv),
55      Value::Null => Value::Bool(false),
56      _ => Value::Null,
57    },
58    Value::Null => match rhe(iv) {
59      Value::Number(_) => Value::Bool(false),
60      Value::Null => Value::Bool(true),
61      _ => Value::Null,
62    },
63    _ => Value::Null,
64  }))
65}
66
67/// Builds an evaluator for `>` operator.
68fn build_ge(lhs: &AstNode, rhs: &AstNode) -> Result<Evaluator> {
69  let lhe = build_evaluator(lhs)?;
70  let rhe = build_evaluator(rhs)?;
71  Ok(Box::new(move |iv: &IndexedValues| {
72    if let Value::Number(lhv) = lhe(iv) {
73      if let Value::Number(rhv) = rhe(iv) {
74        return Value::Bool(lhv >= rhv);
75      }
76    }
77    Value::Null
78  }))
79}
80
81/// Builds an evaluator for `>=` operator.
82fn build_gt(lhs: &AstNode, rhs: &AstNode) -> Result<Evaluator> {
83  let lhe = build_evaluator(lhs)?;
84  let rhe = build_evaluator(rhs)?;
85  Ok(Box::new(move |iv: &IndexedValues| {
86    if let Value::Number(lhv) = lhe(iv) {
87      if let Value::Number(rhv) = rhe(iv) {
88        return Value::Bool(lhv > rhv);
89      }
90    }
91    Value::Null
92  }))
93}
94
95/// Builds an evaluator for `if` expression.
96fn build_if(mhs: &AstNode, lhs: &AstNode, rhs: &AstNode) -> Result<Evaluator> {
97  let mhe = build_evaluator(mhs)?;
98  let lhe = build_evaluator(lhs)?;
99  let rhe = build_evaluator(rhs)?;
100  Ok(Box::new(move |iv: &IndexedValues| {
101    if let Value::Bool(mhv) = mhe(iv) {
102      return if mhv { lhe(iv) } else { rhe(iv) };
103    }
104    Value::Null
105  }))
106}
107
108/// Builds an evaluator for `<` operator.
109fn build_le(lhs: &AstNode, rhs: &AstNode) -> Result<Evaluator> {
110  let lhe = build_evaluator(lhs)?;
111  let rhe = build_evaluator(rhs)?;
112  Ok(Box::new(move |iv: &IndexedValues| {
113    if let Value::Number(lhv) = lhe(iv) {
114      if let Value::Number(rhv) = rhe(iv) {
115        return Value::Bool(lhv <= rhv);
116      }
117    }
118    Value::Null
119  }))
120}
121
122/// Builds an evaluator for `<=` operator.
123fn build_lt(lhs: &AstNode, rhs: &AstNode) -> Result<Evaluator> {
124  let lhe = build_evaluator(lhs)?;
125  let rhe = build_evaluator(rhs)?;
126  Ok(Box::new(move |iv: &IndexedValues| {
127    if let Value::Number(lhv) = lhe(iv) {
128      if let Value::Number(rhv) = rhe(iv) {
129        return Value::Bool(lhv < rhv);
130      }
131    }
132    Value::Null
133  }))
134}
135
136/// Builds an evaluator for `<>` operator.
137fn build_nq(lhs: &AstNode, rhs: &AstNode) -> Result<Evaluator> {
138  let lhe = build_evaluator(lhs)?;
139  let rhe = build_evaluator(rhs)?;
140  Ok(Box::new(move |iv: &IndexedValues| match lhe(iv) {
141    Value::Number(lhv) => match rhe(iv) {
142      Value::Number(rhv) => Value::Bool(lhv != rhv),
143      Value::Null => Value::Bool(true),
144      _ => Value::Null,
145    },
146    Value::Null => match rhe(iv) {
147      Value::Number(_) => Value::Bool(true),
148      Value::Null => Value::Bool(false),
149      _ => Value::Null,
150    },
151    _ => Value::Null,
152  }))
153}
154
155/// Builds an evaluator for `or` operator.
156fn build_or(lhs: &AstNode, rhs: &AstNode) -> Result<Evaluator> {
157  let lhe = build_evaluator(lhs)?;
158  let rhe = build_evaluator(rhs)?;
159  Ok(Box::new(move |iv: &IndexedValues| {
160    if let Value::Bool(lhv) = lhe(iv) {
161      if let Value::Bool(rhv) = rhe(iv) {
162        return Value::Bool(lhv || rhv);
163      }
164    }
165    Value::Null
166  }))
167}
168
169/// Builds an evaluator for `Null` node.
170fn build_null() -> Result<Evaluator> {
171  Ok(Box::new(move |_: &IndexedValues| Value::Null))
172}
173
174/// Builds an evaluator for `Number` node.
175fn build_number(key: IndexKey) -> Result<Evaluator> {
176  Ok(Box::new(
177    move |iv: &IndexedValues| if let Some(value) = iv.get(&key) { *value } else { Value::Null },
178  ))
179}
180
181#[cfg(test)]
182mod tests {
183  use super::*;
184  use crate::IndexedValues;
185  use rust_decimal::Decimal;
186
187  #[test]
188  fn test_build_if() {
189    let mut m = IndexedValues::new();
190    m.insert(1, Value::Bool(true));
191    m.insert(2, Value::Bool(true));
192    let b = build_if(&AstNode::Number(255), &AstNode::Number(1), &AstNode::Number(2)).unwrap();
193    assert_eq!(Value::Null, b(&m));
194  }
195
196  #[test]
197  fn test_build_ge() {
198    let mut r = IndexedValues::new();
199    r.insert(1, Value::Bool(true));
200    r.insert(2, Value::Bool(false));
201    let b = build_ge(&AstNode::Number(1), &AstNode::Number(1)).unwrap();
202    assert_eq!(Value::Null, b(&r));
203  }
204
205  #[test]
206  fn test_build_gt() {
207    let mut r = IndexedValues::new();
208    r.insert(1, Value::Bool(true));
209    r.insert(2, Value::Bool(false));
210    let b = build_gt(&AstNode::Number(1), &AstNode::Number(1)).unwrap();
211    assert_eq!(Value::Null, b(&r));
212  }
213
214  #[test]
215  fn test_build_le() {
216    let mut r = IndexedValues::new();
217    r.insert(1, Value::Bool(true));
218    r.insert(2, Value::Bool(false));
219    let b = build_le(&AstNode::Number(1), &AstNode::Number(1)).unwrap();
220    assert_eq!(Value::Null, b(&r));
221  }
222
223  #[test]
224  fn test_build_lt() {
225    let mut r = IndexedValues::new();
226    r.insert(1, Value::Bool(true));
227    r.insert(2, Value::Bool(false));
228    let b = build_lt(&AstNode::Number(1), &AstNode::Number(1)).unwrap();
229    assert_eq!(Value::Null, b(&r));
230  }
231
232  #[test]
233  fn test_build_eq() {
234    let mut r = IndexedValues::new();
235    r.insert(1, Value::Bool(true));
236    r.insert(2, Value::Number(Decimal::new(123, 2)));
237    let b = build_eq(&AstNode::Null, &AstNode::Number(1)).unwrap();
238    assert_eq!(Value::Null, b(&r));
239    let b = build_eq(&AstNode::Number(2), &AstNode::Number(1)).unwrap();
240    assert_eq!(Value::Null, b(&r));
241    let b = build_eq(&AstNode::Number(1), &AstNode::Number(2)).unwrap();
242    assert_eq!(Value::Null, b(&r));
243  }
244
245  #[test]
246  fn test_build_nq() {
247    let mut r = IndexedValues::new();
248    r.insert(1, Value::Bool(true));
249    r.insert(2, Value::Number(Decimal::new(123, 2)));
250    let b = build_nq(&AstNode::Null, &AstNode::Number(1)).unwrap();
251    assert_eq!(Value::Null, b(&r));
252    let b = build_nq(&AstNode::Number(2), &AstNode::Number(1)).unwrap();
253    assert_eq!(Value::Null, b(&r));
254    let b = build_nq(&AstNode::Number(1), &AstNode::Number(2)).unwrap();
255    assert_eq!(Value::Null, b(&r));
256  }
257
258  #[test]
259  fn test_build_and() {
260    let r = IndexedValues::new();
261    let b = build_and(&AstNode::Null, &AstNode::Null).unwrap();
262    assert_eq!(Value::Null, b(&r));
263  }
264
265  #[test]
266  fn test_build_or() {
267    let r = IndexedValues::new();
268    let b = build_or(&AstNode::Null, &AstNode::Null).unwrap();
269    assert_eq!(Value::Null, b(&r));
270  }
271
272  #[test]
273  fn test_build_null() {
274    let r = IndexedValues::new();
275    let b = build_null().unwrap();
276    assert_eq!(Value::Null, b(&r));
277  }
278
279  #[test]
280  fn test_build_number() {
281    let mut r = IndexedValues::new();
282    let b = build_number(1).unwrap();
283    assert_eq!(Value::Null, b(&r));
284    r.insert(1, Value::Number(Decimal::new(123, 2)));
285    assert_eq!(Value::Number(Decimal::new(123, 2)), b(&r));
286  }
287}