expression parser rewrite with helper (failing tests)
This commit is contained in:
parent
22771168d2
commit
92a4dd02eb
1 changed files with 84 additions and 47 deletions
131
src/parser.rs
131
src/parser.rs
|
@ -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,45 +214,45 @@ 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 {
|
e.instruction
|
||||||
if let Some(e) = input.as_ref()[..end_index + 1]
|
.eval(
|
||||||
.parse::<Expression>()
|
&e.slab,
|
||||||
.ok()
|
&mut BTreeMap::from_iter(variables.iter().map(|v| (v.to_string(), 0.0))),
|
||||||
.and_then(|e| {
|
)
|
||||||
e.instruction
|
.ok()
|
||||||
.eval(
|
.is_some()
|
||||||
&e.slab,
|
.then_some(e)
|
||||||
&mut BTreeMap::from_iter(
|
})
|
||||||
variables.iter().map(|v| (v.to_string(), 0.0)),
|
})
|
||||||
),
|
.parse(input)
|
||||||
)
|
}
|
||||||
.ok()
|
}
|
||||||
.is_some()
|
|
||||||
.then_some(e)
|
pub fn take_while_map<F, I, O, Error: ParseError<I>>(
|
||||||
})
|
cond: F,
|
||||||
{
|
) -> impl FnMut(I) -> IResult<I, O, Error>
|
||||||
current_expression = Some(e);
|
where
|
||||||
end_index += 1;
|
I: Input + Copy,
|
||||||
} else if let Some(e) = current_expression {
|
F: Fn(I) -> Option<O>,
|
||||||
return take(end_index).parse(input).map(|(r, _)| (r, e));
|
{
|
||||||
|
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 {
|
||||||
return Err(nom::Err::Failure(nom::error::Error::new(
|
result = new_result;
|
||||||
input,
|
len += 1;
|
||||||
ErrorKind::Satisfy,
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 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:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue