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 std::num::{NonZeroU16, NonZeroU8};
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
use bng_macros::{QuickModifierParser, SlopeModifierParser};
|
use bng_macros::{QuickModifierParser, SlopeModifierParser};
|
||||||
use fasteval::Instruction;
|
use fasteval::Instruction;
|
||||||
|
use strum::EnumDiscriminants;
|
||||||
|
|
||||||
mod lex;
|
mod lex;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
#[cfg_attr(debug_assertions, derive(Debug, PartialEq))]
|
||||||
pub(super) enum Atom {
|
pub(super) enum Atom {
|
||||||
Note(char),
|
Note(u8),
|
||||||
Rest,
|
Rest,
|
||||||
StartHere,
|
StartHere,
|
||||||
Modifier(Modifier),
|
Modifier(Modifier),
|
||||||
|
@ -18,7 +21,7 @@ pub(super) enum Atom {
|
||||||
Comment,
|
Comment,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
#[cfg_attr(debug_assertions, derive(Debug, PartialEq))]
|
||||||
pub(super) enum FlatAtom {
|
pub(super) enum FlatAtom {
|
||||||
Note(u8),
|
Note(u8),
|
||||||
Rest,
|
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 {
|
pub(super) enum Modifier {
|
||||||
Volume(u8),
|
Volume(u8),
|
||||||
Octave(u8),
|
Octave(u8),
|
||||||
|
@ -63,8 +67,8 @@ impl Default for Modifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(QuickModifierParser)]
|
#[derive(QuickModifierParser, Clone)]
|
||||||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
#[cfg_attr(debug_assertions, derive(Debug, PartialEq))]
|
||||||
pub(super) enum QuickModifier {
|
pub(super) enum QuickModifier {
|
||||||
Volume(bool),
|
Volume(bool),
|
||||||
Octave(bool),
|
Octave(bool),
|
||||||
|
@ -73,7 +77,7 @@ pub(super) enum QuickModifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, SlopeModifierParser)]
|
#[derive(Clone, Copy, SlopeModifierParser)]
|
||||||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
#[cfg_attr(debug_assertions, derive(Debug, PartialEq))]
|
||||||
pub(super) enum SlopeModifier {
|
pub(super) enum SlopeModifier {
|
||||||
Note,
|
Note,
|
||||||
Volume,
|
Volume,
|
||||||
|
|
|
@ -9,7 +9,7 @@ use nom::{
|
||||||
branch::alt,
|
branch::alt,
|
||||||
bytes::complete::{take_till, take_till1},
|
bytes::complete::{take_till, take_till1},
|
||||||
character::complete::{anychar, char, one_of, u16, u8},
|
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,
|
multi::many0,
|
||||||
sequence::{delimited, pair, preceded, separated_pair, terminated},
|
sequence::{delimited, pair, preceded, separated_pair, terminated},
|
||||||
Err, IResult, Parser,
|
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