second_music_system/engine/
interpreter.rs1use 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}