How to have a separator in nom with an optional terminating separator?
Asked Answered
S

2

5

I'd like to parse both of these with nom:

[
   a, 
   b,  
   c
]
[
   a, 
   b,  
   c,
]

Currently I have this code which parses the first but not the second (the first function is a recipe from the nom docs which just parses whitespace):

// https://github.com/Geal/nom/blob/main/doc/nom_recipes.md#wrapper-combinators-that-eat-whitespace-before-and-after-a-parser
fn ws<'a, F: 'a, O, E: ParseError<&'a str>>(
    inner: F,
) -> impl FnMut(&'a str) -> IResult<&'a str, O, E>
where
    F: Fn(&'a str) -> IResult<&'a str, O, E>,
{
    delimited(multispace0, inner, multispace0)
}

pub fn parse_list(input: &str) -> IResult<&str, Vec<&str>> {
    delimited(
        ws(tag("[")),
        separated_list0(
            ws(tag(",")),
            take_while1(|x| char::is_alphabetic(x) || x == '_'),
        ),
        ws(tag("]")),
    )(input)
}

I'm new to nom, and don't have any allegiance to the current code, so fine to tell me I'm doing it all wrong...

Thanks!

Spirketing answered 3/1, 2022 at 8:6 Comment(0)
C
7

Here's one (of possibly many solutions).

Just use terminated along with opt:

pub fn parse_list(input: &str) -> IResult<&str, Vec<&str>> {
    delimited(
        ws(tag("[")),
        terminated(
            separated_list0(
                ws(tag(",")),
                take_while1(|x| char::is_alphabetic(x) || x == '_'),
            ),
            opt(ws(tag(","))),
        ),
        ws(tag("]")),
    )(input)
}
Choirmaster answered 3/1, 2022 at 9:31 Comment(2)
Note that this accepts a single separator with no items, e.g. [,]Ripon
@Ripon You're kind of right. I used separated_list0 because OP did as well. Of course, you can simply use seperated_list1 in case you need at least one element.Choirmaster
R
0

An adaptation of hellow's answer that disallows [,]:

pub fn parse_list(input: &str) -> IResult<&str, Vec<&str>> {
    delimited(
        ws(tag("[")),
        opt(
            terminated(
                separated_list1(
                    ws(tag(",")),
                    take_while1(|x| char::is_alphabetic(x) || x == '_'),
                ),
                opt(ws(tag(","))),
            )
        ).map(Option::unwrap_or_default),
        ws(tag("]")),
    )(input)
}

Playground link

Ripon answered 4/4 at 22:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.