second_music_system/engine/
interpreter.rs

1use std::collections::HashMap;
2
3use crate::data::{PredicateOp, StringOrNumber};
4
5use compact_str::CompactString;
6
7macro_rules! op {
8    ($stack:ident, |$operand:ident| $expr:expr) => {{
9        assert!($stack.len() >= 1, "stack underflow");
10        let $operand = $stack.pop().unwrap();
11        let result = $expr.into();
12        $stack.push(result);
13    }};
14    ($stack:ident, |$lhs:ident, $rhs:ident| $expr:expr) => {{
15        assert!($stack.len() >= 2, "stack underflow");
16        let $rhs = $stack.pop().unwrap();
17        let $lhs = $stack.pop().unwrap();
18        let result = $expr.into();
19        $stack.push(result);
20    }};
21}
22
23pub(crate) fn evaluate(
24    flow_controls: &HashMap<CompactString, StringOrNumber>,
25    ops: &[PredicateOp],
26) -> StringOrNumber {
27    let mut stack: Vec<StringOrNumber> = Vec::with_capacity(16);
28    for op in ops.iter() {
29        use PredicateOp::*;
30        match op {
31            PushVar(x) => {
32                stack.push(flow_controls.get(x).cloned().unwrap_or_default())
33            }
34            PushConst(x) => stack.push(x.clone()),
35            Eq => op!(stack, |a, b| a == b),
36            NotEq => op!(stack, |a, b| a != b),
37            Greater => op!(stack, |a, b| a > b),
38            GreaterEq => op!(stack, |a, b| a >= b),
39            Lesser => op!(stack, |a, b| a < b),
40            LesserEq => op!(stack, |a, b| a <= b),
41            And => op!(stack, |a, b| a.is_truthy() && b.is_truthy()),
42            Or => op!(stack, |a, b| a.is_truthy() || b.is_truthy()),
43            Xor => op!(stack, |a, b| a.is_truthy() ^ b.is_truthy()),
44            Not => op!(stack, |a| !a.is_truthy()),
45            Add => op!(stack, |a, b| a.as_number() + b.as_number()),
46            Sub => op!(stack, |a, b| a.as_number() - b.as_number()),
47            Mul => op!(stack, |a, b| a.as_number() * b.as_number()),
48            Div => op!(stack, |a, b| a.as_number() / b.as_number()),
49            Rem => op!(stack, |a, b| a.as_number() % b.as_number()),
50            IDiv => op!(stack, |a, b| (a.as_number() / b.as_number()).floor()),
51            Pow => op!(stack, |a, b| a.as_number().powf(b.as_number())),
52            Sin => op!(stack, |a| a.as_number().sin().to_degrees()),
53            Cos => op!(stack, |a| a.as_number().cos().to_degrees()),
54            Tan => op!(stack, |a| a.as_number().tan().to_degrees()),
55            ASin => op!(stack, |a| a.as_number().asin().to_degrees()),
56            ACos => op!(stack, |a| a.as_number().acos().to_degrees()),
57            ATan => op!(stack, |a| a.as_number().atan().to_degrees()),
58            ATan2 => op!(stack, |a, b| a
59                .as_number()
60                .atan2(b.as_number())
61                .to_degrees()),
62            Log => op!(stack, |a| a.as_number().ln()),
63            Exp => op!(stack, |a| a.as_number().exp()),
64            Floor => op!(stack, |a| a.as_number().floor()),
65            Ceil => op!(stack, |a| a.as_number().ceil()),
66            Min => op!(stack, |a, b| a.as_number().min(b.as_number())),
67            Max => op!(stack, |a, b| a.as_number().max(b.as_number())),
68            Abs => op!(stack, |a| a.as_number().abs()),
69            Sign => op!(stack, |a| a.as_number().signum()),
70            Negate => op!(stack, |a| -a.as_number()),
71        }
72    }
73    assert_eq!(stack.len(), 1, "stack left with more than one value???");
74    stack.remove(0)
75}