Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Omit elements from many0 or many1 result #1782

Open
mickvangelderen opened this issue Nov 11, 2024 · 1 comment
Open

Omit elements from many0 or many1 result #1782

mickvangelderen opened this issue Nov 11, 2024 · 1 comment

Comments

@mickvangelderen
Copy link

As part of a parser I am writing, I would like to define something that expects at least one multispace and can include line-ended comments. I would like to collect the comments into a Vec.

To accomplish this, I was looking for a multi combinator that would let me collect only certain values. I found fold_many1 and used it as follows:

fn push_some<T>(mut acc: Vec<T>, item: Option<T>) -> Vec<T> {
    if let Some(item) = item {
        acc.push(item);
    }
    acc
}

fn comment<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, &'a str, E>
{
    preceded(char('#'), is_not("\n\r"))(input)
}

// At least 1 multispace or comment, outputs the comment contents if possible.
fn space_or_comment(input: &str) -> IResult<&str, Option<&str>> {
    alt((
        value(None, multispace1),
        map(comment, Some),
    ))(input)
}

fn spacing1(input: &str) -> IResult<&str, Vec<&str>> {
    fold_many1(space_or_comment, Vec::default, push_some)(input)
}

I was wondering if it made sense to have combinators filter_many0 and filter_many1 defined as :

fn push_some<T>(mut acc: Vec<T>, item: Option<T>) -> Vec<T> {
    if let Some(item) = item {
        acc.push(item);
    }
    acc
}

pub fn filter_many0<I, O, E, F>(f: F) -> impl FnMut(I) -> IResult<I, Vec<O>, E>
where
  I: Clone + nom::InputLength,
  F: nom::Parser<I, Option<O>, E>,
  E: ParseError<I>,
{
    nom::multi::fold_many0(f, Vec::default, push_some)
}

pub fn filter_many1<I, O, E, F>(f: F) -> impl FnMut(I) -> IResult<I, Vec<O>, E>
where
  I: Clone + nom::InputLength,
  F: nom::Parser<I, Option<O>, E>,
  E: ParseError<I>,
{
    nom::multi::fold_many1(f, Vec::default, push_some)
}

With these combinators, I could write:

fn spacing1(input: &str) -> IResult<&str, Vec<&str>> {
    filter_many1(space_or_comment)(input)
}

Perhaps there is a better way to think about the problem and I don't actually need this.

@docwilco
Copy link

There's also iterator that you can use, and then you can throw filter() on that and collect::<Vec<_>>().

Though a filter_many[01] would not be a bad idea

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants