From 22771168d2fbb584bc816bb12d53c67e795f46a1 Mon Sep 17 00:00:00 2001 From: Breval Ferrari Date: Wed, 21 May 2025 12:22:58 +0200 Subject: [PATCH] note test, make Note parser generic over notes and match longer strings first --- src/parser.rs | 109 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 88 insertions(+), 21 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 653b43f..21353c9 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -26,14 +26,14 @@ use crate::compiler::{ }; #[derive(Builder)] -pub struct Parser<'i, 'n, 's, 'v> { +pub struct Parser<'i, 'n, 's, 'v, N: AsRef + Clone> { input: &'i str, - notes: &'n [String], + notes: &'n [N], slopes: &'s HashMap, variables: &'v [char], } -impl<'i, 's> Parser<'i, '_, 's, '_> { +impl<'i, 's, N: AsRef + Clone> Parser<'i, '_, 's, '_, N> { pub fn parse_all(&self) -> Result, Error>> { token_parser(self) .parse_complete(LocatedSpan::new(self.input)) @@ -42,13 +42,14 @@ impl<'i, 's> Parser<'i, '_, 's, '_> { } } -fn token_parser<'a, 's, I>( - parser: &Parser<'_, '_, 's, '_>, +fn token_parser<'a, 's, I, N>( + parser: &Parser<'_, '_, 's, '_, N>, ) -> impl NomParser, Error = nom::error::Error> where I: Input + AsRef + for<'z> nom::Compare<&'z str>, ::Item: AsChar, ::Item: PartialEq, + N: AsRef + Clone, { let space_or_comment = || { value( @@ -97,22 +98,26 @@ impl Marker { } impl Note { - fn parser<'n, I>( - notes: &'n [String], + fn parser<'n, N, I>( + notes: &'n [N], ) -> impl NomParser> where + N: AsRef + Clone, I: Input + for<'z> Compare<&'z str>, { |input: I| { - let mut parsers: Vec IResult>> = notes - .iter() - .enumerate() - .map(|(i, t)| { - Box::new(move |input: I| { - value(Note(i as u8), tag(t.clone().as_str())).parse(input) - }) as Box IResult> - }) - .collect(); + let mut parsers: Vec IResult>> = { + let mut sorted = notes.iter().enumerate().collect::>(); + sorted.sort_by_key(|(_, n)| n.as_ref().len()); + sorted + } + .drain(..) + .rev() + .map(|(i, t)| { + Box::new(move |input: I| value(Note(i as u8), tag(t.clone().as_ref())).parse(input)) + as Box IResult> + }) + .collect(); alt(parsers.as_mut_slice()).parse(input) } } @@ -134,11 +139,14 @@ impl VariableChange { } impl<'s> Loop<'s> { - fn parser<'i, 'n, 'v, I>(parser: &Parser<'i, 'n, 's, 'v>) -> impl Fn(I) -> IResult + fn parser<'i, 'n, 'v, I, N>( + parser: &Parser<'i, 'n, 's, 'v, N>, + ) -> impl Fn(I) -> IResult where I: Input + AsRef + for<'z> nom::Compare<&'z str>, ::Item: AsChar, ::Item: PartialEq, + N: AsRef + Clone, { |input| { delimited( @@ -157,11 +165,14 @@ impl<'s> Loop<'s> { } impl<'s> Tuplet<'s> { - fn parser<'i, 'n, 'v, I>(parser: &Parser<'i, 'n, 's, 'v>) -> impl Fn(I) -> IResult + fn parser<'i, 'n, 'v, I, N>( + parser: &Parser<'i, 'n, 's, 'v, N>, + ) -> impl Fn(I) -> IResult where I: Input + for<'z> Compare<&'z str> + AsRef, ::Item: AsChar, ::Item: PartialEq, + N: AsRef + Clone, { |input| { delimited(char('['), token_parser(parser), char(']')) @@ -172,11 +183,14 @@ impl<'s> Tuplet<'s> { } impl<'s> Slope<'s> { - fn parser<'i, 'n, 'v, I>(parser: &Parser<'i, 'n, 's, 'v>) -> impl Fn(I) -> IResult + fn parser<'i, 'n, 'v, I, N>( + parser: &Parser<'i, 'n, 's, 'v, N>, + ) -> impl Fn(I) -> IResult where I: Input + for<'z> Compare<&'z str> + AsRef, ::Item: AsChar, ::Item: PartialEq, + N: AsRef + Clone, { |input| { let iter: std::collections::hash_map::Iter<'s, String, VariableChange> = @@ -245,9 +259,11 @@ fn expression_parser<'a, I: Input + AsRef, C: Into + Ord + Display>( #[cfg(test)] mod tests { use nom::Parser; - use nom_locate::LocatedSpan; - use crate::{compiler::Silence, parser::expression_parser}; + use crate::{ + compiler::{Marker, Note, Silence}, + parser::expression_parser, + }; #[test] fn expression_parser_test() { @@ -301,4 +317,55 @@ mod tests { assert!(output.is_err(), "result was not Err: {output:?}"); } } + + #[test] + fn marker() { + let parser = |input| Marker::parser().parse(input); + let mut working_cases = vec![ + ("%", ("", Marker)), + ("%dd", ("dd", Marker)), + ("%%", ("%", Marker)), + ]; + let mut not_working_cases = vec!["", ",", "d%", " "]; + for (test, expected) in working_cases.drain(..) { + let output = parser(test); + if let Ok(result) = output { + assert_eq!(expected, result); + } else { + panic!("result was not Ok: {output:?}"); + } + } + for test in not_working_cases.drain(..) { + let output = parser(test); + assert!(output.is_err(), "result was not Err: {output:?}"); + } + } + + #[test] + fn note() { + let parser = |input| Note::parser(&["do", "r", "ré", "mi", "m"]).parse(input); + let mut working_cases = vec![ + ("do", ("", Note(0))), + ("ré", ("", Note(2))), + ("rér", ("r", Note(2))), + ("r.", (".", Note(1))), + ("mi", ("", Note(3))), + ("mif", ("f", Note(3))), + ("mi ", (" ", Note(3))), + ("m ", (" ", Note(4))), + ]; + let mut not_working_cases = vec!["", ",", " mi", " ", "dré"]; + for (test, expected) in working_cases.drain(..) { + let output = parser(test); + if let Ok(result) = output { + assert_eq!(expected, result, "{test}"); + } else { + panic!("result was not Ok: {output:?}"); + } + } + for test in not_working_cases.drain(..) { + let output = parser(test); + assert!(output.is_err(), "result was not Err: {output:?}"); + } + } }