expression parser rewrite with helper (failing tests)

This commit is contained in:
Breval Ferrari 2025-05-21 16:07:04 +02:00
parent 22771168d2
commit 92a4dd02eb

View file

@ -13,8 +13,8 @@ use nom::{
complete::{char, space1, usize}, complete::{char, space1, usize},
streaming::one_of, streaming::one_of,
}, },
combinator::{opt, value}, combinator::{opt, value, verify},
error::{Error, ErrorKind}, error::{Error, ErrorKind, ParseError},
multi::many0, multi::many0,
sequence::{delimited, preceded}, sequence::{delimited, preceded},
}; };
@ -46,7 +46,7 @@ fn token_parser<'a, 's, I, N>(
parser: &Parser<'_, '_, 's, '_, N>, parser: &Parser<'_, '_, 's, '_, N>,
) -> impl NomParser<I, Output = TokenVec<'s>, Error = nom::error::Error<I>> ) -> impl NomParser<I, Output = TokenVec<'s>, Error = nom::error::Error<I>>
where where
I: Input + AsRef<str> + for<'z> nom::Compare<&'z str>, I: Input + AsRef<str> + for<'z> nom::Compare<&'z str> + Copy,
<I as Input>::Item: AsChar, <I as Input>::Item: AsChar,
<I as Input>::Item: PartialEq<char>, <I as Input>::Item: PartialEq<char>,
N: AsRef<str> + Clone, N: AsRef<str> + Clone,
@ -126,7 +126,7 @@ impl Note {
impl VariableChange { impl VariableChange {
fn parser<I>(variables: &[char]) -> impl Fn(I) -> IResult<I, Self> fn parser<I>(variables: &[char]) -> impl Fn(I) -> IResult<I, Self>
where where
I: Input + AsRef<str>, I: Input + AsRef<str> + Copy,
<I as Input>::Item: AsChar, <I as Input>::Item: AsChar,
{ {
move |i: I| { move |i: I| {
@ -143,7 +143,7 @@ impl<'s> Loop<'s> {
parser: &Parser<'i, 'n, 's, 'v, N>, parser: &Parser<'i, 'n, 's, 'v, N>,
) -> impl Fn(I) -> IResult<I, Self> ) -> impl Fn(I) -> IResult<I, Self>
where where
I: Input + AsRef<str> + for<'z> nom::Compare<&'z str>, I: Input + AsRef<str> + for<'z> nom::Compare<&'z str> + Copy,
<I as Input>::Item: AsChar, <I as Input>::Item: AsChar,
<I as Input>::Item: PartialEq<char>, <I as Input>::Item: PartialEq<char>,
N: AsRef<str> + Clone, N: AsRef<str> + Clone,
@ -169,7 +169,7 @@ impl<'s> Tuplet<'s> {
parser: &Parser<'i, 'n, 's, 'v, N>, parser: &Parser<'i, 'n, 's, 'v, N>,
) -> impl Fn(I) -> IResult<I, Self> ) -> impl Fn(I) -> IResult<I, Self>
where where
I: Input + for<'z> Compare<&'z str> + AsRef<str>, I: Input + for<'z> Compare<&'z str> + AsRef<str> + Copy,
<I as Input>::Item: AsChar, <I as Input>::Item: AsChar,
<I as Input>::Item: PartialEq<char>, <I as Input>::Item: PartialEq<char>,
N: AsRef<str> + Clone, N: AsRef<str> + Clone,
@ -187,7 +187,7 @@ impl<'s> Slope<'s> {
parser: &Parser<'i, 'n, 's, 'v, N>, parser: &Parser<'i, 'n, 's, 'v, N>,
) -> impl Fn(I) -> IResult<I, Self> ) -> impl Fn(I) -> IResult<I, Self>
where where
I: Input + for<'z> Compare<&'z str> + AsRef<str>, I: Input + for<'z> Compare<&'z str> + AsRef<str> + Copy,
<I as Input>::Item: AsChar, <I as Input>::Item: AsChar,
<I as Input>::Item: PartialEq<char>, <I as Input>::Item: PartialEq<char>,
N: AsRef<str> + Clone, N: AsRef<str> + Clone,
@ -214,44 +214,44 @@ impl<'s> Slope<'s> {
} }
/// Will return the longest valid fasteval expression /// Will return the longest valid fasteval expression
fn expression_parser<'a, I: Input + AsRef<str>, C: Into<char> + Ord + Display>( fn expression_parser<'a, I: Input + AsRef<str> + Copy, C: Into<char> + Ord + Display>(
variables: &[C], variables: &[C],
) -> impl Fn(I) -> IResult<I, Expression> { ) -> impl Fn(I) -> IResult<I, Expression> {
|input: I| { |input: I| {
let mut end_index = 0; take_while_map(|i: I| {
let mut current_expression = None; i.as_ref().parse::<Expression>().ok().and_then(|e| {
while input.input_len() > end_index {
if let Some(e) = input.as_ref()[..end_index + 1]
.parse::<Expression>()
.ok()
.and_then(|e| {
e.instruction e.instruction
.eval( .eval(
&e.slab, &e.slab,
&mut BTreeMap::from_iter( &mut BTreeMap::from_iter(variables.iter().map(|v| (v.to_string(), 0.0))),
variables.iter().map(|v| (v.to_string(), 0.0)),
),
) )
.ok() .ok()
.is_some() .is_some()
.then_some(e) .then_some(e)
}) })
{ })
current_expression = Some(e); .parse(input)
end_index += 1;
} else if let Some(e) = current_expression {
return take(end_index).parse(input).map(|(r, _)| (r, e));
} else {
return Err(nom::Err::Failure(nom::error::Error::new(
input,
ErrorKind::Satisfy,
)));
} }
} }
if let Some(e) = current_expression {
Ok((input, e)) pub fn take_while_map<F, I, O, Error: ParseError<I>>(
cond: F,
) -> impl FnMut(I) -> IResult<I, O, Error>
where
I: Input + Copy,
F: Fn(I) -> Option<O>,
{
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 { } else {
Err(nom::Err::Incomplete(nom::Needed::Unknown)) result = new_result;
len += 1;
}
} }
} }
} }
@ -261,7 +261,7 @@ mod tests {
use nom::Parser; use nom::Parser;
use crate::{ use crate::{
compiler::{Marker, Note, Silence}, compiler::{Marker, Note, Silence, VariableChange},
parser::expression_parser, parser::expression_parser,
}; };
@ -269,10 +269,13 @@ mod tests {
fn expression_parser_test() { fn expression_parser_test() {
let parser = expression_parser(&['x']); let parser = expression_parser(&['x']);
let mut working_test_cases = vec![ let mut working_test_cases = vec![
"1", ("1", ("", "1")),
"1x", ("1x", ("", "1x")),
"56coucou", // should stop after 56 because c is not a known variable ("56coucou", ("coucou", "56")), // should stop after 56 because c is not a known variable
(
"8x + 1 heille salut ça va ou quoi 46 - 5x", "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![ 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 " 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); 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(..) { for test in not_working_test_cases.drain(..) {
@ -368,4 +379,30 @@ mod tests {
assert!(output.is_err(), "result was not Err: {output:?}"); 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:?}");
}
}
} }