diff --git a/src/parser.rs b/src/parser.rs index 21353c9..0d12d79 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -13,8 +13,8 @@ use nom::{ complete::{char, space1, usize}, streaming::one_of, }, - combinator::{opt, value}, - error::{Error, ErrorKind}, + combinator::{opt, value, verify}, + error::{Error, ErrorKind, ParseError}, multi::many0, sequence::{delimited, preceded}, }; @@ -46,7 +46,7 @@ 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>, + I: Input + AsRef + for<'z> nom::Compare<&'z str> + Copy, ::Item: AsChar, ::Item: PartialEq, N: AsRef + Clone, @@ -126,7 +126,7 @@ impl Note { impl VariableChange { fn parser(variables: &[char]) -> impl Fn(I) -> IResult where - I: Input + AsRef, + I: Input + AsRef + Copy, ::Item: AsChar, { move |i: I| { @@ -143,7 +143,7 @@ impl<'s> Loop<'s> { parser: &Parser<'i, 'n, 's, 'v, N>, ) -> impl Fn(I) -> IResult where - I: Input + AsRef + for<'z> nom::Compare<&'z str>, + I: Input + AsRef + for<'z> nom::Compare<&'z str> + Copy, ::Item: AsChar, ::Item: PartialEq, N: AsRef + Clone, @@ -169,7 +169,7 @@ impl<'s> Tuplet<'s> { parser: &Parser<'i, 'n, 's, 'v, N>, ) -> impl Fn(I) -> IResult where - I: Input + for<'z> Compare<&'z str> + AsRef, + I: Input + for<'z> Compare<&'z str> + AsRef + Copy, ::Item: AsChar, ::Item: PartialEq, N: AsRef + Clone, @@ -187,7 +187,7 @@ impl<'s> Slope<'s> { parser: &Parser<'i, 'n, 's, 'v, N>, ) -> impl Fn(I) -> IResult where - I: Input + for<'z> Compare<&'z str> + AsRef, + I: Input + for<'z> Compare<&'z str> + AsRef + Copy, ::Item: AsChar, ::Item: PartialEq, N: AsRef + Clone, @@ -214,45 +214,45 @@ impl<'s> Slope<'s> { } /// Will return the longest valid fasteval expression -fn expression_parser<'a, I: Input + AsRef, C: Into + Ord + Display>( +fn expression_parser<'a, I: Input + AsRef + Copy, C: Into + Ord + Display>( variables: &[C], ) -> impl Fn(I) -> IResult { |input: I| { - let mut end_index = 0; - let mut current_expression = None; - while input.input_len() > end_index { - if let Some(e) = input.as_ref()[..end_index + 1] - .parse::() - .ok() - .and_then(|e| { - e.instruction - .eval( - &e.slab, - &mut BTreeMap::from_iter( - variables.iter().map(|v| (v.to_string(), 0.0)), - ), - ) - .ok() - .is_some() - .then_some(e) - }) - { - current_expression = Some(e); - end_index += 1; - } else if let Some(e) = current_expression { - return take(end_index).parse(input).map(|(r, _)| (r, e)); + take_while_map(|i: I| { + i.as_ref().parse::().ok().and_then(|e| { + e.instruction + .eval( + &e.slab, + &mut BTreeMap::from_iter(variables.iter().map(|v| (v.to_string(), 0.0))), + ) + .ok() + .is_some() + .then_some(e) + }) + }) + .parse(input) + } +} + +pub fn take_while_map>( + cond: F, +) -> impl FnMut(I) -> IResult +where + I: Input + Copy, + F: Fn(I) -> Option, +{ + move |input: I| { + let mut len = 1usize; + let mut result = Err(nom::Err::Incomplete(nom::Needed::Unknown)); + loop { + let new_result = take(len).map_opt(&cond).parse(input); + if new_result.is_err() { + return result; } else { - return Err(nom::Err::Failure(nom::error::Error::new( - input, - ErrorKind::Satisfy, - ))); + result = new_result; + len += 1; } } - if let Some(e) = current_expression { - Ok((input, e)) - } else { - Err(nom::Err::Incomplete(nom::Needed::Unknown)) - } } } @@ -261,7 +261,7 @@ mod tests { use nom::Parser; use crate::{ - compiler::{Marker, Note, Silence}, + compiler::{Marker, Note, Silence, VariableChange}, parser::expression_parser, }; @@ -269,10 +269,13 @@ mod tests { fn expression_parser_test() { let parser = expression_parser(&['x']); let mut working_test_cases = vec![ - "1", - "1x", - "56coucou", // should stop after 56 because c is not a known variable - "8x + 1 heille salut ça va ou quoi 46 - 5x", + ("1", ("", "1")), + ("1x", ("", "1x")), + ("56coucou", ("coucou", "56")), // should stop after 56 because c is not a known variable + ( + "8x + 1 heille salut ça va ou quoi 46 - 5x", + ("heille salut ça va ou quoi 46 - 5x", "8x + 1"), + ), ]; let mut not_working_test_cases = vec![ "", @@ -284,9 +287,17 @@ mod tests { " 1", // this too but the parser should remain dumb and not expect spaces before / after ]; - for test in working_test_cases.drain(..) { + for (test, expected) in working_test_cases.drain(..) { let output = parser(test); - assert!(output.is_ok(), "result was not Ok: {output:?}"); + if let Ok(result) = output { + assert_eq!( + (expected.0, expected.1.parse().unwrap()), + result, + "case \"{test}\"" + ) + } else { + panic!("result was not Ok: {output:?}"); + } } for test in not_working_test_cases.drain(..) { @@ -368,4 +379,30 @@ mod tests { assert!(output.is_err(), "result was not Err: {output:?}"); } } + + #[test] + fn variable_change() { + let parser = |input| VariableChange::parser(&['a', 'b', 'ö']).parse(input); + let mut working_cases = vec![("$a5", ("", ('a', "5")))]; + let mut not_working_cases = vec!["", "$", "$a", "$c8", "$d/1"]; + for (test, expected) in working_cases.drain(..) { + let output = parser(test); + if let Ok(result) = output { + assert_eq!( + ( + expected.0, + VariableChange(expected.1.0, expected.1.1.parse().unwrap()) + ), + result, + "case \"{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:?}"); + } + } }