klavier_core/
expr.rs

1use std::collections::BTreeMap;
2use fasteval::{self, Compiler, Evaler};
3use crate::expr::fasteval::eval_compiled_ref;
4use crate::note::Note;
5
6#[derive(Debug)]
7pub struct Expr {
8  slab: fasteval::Slab,
9  compiled: fasteval::Instruction,
10}
11
12impl Expr {
13  pub fn new<S: AsRef<str>>(expr: S) -> Result<Self, fasteval::Error>  {
14    let parser = fasteval::Parser::new();
15    let mut slab = fasteval::Slab::new();
16    let compiled: fasteval::Instruction = parser.parse(expr.as_ref(), &mut slab.ps)?.from(&slab.ps).compile(&slab.ps, &mut slab.cs);
17
18    Ok(Self { slab, compiled })
19  }
20  
21  pub fn evaluate_note(&self, note: &Note) -> Result<bool, fasteval::Error>{
22    let mut map: BTreeMap<&str, f64> = BTreeMap::new();
23    map.insert("v", note.velocity().as_u8() as f64); // velocity
24    map.insert("bv", note.base_velocity.as_u8() as f64); // base velocity
25    map.insert("vt0", note.velocity_trimmer.value(0) as f64); // velocity trimmer 0
26    map.insert("vt1", note.velocity_trimmer.value(1) as f64); // velocity trimmer 1
27    map.insert("vt2", note.velocity_trimmer.value(2) as f64); // velocity trimmer 2
28    map.insert("vt3", note.velocity_trimmer.value(3) as f64); // velocity trimmer 3
29    map.insert("vt", note.velocity_trimmer.sum() as f64); // sum of velocity trimmers
30    
31    #[allow(unexpected_cfgs)]
32    let val: f64 = eval_compiled_ref!(&self.compiled, &self.slab, &mut map);
33
34    Ok(val != 0.)
35  }
36}
37
38#[cfg(test)]
39mod tests {
40    use crate::{note::{Note, NoteBuilder}, trimmer::Trimmer, velocity::Velocity};
41    use super::Expr;
42
43  #[test]
44  fn eval_note_velocity() {
45    let expr0: Expr = Expr::new("bv < 64").unwrap();
46    let expr1: Expr = Expr::new("vt0 < 20").unwrap();
47    let expr2: Expr = Expr::new("vt0 <= 20").unwrap();
48    let expr3: Expr = Expr::new("vt < 20").unwrap();
49    let expr4: Expr = Expr::new("vt <= 20").unwrap();
50    let note0: Note = NoteBuilder::default()
51        .base_velocity(Velocity::new(64))
52        .velocity_trimmer(Trimmer::new(20, 0, 0, 0))
53        .build().unwrap();
54    assert!(!expr0.evaluate_note(&note0).unwrap());
55    assert!(!expr1.evaluate_note(&note0).unwrap());
56    assert!(expr2.evaluate_note(&note0).unwrap());
57    assert!(!expr3.evaluate_note(&note0).unwrap());
58    assert!(expr4.evaluate_note(&note0).unwrap());
59
60    let note1: Note = NoteBuilder::default()
61        .base_velocity(Velocity::new(63))
62        .velocity_trimmer(Trimmer::new(10, 10, 0, 0))
63        .build().unwrap();
64    assert!(expr0.evaluate_note(&note1).unwrap());
65    assert!(expr1.evaluate_note(&note1).unwrap());
66    assert!(expr2.evaluate_note(&note1).unwrap());
67    assert!(!expr3.evaluate_note(&note1).unwrap());
68    assert!(expr4.evaluate_note(&note1).unwrap());
69
70    assert_eq!(Expr::new("foo <= 20").unwrap().evaluate_note(&note1), Err(fasteval::Error::Undefined("foo".to_owned())));
71  }
72}