make parsers generic over I again for tests and other things

This commit is contained in:
Breval Ferrari 2025-05-21 11:20:39 +02:00
parent 8563ee3de2
commit 16f79c302a

View file

@ -6,7 +6,7 @@ use std::{
use derive_builder::Builder; use derive_builder::Builder;
use fasteval::Evaler; use fasteval::Evaler;
use nom::{ use nom::{
Finish, IResult, Input, Parser as NomParser, AsChar, Compare, Finish, IResult, Input, Parser as NomParser,
branch::alt, branch::alt,
bytes::complete::{tag, take, take_till}, bytes::complete::{tag, take, take_till},
character::{ character::{
@ -43,13 +43,14 @@ impl<'i, 's> Parser<'i, '_, 's, '_> {
} }
} }
fn token_parser<'a, 's>( fn token_parser<'a, 's, I>(
parser: &Parser<'_, '_, 's, '_>, parser: &Parser<'_, '_, 's, '_>,
) -> impl NomParser< ) -> impl NomParser<I, Output = Vec<Box<dyn Token + 's>>, Error = nom::error::Error<I>>
LocatedSpan<&'a str>, where
Output = Vec<Box<dyn Token + 's>>, I: Input + AsRef<str> + for<'z> nom::Compare<&'z str>,
Error = nom::error::Error<LocatedSpan<&'a str>>, <I as Input>::Item: AsChar,
> { <I as Input>::Item: PartialEq<char>,
{
let space_or_comment = || { let space_or_comment = || {
value( value(
(), (),
@ -75,58 +76,55 @@ fn into_box<'a>(token: impl Token + 'a) -> Box<dyn Token + 'a> {
Box::new(token) Box::new(token)
} }
pub type TokenResult<'a, T> = IResult<LocatedSpan<&'a str>, T>;
impl Silence { impl Silence {
fn parser<'a>() -> impl NomParser< fn parser<I>() -> impl NomParser<I, Output = Self, Error = nom::error::Error<I>>
LocatedSpan<&'a str>, where
Output = Self, I: Input,
Error = nom::error::Error<LocatedSpan<&'a str>>, <I as Input>::Item: AsChar,
> { {
value(Self, char('.')) value(Self, char('.'))
} }
} }
impl Marker { impl Marker {
fn parser<'a>() -> impl NomParser< fn parser<I>() -> impl NomParser<I, Output = Self, Error = nom::error::Error<I>>
LocatedSpan<&'a str>, where
Output = Self, I: Input,
Error = nom::error::Error<LocatedSpan<&'a str>>, <I as Input>::Item: AsChar,
> { {
value(Marker, char('%')) value(Marker, char('%'))
} }
} }
impl Note { impl Note {
fn parser<'a, 'n>( fn parser<'n, I>(
notes: &'n [String], notes: &'n [String],
) -> impl NomParser< ) -> impl NomParser<I, Output = Self, Error = nom::error::Error<I>>
LocatedSpan<&'a str>, where
Output = Self, I: Input + for<'z> Compare<&'z str>,
Error = nom::error::Error<LocatedSpan<&'a str>>, {
> { |input: I| {
|input: LocatedSpan<&'a str>| { let mut parsers: Vec<Box<dyn Fn(I) -> IResult<I, Self>>> = notes
let mut parsers: Vec<Box<dyn Fn(LocatedSpan<&'a str>) -> TokenResult<'a, Self>>> = .iter()
notes .enumerate()
.iter() .map(|(i, t)| {
.enumerate() Box::new(move |input: I| {
.map(|(i, t)| { value(Note(i as u8), tag(t.clone().as_str())).parse(input)
Box::new(move |input: LocatedSpan<&'a str>| { }) as Box<dyn Fn(I) -> IResult<I, Self>>
value(Note(i as u8), tag(t.clone().as_str())).parse(input) })
}) .collect();
as Box<dyn Fn(LocatedSpan<&'a str>) -> TokenResult<'a, Self>>
})
.collect();
alt(parsers.as_mut_slice()).parse(input) alt(parsers.as_mut_slice()).parse(input)
} }
} }
} }
impl VariableChange { impl VariableChange {
fn parser( fn parser<I>(variables: &[char]) -> impl Fn(I) -> IResult<I, Self>
variables: &[char], where
) -> impl for<'a> Fn(LocatedSpan<&'a str>) -> TokenResult<'a, Self> { I: Input + AsRef<str>,
move |i: LocatedSpan<&str>| { <I as Input>::Item: AsChar,
{
move |i: I| {
preceded(char('$'), one_of(variables)) preceded(char('$'), one_of(variables))
.and(expression_parser(variables)) .and(expression_parser(variables))
.map(|(name, change)| VariableChange(name, change)) .map(|(name, change)| VariableChange(name, change))
@ -136,9 +134,12 @@ impl VariableChange {
} }
impl<'s> Loop<'s> { impl<'s> Loop<'s> {
fn parser<'i, 'n, 'v>( fn parser<'i, 'n, 'v, I>(parser: &Parser<'i, 'n, 's, 'v>) -> impl Fn(I) -> IResult<I, Self>
parser: &Parser<'i, 'n, 's, 'v>, where
) -> impl Fn(LocatedSpan<&str>) -> TokenResult<Self> { I: Input + AsRef<str> + for<'z> nom::Compare<&'z str>,
<I as Input>::Item: AsChar,
<I as Input>::Item: PartialEq<char>,
{
|input| { |input| {
delimited( delimited(
char('('), char('('),
@ -156,9 +157,12 @@ impl<'s> Loop<'s> {
} }
impl<'s> Tuplet<'s> { impl<'s> Tuplet<'s> {
fn parser<'i, 'n, 'v>( fn parser<'i, 'n, 'v, I>(parser: &Parser<'i, 'n, 's, 'v>) -> impl Fn(I) -> IResult<I, Self>
parser: &Parser<'i, 'n, 's, 'v>, where
) -> impl Fn(LocatedSpan<&str>) -> TokenResult<Self> { I: Input + for<'z> Compare<&'z str> + AsRef<str>,
<I as Input>::Item: AsChar,
<I as Input>::Item: PartialEq<char>,
{
|input| { |input| {
delimited(char('['), token_parser(parser), char(']')) delimited(char('['), token_parser(parser), char(']'))
.map(Self) .map(Self)
@ -168,34 +172,24 @@ impl<'s> Tuplet<'s> {
} }
impl<'s> Slope<'s> { impl<'s> Slope<'s> {
fn parser<'a, 'i, 'n, 'v>( fn parser<'i, 'n, 'v, I>(parser: &Parser<'i, 'n, 's, 'v>) -> impl Fn(I) -> IResult<I, Self>
parser: &Parser<'i, 'n, 's, 'v>, where
) -> impl Fn(LocatedSpan<&'a str>) -> TokenResult<'a, Self> { I: Input + for<'z> Compare<&'z str> + AsRef<str>,
<I as Input>::Item: AsChar,
<I as Input>::Item: PartialEq<char>,
{
|input| { |input| {
let iter: std::collections::hash_map::Iter<'s, String, VariableChange> = let iter: std::collections::hash_map::Iter<'s, String, VariableChange> =
parser.slopes.iter(); parser.slopes.iter();
delimited( delimited(
char('{'), char('{'),
alt( alt(iter
iter .map(|(k, v)| {
.map(|(k, v)| { Box::new(move |input: I| value(v, tag(k.as_str())).parse(input))
Box::new(move|input: LocatedSpan<&'a str>| { as Box<dyn 's + Fn(I) -> IResult<I, &'s VariableChange>>
value(v, tag(k.as_str())).parse(input) })
}) .collect::<Vec<Box<dyn 's + Fn(I) -> IResult<I, &'s VariableChange>>>>()
as Box< .as_mut_slice())
dyn 's + Fn(
LocatedSpan<&'a str>,
)
-> TokenResult<'a, &'s VariableChange>,
>
})
.collect::<Vec<
Box<
dyn 's + Fn(LocatedSpan<&'a str>) -> TokenResult<'a, &'s VariableChange>,
>,
>>()
.as_mut_slice(),
)
.and(token_parser(parser)), .and(token_parser(parser)),
char('}'), char('}'),
) )
@ -206,14 +200,14 @@ impl<'s> Slope<'s> {
} }
/// Will return the longest valid fasteval expression /// Will return the longest valid fasteval expression
fn expression_parser<C: Into<char> + Ord + Display>( fn expression_parser<'a, I: Input + AsRef<str>, C: Into<char> + Ord + Display>(
variables: &[C], variables: &[C],
) -> impl Fn(LocatedSpan<&str>) -> TokenResult<Expression> { ) -> impl Fn(I) -> IResult<I, Expression> {
|input: LocatedSpan<&str>| { |input: I| {
let mut end_index = 0; let mut end_index = 0;
let mut current_expression = None; let mut current_expression = None;
while input.input_len() > end_index { while input.input_len() > end_index {
if let Some(e) = input[..end_index + 1] if let Some(e) = input.as_ref()[..end_index + 1]
.parse::<Expression>() .parse::<Expression>()
.ok() .ok()
.and_then(|e| { .and_then(|e| {
@ -241,9 +235,7 @@ fn expression_parser<C: Into<char> + Ord + Display>(
} }
} }
if let Some(e) = current_expression { if let Some(e) = current_expression {
Ok::<(LocatedSpan<&str>, Expression), nom::Err<nom::error::Error<LocatedSpan<&str>>>>(( Ok((input, e))
input, e,
))
} else { } else {
Err(nom::Err::Incomplete(nom::Needed::Unknown)) Err(nom::Err::Incomplete(nom::Needed::Unknown))
} }
@ -252,13 +244,14 @@ fn expression_parser<C: Into<char> + Ord + Display>(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use nom::Parser;
use nom_locate::LocatedSpan; use nom_locate::LocatedSpan;
use crate::parser::expression_parser; use crate::{compiler::Silence, parser::expression_parser};
#[test] #[test]
fn expression_parser_test() { fn expression_parser_test() {
let parser = expression_parser::<char>(&['x']); let parser = expression_parser(&['x']);
let mut working_test_cases = vec![ let mut working_test_cases = vec![
"1", "1",
"1x", "1x",
@ -276,13 +269,32 @@ mod tests {
]; ];
for test in working_test_cases.drain(..) { for test in working_test_cases.drain(..) {
let output = parser(LocatedSpan::new(test)); let output = parser(test);
assert!(output.is_ok(), "result was not Ok: {output:?}"); assert!(output.is_ok(), "result was not Ok: {output:?}");
} }
for test in not_working_test_cases.drain(..) { for test in not_working_test_cases.drain(..) {
let output = parser(LocatedSpan::new(test)); let output = parser(test);
assert!(output.is_err(), "result was not Err: {output:?}"); assert!(output.is_err(), "result was not Err: {output:?}");
} }
} }
// #[test]
// fn silence() {
// let parser = |input: &'static str| Silence::parser().parse(input.into());
// let mut working_cases = vec![(".", ()), ".dd", ".."];
// let mut not_working_cases = vec![];
// 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:?}");
// }
// }
} }