diff --git a/poc/poc.yml b/poc/poc.yml index f4c5e2f..92c4b32 100644 --- a/poc/poc.yml +++ b/poc/poc.yml @@ -37,17 +37,17 @@ channels: octave: >< comment?: ;, start here: ':' - slope: {MODIFIER EXPR, score} + slope: {MODIFIER EXPR score} note modifier prefix: n volume modifier prefix: v octave modifier prefix: o length modifier prefix: l tempo modifier prefix: t loop: () - loop with count: (COUNT, score) + loop with count: (COUNT score) tuple: [] modifier: ! volume modifier prefix: v octave modifier prefix: o length modifier prefix: l - tempo modifier prefix: t, + tempo modifier prefix: t diff --git a/src/bng/score.rs b/src/bng/score.rs index 515b814..38b7609 100644 --- a/src/bng/score.rs +++ b/src/bng/score.rs @@ -30,7 +30,7 @@ pub use de::*; use super::Expression as Instruction; -#[derive(Deref, From, Default)] +#[derive(Deref, From, Default, Clone)] #[cfg_attr(debug_assertions, derive(Serialize, Debug, PartialEq))] pub struct Atoms(Vec); @@ -50,7 +50,12 @@ pub enum Atom { impl Clone for Atom { fn clone(&self) -> Self { match self { + Self::Note(n) => Self::Note(*n), Self::Rest => Self::Rest, + Self::StartHere => Self::StartHere, + Self::Modifier(m) => Self::Modifier(m.clone()), + Self::QuickModifier(q) => Self::QuickModifier(q.clone()), + Self::Comment => Self::Comment, _ => unimplemented!("variant can't be cloned right now"), } } diff --git a/src/bng/score/lex/lexer.rs b/src/bng/score/lex/lexer.rs index aaba0da..ef37ccc 100644 --- a/src/bng/score/lex/lexer.rs +++ b/src/bng/score/lex/lexer.rs @@ -5,13 +5,14 @@ use std::{ }; use clap::builder::TypedValueParser; +use const_format::concatcp; use fasteval::Compiler; use lazy_static::lazy_static; use nom::{ branch::alt, - bytes::complete::{take_till, take_till1}, + bytes::complete::{is_not, tag, take_till, take_till1}, character::complete::{anychar, char, one_of, space1, u16, u8}, - combinator::{all_consuming, map_opt, map_res, opt, value, verify}, + combinator::{all_consuming, map_opt, map_res, not, opt, value, verify}, error::{context, ContextError, ErrorKind, FromExternalError, ParseError}, multi::{many0, many1}, sequence::{delimited, pair, preceded, separated_pair, terminated}, @@ -24,8 +25,8 @@ use super::{ super::super::Expression as Instruction, Atom, Modifier, QuickModifier, SlopeModifier, }; -// #[cfg(test)] -// mod tests; +#[cfg(test)] +mod tests; lazy_static! { static ref NOTES: Mutex> = Mutex::new(None); @@ -37,11 +38,11 @@ pub fn set_notes( Ok(NOTES.lock()?.replace(notes.to_string())) } -fn maybe_yml_str_space<'a, E>() -> impl Parser<&'a str, Vec, E> +fn maybe_yml_str_space<'a, E>(i: &'a str) -> IResult<&'a str, Vec, E> where E: nom::error::ParseError<&'a str> + ContextError<&'a str>, { - context("yml white space", many0(one_of(" \t\r\n"))) + context("yml white space", many0(one_of(" \t\r\n"))).parse(i) } pub fn root<'a, E>(i: &'a str) -> IResult<&'a str, Atoms, E> @@ -52,8 +53,8 @@ where + FromExternalError<&'a str, E>, { terminated( - many1(preceded(maybe_yml_str_space(), atom)), - maybe_yml_str_space(), + many1(preceded(maybe_yml_str_space, atom)), + maybe_yml_str_space, ) .map(Atoms) .parse(i) @@ -166,9 +167,9 @@ where use super::super::slope_modifier; context( "slope_starts", - separated_pair( - preceded( - char(Atom::SLOPE.0), + delimited( + char(Atom::SLOPE.0), + separated_pair( separated_pair( slope_modifier, char(' '), @@ -177,9 +178,10 @@ where .map_err(|_| E::from_error_kind(s, ErrorKind::Verify)) }), ), + char(','), + root, ), - char(','), - root, + char(Atom::SLOPE.1), ), ) .map(|((sm, i), v)| Atom::Slope(sm, i, v)) @@ -196,7 +198,10 @@ where Atom::Comment, delimited( char(Atom::COMMENT.0), - take_till(|c| c == Atom::COMMENT.1), + many0(alt(( + value((), comment), + value((), is_not(concatcp!(Atom::COMMENT.0, Atom::COMMENT.1))), + ))), char(Atom::COMMENT.1), ), ), diff --git a/src/bng/score/lex/lexer/tests.rs b/src/bng/score/lex/lexer/tests.rs index 3c3cc58..55d7675 100644 --- a/src/bng/score/lex/lexer/tests.rs +++ b/src/bng/score/lex/lexer/tests.rs @@ -1,6 +1,6 @@ use const_format::concatcp; use nom::{ - error::{Error, ErrorKind}, + error::{Error, ErrorKind, VerboseError}, Err, }; @@ -8,12 +8,21 @@ use flat_atom::{ FASTEVAL_INSTRUCTION as FLATATOM_FASTEVAL_INSTRUCTION, SAMPLE_STR as FLATATOM_SAMPLE_STRING, }; +fn set_notes() { + super::set_notes("abcdefg").unwrap(); +} + +type NomError<'a> = VerboseError<&'a str>; + mod flat_atom { use std::num::NonZeroU8; use fasteval::Compiler; + use lazy_static::lazy_static; use nom::Parser; + use crate::bng::score::Atoms; + use super::super::{ super::super::super::Expression as Instruction, super::UP, atom, Atom, Modifier, QuickModifier, SlopeModifier, @@ -26,16 +35,27 @@ mod flat_atom { Atom::TUPLE.1, "ed", Atom::COMMENT.0, - "hello" + "hello", + Atom::COMMENT.1 ); + pub(super) fn sample_str_expr() -> Atoms { + Atoms(vec![ + Atom::Tuple(Atoms(vec![Atom::Note(0), Atom::Note(2), Atom::Note(2)])), + Atom::Note(4), + Atom::Note(3), + Atom::Comment, + ]) + } + pub(super) const FASTEVAL_INSTRUCTION: &str = "1-cos((PI*x)/2)"; #[test] fn note() { + set_notes(); assert_eq!( Ok((SAMPLE_STR, Atom::Note(2))), - atom::>("abcdefg").parse(concatcp!('c', SAMPLE_STR)) + atom::.parse(concatcp!('c', SAMPLE_STR)) ) } @@ -43,7 +63,7 @@ mod flat_atom { fn rest() { assert_eq!( Ok((SAMPLE_STR, Atom::Rest)), - atom::>("abcdefg").parse(concatcp!(Atom::REST, SAMPLE_STR)) + atom::.parse(concatcp!(Atom::REST, SAMPLE_STR)) ) } @@ -51,7 +71,7 @@ mod flat_atom { fn start_here() { assert_eq!( Ok((SAMPLE_STR, Atom::StartHere)), - atom::>("abcdefg").parse(concatcp!(Atom::START_HERE, SAMPLE_STR)) + atom::.parse(concatcp!(Atom::START_HERE, SAMPLE_STR)) ) } @@ -62,12 +82,7 @@ mod flat_atom { SAMPLE_STR, Atom::Modifier(Modifier::Length(unsafe { NonZeroU8::new_unchecked(2) })) )), - atom::>("abcdefg").parse(concatcp!( - Atom::MODIFIER, - Modifier::LENGTH, - 2u8, - SAMPLE_STR - )) + atom::.parse(concatcp!(Atom::MODIFIER, Modifier::LENGTH, 2u8, SAMPLE_STR)) ) } @@ -75,90 +90,92 @@ mod flat_atom { fn quick_modifier() { assert_eq!( Ok((SAMPLE_STR, Atom::QuickModifier(QuickModifier::Length(UP)))), - atom::>("abcdefg").parse(concatcp!(QuickModifier::LENGTH.0, SAMPLE_STR)) + atom::.parse(concatcp!(QuickModifier::LENGTH.0, SAMPLE_STR)) ) } #[test] - fn loop_starts() { + fn r#loop() { + set_notes(); assert_eq!( Ok(( SAMPLE_STR, - Atom::LoopStarts(unsafe { NonZeroU8::new_unchecked(3) }) + Atom::Loop(unsafe { NonZeroU8::new_unchecked(3) }, sample_str_expr()) )), - atom::>("abcdefg").parse(concatcp!(Atom::LOOP.0, 3u8, SAMPLE_STR)) + atom::.parse(concatcp!( + Atom::LOOP.0, + 3u8, + SAMPLE_STR, + Atom::LOOP.1, + SAMPLE_STR + )) ); assert_eq!( Ok(( SAMPLE_STR, - Atom::LoopStarts(unsafe { NonZeroU8::new_unchecked(2) }) + Atom::Loop(unsafe { NonZeroU8::new_unchecked(2) }, sample_str_expr()) )), - atom::>("abcdefg").parse(concatcp!(Atom::LOOP.0, SAMPLE_STR)) + atom::.parse(concatcp!( + Atom::LOOP.0, + SAMPLE_STR, + Atom::LOOP.1, + SAMPLE_STR + )) ); assert_eq!( Err(nom::Err::Error(Error::new( concatcp!(Atom::LOOP.0, 0u8, SAMPLE_STR), ErrorKind::Char ))), - atom("abcdefg").parse(concatcp!(Atom::LOOP.0, 0u8, SAMPLE_STR)) + atom.parse(concatcp!(Atom::LOOP.0, 0u8, SAMPLE_STR)) ) } #[test] - fn loop_ends() { + fn tuple() { + set_notes(); assert_eq!( - Ok((SAMPLE_STR, Atom::LoopEnds)), - atom::>("abcdefg").parse(concatcp!(Atom::LOOP.1, SAMPLE_STR)) - ) - } - - #[test] - fn tuple_starts() { - assert_eq!( - Ok((SAMPLE_STR, Atom::TupleStarts)), - atom::>("abcdefg").parse(concatcp!(Atom::TUPLE.0, SAMPLE_STR)) - ) - } - - #[test] - fn tuple_ends() { - assert_eq!( - Ok((SAMPLE_STR, Atom::TupleEnds)), - atom::>("abcdefg").parse(concatcp!(Atom::TUPLE.1, SAMPLE_STR)) - ) - } - - #[test] - fn slope_starts() { - assert_eq!( - Ok(( + Ok((SAMPLE_STR, Atom::Tuple(sample_str_expr()))), + atom::.parse(concatcp!( + Atom::TUPLE.0, SAMPLE_STR, - Atom::SlopeStarts(SlopeModifier::Note, FASTEVAL_INSTRUCTION.parse().unwrap()) - )), - atom::>("abcdefg").parse(concatcp!( - Atom::SLOPE.0, - SlopeModifier::NOTE, - ' ', - FASTEVAL_INSTRUCTION, - ',', + Atom::TUPLE.1, SAMPLE_STR )) ) } #[test] - fn slope_ends() { + fn slope() { + set_notes(); assert_eq!( - Ok((SAMPLE_STR, Atom::SlopeEnds)), - atom::>("abcdefg").parse(concatcp!(Atom::SLOPE.1, SAMPLE_STR)) + Ok(( + SAMPLE_STR, + Atom::Slope( + SlopeModifier::Note, + FASTEVAL_INSTRUCTION.parse().unwrap(), + sample_str_expr() + ) + )), + atom::.parse(concatcp!( + Atom::SLOPE.0, + SlopeModifier::NOTE, + ' ', + FASTEVAL_INSTRUCTION, + ',', + SAMPLE_STR, + Atom::SLOPE.1, + SAMPLE_STR + )) ) } #[test] fn comment() { + set_notes(); assert_eq!( Ok((SAMPLE_STR, Atom::Comment)), - atom::>("abcdefg").parse(concatcp!( + atom::.parse(concatcp!( Atom::COMMENT.0, "hi I'm a little pony", SAMPLE_STR, @@ -167,6 +184,15 @@ mod flat_atom { )) ) } + + #[test] + fn nested_comments() { + set_notes(); + assert_eq!( + Ok(("", Atom::Comment)), + atom::.parse(";d;;dd,ef,,") + ) + } } mod modifier { @@ -175,14 +201,14 @@ mod modifier { use const_format::concatcp; use nom::error::{Error, ErrorKind, VerboseError}; - use super::FLATATOM_SAMPLE_STRING as SAMPLE_STR; + use super::{NomError, FLATATOM_SAMPLE_STRING as SAMPLE_STR}; use crate::bng::score::{modifier, Modifier}; #[test] fn volume() { assert_eq!( Ok((SAMPLE_STR, Modifier::Volume(2))), - modifier::>(concatcp!(Modifier::VOLUME, 2u8, SAMPLE_STR)) + modifier::(concatcp!(Modifier::VOLUME, 2u8, SAMPLE_STR)) ); assert_eq!( Err(nom::Err::Error(Error::new( @@ -197,7 +223,7 @@ mod modifier { fn octave() { assert_eq!( Ok((SAMPLE_STR, Modifier::Octave(2))), - modifier::>(concatcp!(Modifier::OCTAVE, 2u8, SAMPLE_STR)) + modifier::(concatcp!(Modifier::OCTAVE, 2u8, SAMPLE_STR)) ); assert_eq!( Err(nom::Err::Error(Error::new( @@ -215,7 +241,7 @@ mod modifier { SAMPLE_STR, Modifier::Length(unsafe { NonZeroU8::new_unchecked(2) }) )), - modifier::>(concatcp!(Modifier::LENGTH, 2u8, SAMPLE_STR)) + modifier::(concatcp!(Modifier::LENGTH, 2u8, SAMPLE_STR)) ); assert_eq!( Err(nom::Err::Error(Error::new( @@ -240,7 +266,7 @@ mod modifier { SAMPLE_STR, Modifier::Tempo(unsafe { NonZeroU16::new_unchecked(2) }) )), - modifier::>(concatcp!(Modifier::TEMPO, 2u8, SAMPLE_STR)) + modifier::(concatcp!(Modifier::TEMPO, 2u8, SAMPLE_STR)) ); assert_eq!( Err(nom::Err::Error(Error::new( @@ -256,7 +282,7 @@ mod quick_modifier { use const_format::concatcp; use nom::error::VerboseError; - use super::FLATATOM_SAMPLE_STRING as SAMPLE_STR; + use super::{NomError, FLATATOM_SAMPLE_STRING as SAMPLE_STR}; use crate::bng::score::{ lex::{DOWN, OFF, ON, UP}, quick_modifier, QuickModifier, @@ -266,11 +292,11 @@ mod quick_modifier { fn volume() { assert_eq!( Ok((SAMPLE_STR, QuickModifier::Volume(UP))), - quick_modifier::>(concatcp!(QuickModifier::VOLUME.0, SAMPLE_STR)) + quick_modifier::(concatcp!(QuickModifier::VOLUME.0, SAMPLE_STR)) ); assert_eq!( Ok((SAMPLE_STR, QuickModifier::Volume(DOWN))), - quick_modifier::>(concatcp!(QuickModifier::VOLUME.1, SAMPLE_STR)) + quick_modifier::(concatcp!(QuickModifier::VOLUME.1, SAMPLE_STR)) ); } @@ -278,11 +304,11 @@ mod quick_modifier { fn octave() { assert_eq!( Ok((SAMPLE_STR, QuickModifier::Octave(UP))), - quick_modifier::>(concatcp!(QuickModifier::OCTAVE.0, SAMPLE_STR)) + quick_modifier::(concatcp!(QuickModifier::OCTAVE.0, SAMPLE_STR)) ); assert_eq!( Ok((SAMPLE_STR, QuickModifier::Octave(DOWN))), - quick_modifier::>(concatcp!(QuickModifier::OCTAVE.1, SAMPLE_STR)) + quick_modifier::(concatcp!(QuickModifier::OCTAVE.1, SAMPLE_STR)) ); } @@ -290,11 +316,11 @@ mod quick_modifier { fn length() { assert_eq!( Ok((SAMPLE_STR, QuickModifier::Length(UP))), - quick_modifier::>(concatcp!(QuickModifier::LENGTH.0, SAMPLE_STR)) + quick_modifier::(concatcp!(QuickModifier::LENGTH.0, SAMPLE_STR)) ); assert_eq!( Ok((SAMPLE_STR, QuickModifier::Length(DOWN))), - quick_modifier::>(concatcp!(QuickModifier::LENGTH.1, SAMPLE_STR)) + quick_modifier::(concatcp!(QuickModifier::LENGTH.1, SAMPLE_STR)) ); } @@ -302,11 +328,11 @@ mod quick_modifier { fn pizz() { assert_eq!( Ok((SAMPLE_STR, QuickModifier::Pizz(ON))), - quick_modifier::>(concatcp!(QuickModifier::PIZZ.0, SAMPLE_STR)) + quick_modifier::(concatcp!(QuickModifier::PIZZ.0, SAMPLE_STR)) ); assert_eq!( Ok((SAMPLE_STR, QuickModifier::Pizz(OFF))), - quick_modifier::>(concatcp!(QuickModifier::PIZZ.1, SAMPLE_STR)) + quick_modifier::(concatcp!(QuickModifier::PIZZ.1, SAMPLE_STR)) ); } } @@ -316,7 +342,7 @@ mod slope_modifier { use const_format::concatcp; use nom::error::VerboseError; - use super::FLATATOM_FASTEVAL_INSTRUCTION as INSTRUCTION; + use super::{NomError, FLATATOM_FASTEVAL_INSTRUCTION as INSTRUCTION}; use crate::bng::score::{slope_modifier, Atom, SlopeModifier}; const SAMPLE_STR: &str = concatcp!(' ', INSTRUCTION, Atom::SLOPE.1); @@ -325,7 +351,7 @@ mod slope_modifier { fn note() { assert_eq!( Ok((SAMPLE_STR, SlopeModifier::Note)), - slope_modifier::>(concatcp!(SlopeModifier::NOTE, SAMPLE_STR)) + slope_modifier::(concatcp!(SlopeModifier::NOTE, SAMPLE_STR)) ) } @@ -333,7 +359,7 @@ mod slope_modifier { fn volume() { assert_eq!( Ok((SAMPLE_STR, SlopeModifier::Volume)), - slope_modifier::>(concatcp!(SlopeModifier::VOLUME, SAMPLE_STR)) + slope_modifier::(concatcp!(SlopeModifier::VOLUME, SAMPLE_STR)) ) } @@ -341,7 +367,7 @@ mod slope_modifier { fn octave() { assert_eq!( Ok((SAMPLE_STR, SlopeModifier::Octave)), - slope_modifier::>(concatcp!(SlopeModifier::OCTAVE, SAMPLE_STR)) + slope_modifier::(concatcp!(SlopeModifier::OCTAVE, SAMPLE_STR)) ) } @@ -349,7 +375,7 @@ mod slope_modifier { fn length() { assert_eq!( Ok((SAMPLE_STR, SlopeModifier::Length)), - slope_modifier::>(concatcp!(SlopeModifier::LENGTH, SAMPLE_STR)) + slope_modifier::(concatcp!(SlopeModifier::LENGTH, SAMPLE_STR)) ) } @@ -357,7 +383,7 @@ mod slope_modifier { fn tempo() { assert_eq!( Ok((SAMPLE_STR, SlopeModifier::Tempo)), - slope_modifier::>(concatcp!(SlopeModifier::TEMPO, SAMPLE_STR)) + slope_modifier::(concatcp!(SlopeModifier::TEMPO, SAMPLE_STR)) ) } }