diff --git a/src/compiler.rs b/src/compiler.rs index 3eea276..aa00c99 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -6,7 +6,7 @@ use fasteval::{Compiler, Instruction, Slab}; #[derive(From, AsRef)] #[cfg_attr(test, derive(Debug))] -pub struct TokenVec<'a>(Vec>); +pub struct TokenVec<'a>(pub(crate) Vec>); #[cfg(not(test))] pub trait Token { @@ -63,7 +63,7 @@ pub enum LoopCount { impl Default for LoopCount { fn default() -> Self { - LoopCount::Litteral(1) + LoopCount::Litteral(2) } } diff --git a/src/parser.rs b/src/parser.rs index 4994d2e..d34b6e7 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -26,24 +26,26 @@ use crate::compiler::{ }; #[derive(Builder)] -pub struct Parser<'i, 'n, 's, 'v, N: AsRef + Clone> { - input: &'i str, +pub struct Parser<'n, 's, 'v, N: AsRef + Clone> { notes: &'n [N], slopes: &'s HashMap, variables: &'v [char], } -impl<'i, 's, N: AsRef + Clone> Parser<'i, '_, 's, '_, N> { - pub fn parse_all(&self) -> Result, Error>> { +impl<'a, 's, N: AsRef + Clone> Parser<'_, 's, '_, N> { + pub fn parse_all( + &self, + input: &'a str, + ) -> Result, Error>> { token_parser(self) - .parse_complete(LocatedSpan::new(self.input)) + .parse_complete(LocatedSpan::new(input)) .finish() .map(|(_, o)| o) } } fn token_parser<'a, 's, I, N>( - parser: &Parser<'_, '_, 's, '_, N>, + parser: &Parser<'_, 's, '_, N>, ) -> impl NomParser, Error = nom::error::Error> where I: Input + AsRef + for<'z> nom::Compare<&'z str> + Copy, @@ -139,9 +141,7 @@ impl VariableChange { } impl<'s> Loop<'s> { - fn parser<'i, 'n, 'v, I, N>( - parser: &Parser<'i, 'n, 's, 'v, N>, - ) -> impl Fn(I) -> IResult + fn parser<'n, 'v, I, N>(parser: &Parser<'n, 's, 'v, N>) -> impl Fn(I) -> IResult where I: Input + AsRef + for<'z> nom::Compare<&'z str> + Copy, ::Item: AsChar, @@ -165,9 +165,7 @@ impl<'s> Loop<'s> { } impl<'s> Tuplet<'s> { - fn parser<'i, 'n, 'v, I, N>( - parser: &Parser<'i, 'n, 's, 'v, N>, - ) -> impl Fn(I) -> IResult + fn parser<'n, 'v, I, N>(parser: &Parser<'n, 's, 'v, N>) -> impl Fn(I) -> IResult where I: Input + for<'z> Compare<&'z str> + AsRef + Copy, ::Item: AsChar, @@ -183,9 +181,7 @@ impl<'s> Tuplet<'s> { } impl<'s> Slope<'s> { - fn parser<'i, 'n, 'v, I, N>( - parser: &Parser<'i, 'n, 's, 'v, N>, - ) -> impl Fn(I) -> IResult + fn parser<'n, 'v, I, N>(parser: &Parser<'n, 's, 'v, N>) -> impl Fn(I) -> IResult where I: Input + for<'z> Compare<&'z str> + AsRef + Copy, ::Item: AsChar, @@ -257,13 +253,19 @@ where #[cfg(test)] mod tests { - use nom::Parser; + use std::collections::HashMap; + + use nom::{IResult, Parser}; use crate::{ - compiler::{Marker, Note, Silence, VariableChange}, + compiler::{ + Loop, LoopCount, Marker, Note, Silence, Slope, Token, TokenVec, VariableChange, + }, parser::expression_parser, }; + use super::ParserBuilder; + #[test] fn expression_parser_test() { let parser = expression_parser(&['x']); @@ -419,4 +421,66 @@ mod tests { ); } } + + #[test] + fn r#loop() { + let slopes = Default::default(); + fn parser_builder<'s>( + slopes: &'s HashMap, + ) -> impl Fn(&str) -> IResult<&str, Loop<'s>> { + move |input: &str| { + Loop::parser( + &ParserBuilder::create_empty() + .notes(&["do", "ré", "mi"]) + .slopes(slopes) + .variables(&['n']) + .build() + .unwrap(), + ) + .parse(input) + } + } + let parser = parser_builder(&slopes); + let mut working_cases = vec![ + ( + "(.%)", + ( + "", + Loop( + LoopCount::Litteral(2), + TokenVec(vec![Box::new(Silence), Box::new(Marker)]), + ), + ), + ), + ("()", ("", Loop(LoopCount::Litteral(2), TokenVec(vec![])))), + ("(4)", ("", Loop(LoopCount::Litteral(4), TokenVec(vec![])))), + ( + "(n)", + ("", Loop(LoopCount::Variable('n'), TokenVec(vec![]))), + ), + ( + "(ndo)", + ( + "", + Loop(LoopCount::Variable('n'), TokenVec(vec![Box::new(Note(0))])), + ), + ), + ]; + let mut not_working_cases = vec!["", "(", ")", "(2", "(p)"]; + for (test, expected) in working_cases.drain(..) { + let output = parser(test); + if let Ok(result) = output { + assert_eq!(expected, result, "case \"{test}\""); + } else { + panic!("result of \"{test}\" was not Ok: {output:?}"); + } + } + for test in not_working_cases.drain(..) { + let output = parser(test); + assert!( + output.is_err(), + "result of \"{test}\" was not Err: {output:?}" + ); + } + } }