Compare commits
2 commits
892914256d
...
66b42e2aa0
Author | SHA1 | Date | |
---|---|---|---|
66b42e2aa0 | |||
aa512e6206 |
4 changed files with 302 additions and 8 deletions
|
@ -1,13 +1,16 @@
|
|||
use std::num::{NonZeroU16, NonZeroU8};
|
||||
|
||||
use anyhow::Context;
|
||||
use bng_macros::{QuickModifierParser, SlopeModifierParser};
|
||||
use fasteval::Instruction;
|
||||
use strum::EnumDiscriminants;
|
||||
|
||||
mod lex;
|
||||
mod utils;
|
||||
|
||||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
||||
#[cfg_attr(debug_assertions, derive(Debug, PartialEq))]
|
||||
pub(super) enum Atom {
|
||||
Note(char),
|
||||
Note(u8),
|
||||
Rest,
|
||||
StartHere,
|
||||
Modifier(Modifier),
|
||||
|
@ -18,7 +21,7 @@ pub(super) enum Atom {
|
|||
Comment,
|
||||
}
|
||||
|
||||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
||||
#[cfg_attr(debug_assertions, derive(Debug, PartialEq))]
|
||||
pub(super) enum FlatAtom {
|
||||
Note(u8),
|
||||
Rest,
|
||||
|
@ -49,7 +52,8 @@ impl Clone for FlatAtom {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(debug_assertions, derive(Debug, PartialEq))]
|
||||
pub(super) enum Modifier {
|
||||
Volume(u8),
|
||||
Octave(u8),
|
||||
|
@ -63,8 +67,8 @@ impl Default for Modifier {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(QuickModifierParser)]
|
||||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
||||
#[derive(QuickModifierParser, Clone)]
|
||||
#[cfg_attr(debug_assertions, derive(Debug, PartialEq))]
|
||||
pub(super) enum QuickModifier {
|
||||
Volume(bool),
|
||||
Octave(bool),
|
||||
|
@ -73,7 +77,7 @@ pub(super) enum QuickModifier {
|
|||
}
|
||||
|
||||
#[derive(Clone, Copy, SlopeModifierParser)]
|
||||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
||||
#[cfg_attr(debug_assertions, derive(Debug, PartialEq))]
|
||||
pub(super) enum SlopeModifier {
|
||||
Note,
|
||||
Volume,
|
||||
|
|
|
@ -9,7 +9,7 @@ use nom::{
|
|||
branch::alt,
|
||||
bytes::complete::{take_till, take_till1},
|
||||
character::complete::{anychar, char, one_of, u16, u8},
|
||||
combinator::{map_opt, map_res, opt, value},
|
||||
combinator::{map_opt, map_res, opt, value, verify},
|
||||
multi::many0,
|
||||
sequence::{delimited, pair, preceded, separated_pair, terminated},
|
||||
Err, IResult, Parser,
|
||||
|
|
204
src/bng/score/utils.rs
Normal file
204
src/bng/score/utils.rs
Normal file
|
@ -0,0 +1,204 @@
|
|||
use super::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
// TODO: replace panics with custom error type
|
||||
fn inflate(mut flat_atoms: Vec<FlatAtom>) -> Vec<Atom> {
|
||||
#[derive(EnumDiscriminants)]
|
||||
#[cfg_attr(debug_assertions, derive(Debug))]
|
||||
enum CurrentStack {
|
||||
Loop(NonZeroU8),
|
||||
Tuple,
|
||||
Slope(SlopeModifier, Instruction),
|
||||
}
|
||||
let mut result = Vec::with_capacity(flat_atoms.len());
|
||||
let mut loop_stack: Vec<Vec<Atom>> = Vec::new();
|
||||
let mut tuple_stack: Vec<Vec<Atom>> = Vec::new();
|
||||
let mut slope_stack: Vec<Vec<Atom>> = Vec::new();
|
||||
let mut stack_history: Vec<CurrentStack> = Vec::new();
|
||||
for mut atom in flat_atoms.into_iter() {
|
||||
// #[cfg(test)]
|
||||
// {
|
||||
// dbg!(&atom);
|
||||
// dbg!(&loop_stack);
|
||||
// dbg!(&tuple_stack);
|
||||
// dbg!(&slope_stack);
|
||||
// dbg!(&stack_history);
|
||||
// }
|
||||
if let Some(stack) = stack_history.last() {
|
||||
match CurrentStackDiscriminants::from(stack) {
|
||||
CurrentStackDiscriminants::Loop => match atom {
|
||||
FlatAtom::Note(n) => loop_stack.last_mut().unwrap().push(Atom::Note(n)),
|
||||
FlatAtom::Rest => loop_stack.last_mut().unwrap().push(Atom::Rest),
|
||||
FlatAtom::StartHere => loop_stack.last_mut().unwrap().push(Atom::StartHere),
|
||||
FlatAtom::Modifier(m) => loop_stack.last_mut().unwrap().push(Atom::Modifier(m)),
|
||||
FlatAtom::QuickModifier(q) => {
|
||||
loop_stack.last_mut().unwrap().push(Atom::QuickModifier(q))
|
||||
}
|
||||
FlatAtom::LoopStarts(n) => {
|
||||
loop_stack.push(Vec::new());
|
||||
stack_history.push(CurrentStack::Loop(n));
|
||||
}
|
||||
FlatAtom::LoopEnds => {
|
||||
let popped = loop_stack.pop().unwrap();
|
||||
if stack_history.len() > 1 {
|
||||
match CurrentStackDiscriminants::from(
|
||||
stack_history.get(stack_history.len() - 2).unwrap(),
|
||||
) {
|
||||
CurrentStackDiscriminants::Loop => &mut loop_stack,
|
||||
CurrentStackDiscriminants::Tuple => &mut tuple_stack,
|
||||
CurrentStackDiscriminants::Slope => &mut slope_stack,
|
||||
}
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.push(Atom::Loop(
|
||||
match stack_history.pop().unwrap() {
|
||||
CurrentStack::Loop(n) => n,
|
||||
_ => unreachable!("this one is proven to be a loop"),
|
||||
},
|
||||
popped,
|
||||
))
|
||||
} else {
|
||||
result.push(Atom::Loop(
|
||||
match stack_history.pop().unwrap() {
|
||||
CurrentStack::Loop(n) => n,
|
||||
_ => unreachable!("this one is proven to be a loop"),
|
||||
},
|
||||
popped,
|
||||
))
|
||||
}
|
||||
}
|
||||
FlatAtom::TupleStarts => {
|
||||
tuple_stack.push(Vec::new());
|
||||
stack_history.push(CurrentStack::Tuple);
|
||||
}
|
||||
FlatAtom::TupleEnds => panic!("unmatched end tuple in a loop"),
|
||||
FlatAtom::SlopeStarts(s, i) => {
|
||||
slope_stack.push(Vec::new());
|
||||
stack_history.push(CurrentStack::Slope(s, i));
|
||||
}
|
||||
FlatAtom::SlopeEnds => panic!("unmatched end slope in a loop"),
|
||||
FlatAtom::Comment => loop_stack.last_mut().unwrap().push(Atom::Comment),
|
||||
},
|
||||
CurrentStackDiscriminants::Tuple => match atom {
|
||||
FlatAtom::Note(n) => tuple_stack.last_mut().unwrap().push(Atom::Note(n)),
|
||||
FlatAtom::Rest => tuple_stack.last_mut().unwrap().push(Atom::Rest),
|
||||
FlatAtom::StartHere => tuple_stack.last_mut().unwrap().push(Atom::StartHere),
|
||||
FlatAtom::Modifier(m) => {
|
||||
tuple_stack.last_mut().unwrap().push(Atom::Modifier(m))
|
||||
}
|
||||
FlatAtom::QuickModifier(q) => {
|
||||
tuple_stack.last_mut().unwrap().push(Atom::QuickModifier(q))
|
||||
}
|
||||
FlatAtom::LoopStarts(n) => {
|
||||
tuple_stack.push(Vec::new());
|
||||
stack_history.push(CurrentStack::Loop(n));
|
||||
}
|
||||
FlatAtom::LoopEnds => panic!("unmatched end loop in a tuple"),
|
||||
FlatAtom::TupleStarts => {
|
||||
tuple_stack.push(Vec::new());
|
||||
stack_history.push(CurrentStack::Tuple);
|
||||
}
|
||||
FlatAtom::TupleEnds => {
|
||||
let popped = tuple_stack.pop().unwrap();
|
||||
if stack_history.len() > 1 {
|
||||
match CurrentStackDiscriminants::from(
|
||||
stack_history.get(stack_history.len() - 2).unwrap(),
|
||||
) {
|
||||
CurrentStackDiscriminants::Loop => &mut loop_stack,
|
||||
CurrentStackDiscriminants::Tuple => &mut tuple_stack,
|
||||
CurrentStackDiscriminants::Slope => &mut slope_stack,
|
||||
}
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.push(Atom::Tuple(popped))
|
||||
} else {
|
||||
result.push(Atom::Tuple(popped))
|
||||
}
|
||||
}
|
||||
FlatAtom::SlopeStarts(s, i) => {
|
||||
slope_stack.push(Vec::new());
|
||||
stack_history.push(CurrentStack::Slope(s, i));
|
||||
}
|
||||
FlatAtom::SlopeEnds => panic!("unmatched end slope in a tuple"),
|
||||
FlatAtom::Comment => tuple_stack.last_mut().unwrap().push(Atom::Comment),
|
||||
},
|
||||
CurrentStackDiscriminants::Slope => match atom {
|
||||
FlatAtom::Note(n) => slope_stack.last_mut().unwrap().push(Atom::Note(n)),
|
||||
FlatAtom::Rest => slope_stack.last_mut().unwrap().push(Atom::Rest),
|
||||
FlatAtom::StartHere => slope_stack.last_mut().unwrap().push(Atom::StartHere),
|
||||
FlatAtom::Modifier(m) => {
|
||||
slope_stack.last_mut().unwrap().push(Atom::Modifier(m))
|
||||
}
|
||||
FlatAtom::QuickModifier(q) => {
|
||||
slope_stack.last_mut().unwrap().push(Atom::QuickModifier(q))
|
||||
}
|
||||
FlatAtom::LoopStarts(n) => {
|
||||
slope_stack.push(Vec::new());
|
||||
stack_history.push(CurrentStack::Loop(n));
|
||||
}
|
||||
FlatAtom::LoopEnds => panic!("unmatched end loop"),
|
||||
FlatAtom::TupleStarts => {
|
||||
tuple_stack.push(Vec::new());
|
||||
stack_history.push(CurrentStack::Tuple);
|
||||
}
|
||||
FlatAtom::TupleEnds => panic!("unmatched end tuple"),
|
||||
FlatAtom::SlopeStarts(s, i) => {
|
||||
slope_stack.push(Vec::new());
|
||||
stack_history.push(CurrentStack::Slope(s, i));
|
||||
}
|
||||
FlatAtom::SlopeEnds => {
|
||||
let popped = slope_stack.pop().unwrap();
|
||||
if stack_history.len() > 1 {
|
||||
match CurrentStackDiscriminants::from(
|
||||
stack_history.get(stack_history.len() - 2).unwrap(),
|
||||
) {
|
||||
CurrentStackDiscriminants::Loop => &mut loop_stack,
|
||||
CurrentStackDiscriminants::Tuple => &mut tuple_stack,
|
||||
CurrentStackDiscriminants::Slope => &mut slope_stack,
|
||||
}
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.push(match stack_history.pop().unwrap() {
|
||||
CurrentStack::Slope(m, i) => Atom::Slope(m, i, popped),
|
||||
_ => unreachable!("this one is proven to be a slope"),
|
||||
})
|
||||
} else {
|
||||
result.push(match stack_history.pop().unwrap() {
|
||||
CurrentStack::Slope(m, i) => Atom::Slope(m, i, popped),
|
||||
_ => unreachable!("this one is proven to be a slope"),
|
||||
})
|
||||
}
|
||||
}
|
||||
FlatAtom::Comment => slope_stack.last_mut().unwrap().push(Atom::Comment),
|
||||
},
|
||||
}
|
||||
} else {
|
||||
match atom {
|
||||
FlatAtom::Note(n) => result.push(Atom::Note(n)),
|
||||
FlatAtom::Rest => result.push(Atom::Rest),
|
||||
FlatAtom::StartHere => result.push(Atom::StartHere),
|
||||
FlatAtom::Modifier(m) => result.push(Atom::Modifier(m)),
|
||||
FlatAtom::QuickModifier(q) => result.push(Atom::QuickModifier(q)),
|
||||
FlatAtom::LoopStarts(n) => {
|
||||
loop_stack.push(Vec::new());
|
||||
stack_history.push(CurrentStack::Loop(n));
|
||||
}
|
||||
FlatAtom::LoopEnds => panic!("unmatched end loop"),
|
||||
FlatAtom::TupleStarts => {
|
||||
tuple_stack.push(Vec::new());
|
||||
stack_history.push(CurrentStack::Tuple);
|
||||
}
|
||||
FlatAtom::TupleEnds => panic!("unmatched end tuple"),
|
||||
FlatAtom::SlopeStarts(s, i) => {
|
||||
slope_stack.push(Vec::new());
|
||||
stack_history.push(CurrentStack::Slope(s, i));
|
||||
}
|
||||
FlatAtom::SlopeEnds => panic!("unmatched end slope"),
|
||||
FlatAtom::Comment => result.push(Atom::Comment),
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
86
src/bng/score/utils/tests.rs
Normal file
86
src/bng/score/utils/tests.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
#[cfg(test)]
|
||||
mod inflate {
|
||||
use fasteval::Compiler;
|
||||
use lex::UP;
|
||||
|
||||
use super::{super::*, inflate};
|
||||
|
||||
const FASTEVAL_INSTRUCTION: &str = "1-cos((PI*x)/2)";
|
||||
|
||||
#[test]
|
||||
fn inflate_flat() {
|
||||
assert_eq!(
|
||||
vec![
|
||||
Atom::Note(2),
|
||||
Atom::Rest,
|
||||
Atom::StartHere,
|
||||
Atom::Modifier(Modifier::Volume(2)),
|
||||
Atom::QuickModifier(QuickModifier::Volume(UP)),
|
||||
Atom::Comment
|
||||
],
|
||||
inflate(vec![
|
||||
FlatAtom::Note(2),
|
||||
FlatAtom::Rest,
|
||||
FlatAtom::StartHere,
|
||||
FlatAtom::Modifier(Modifier::Volume(2)),
|
||||
FlatAtom::QuickModifier(QuickModifier::Volume(UP)),
|
||||
FlatAtom::Comment
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inflate_loop_l1() {
|
||||
assert_eq!(
|
||||
vec![Atom::Loop(
|
||||
unsafe { NonZeroU8::new_unchecked(3) },
|
||||
vec![Atom::Note(2), Atom::Note(3)]
|
||||
)],
|
||||
inflate(vec![
|
||||
FlatAtom::LoopStarts(unsafe { NonZeroU8::new_unchecked(3) }),
|
||||
FlatAtom::Note(2),
|
||||
FlatAtom::Note(3),
|
||||
FlatAtom::LoopEnds
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inflate_tuple_l1() {
|
||||
assert_eq!(
|
||||
vec![Atom::Tuple(vec![Atom::Note(2), Atom::Note(3)])],
|
||||
inflate(vec![
|
||||
FlatAtom::TupleStarts,
|
||||
FlatAtom::Note(2),
|
||||
FlatAtom::Note(3),
|
||||
FlatAtom::TupleEnds
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inflate_slope_l1() {
|
||||
let instruction = || {
|
||||
let parser = fasteval::Parser::new();
|
||||
let mut slab = fasteval::Slab::new();
|
||||
parser
|
||||
.parse(FASTEVAL_INSTRUCTION, &mut slab.ps)
|
||||
.unwrap()
|
||||
.from(&slab.ps)
|
||||
.compile(&slab.ps, &mut slab.cs)
|
||||
};
|
||||
assert_eq!(
|
||||
vec![Atom::Slope(
|
||||
SlopeModifier::Note,
|
||||
instruction(),
|
||||
vec![Atom::Note(2), Atom::Note(3)]
|
||||
)],
|
||||
inflate(vec![
|
||||
FlatAtom::SlopeStarts(SlopeModifier::Note, instruction()),
|
||||
FlatAtom::Note(2),
|
||||
FlatAtom::Note(3),
|
||||
FlatAtom::SlopeEnds
|
||||
])
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue