inflate FlatAtoms to Atoms (garbage code) with tests (two fails)

This commit is contained in:
Ponj 2024-10-13 23:43:20 -04:00
parent 892914256d
commit aa512e6206
Signed by: p6nj
GPG key ID: 6FED68D87C479A59
4 changed files with 288 additions and 3 deletions

View file

@ -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))]
pub(super) enum Atom {
Note(char),
Note(u8),
Rest,
StartHere,
Modifier(Modifier),
@ -49,6 +52,7 @@ impl Clone for FlatAtom {
}
}
#[derive(Clone)]
#[cfg_attr(test, derive(Debug, PartialEq))]
pub(super) enum Modifier {
Volume(u8),
@ -63,7 +67,7 @@ impl Default for Modifier {
}
}
#[derive(QuickModifierParser)]
#[derive(QuickModifierParser, Clone)]
#[cfg_attr(test, derive(Debug, PartialEq))]
pub(super) enum QuickModifier {
Volume(bool),

View file

@ -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,

195
src/bng/score/utils.rs Normal file
View file

@ -0,0 +1,195 @@
use super::*;
#[cfg(test)]
mod tests;
// TODO: replace panics with custom error type
fn inflate(mut flat_atoms: Vec<FlatAtom>) -> Vec<Atom> {
#[derive(EnumDiscriminants)]
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() {
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 = 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::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 = 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(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
}

View 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
])
)
}
}