rust-bakery / nom

Rust parser combinator framework
MIT License
9.43k stars 805 forks source link

Provide Postfix Method Syntax for Some Combinators #1134

Closed eignnx closed 3 years ago

eignnx commented 4 years ago

Proposal

I think it could improve readability of some complicated parsers if specific combinators could be applied as postfix method calls.

Candidate Combinators

Example of Proposed Syntax

This change would allow the following code...

fn data_variant_decl<'i>(input: &'i [Lex]) -> Res<'i, (DataVariant, Vec<SententialForm>)> {
    tuple((
        map(lower_ident, DataVariant),
        map(
            opt(delimited(
                lexeme(Lex::LParen),
                sentential_form_alternatives,
                lexeme(Lex::RParen),
            )),
            |opt_alternatives| opt_alternatives.unwrap_or_else(Vec::new),
        ),
    ))(input)
}

...to be rewritten:

fn data_variant_decl<'i>(input: &'i [Lex]) -> Res<'i, (DataVariant, Vec<SententialForm>)> {
    tuple((
        lower_ident.map(DataVariant),
        delimited(
            lexeme(Lex::LParen),
            sentential_form_alternatives,
            lexeme(Lex::RParen),
        ).opt().map(|opt_alternatives| opt_alternatives.unwrap_or_else(Vec::new)),
    ))(input)
}

This transformation allowed the delimited parser to be "un-nested" two levels.

(The example is from here.)

Implementation

As I understand it, nom doesn't define anything like a ParserCombinator trait to which these methods could be attached, but maybe a new trait that Fn(I) -> IResult<I, O, E> implements could be created (warning, pseudocode ahead): See recent commit 84d1ac0 where a Parser trait was defined.

trait PostfixCombinators {
    fn map(...) -> ...;
    fn opt(...) -> ...;
    fn context(...) -> ...;
    fn cut(...) -> ...;
    // ...
}

impl<P, I, O, E> PostfixCombinators for P
    where P: Fn(I) -> IResult<I, O, E>
{
    fn map<F, O2>(self, f: F) -> Box<dyn Fn(I) -> IResult<I, O2, E>>
        where F: Fn(O) -> IResult<I, O2, E>
    {
        Box::new(map(self, f))
    }

    // ...
}

Syntactic changes like this probably shouldn't be high up on the priority list, but I just wanted to put this proposal out there in case other people are interested.

Geal commented 4 years ago

so with ac34e5a and 61772cd we now have map, flat_map, and, and_then and or methods. I'm not sure about adding opt and cut, and context will not appear there, since I'm separating from the main error type (cf #1131)

eignnx commented 4 years ago

The current map syntax in 5.1.1 is the by far least ergonomic out of the combinators I listed, so I'm really glad to see it will be updated!

Geal commented 3 years ago

nom 6 is released now :partying_face: