Nom: parsing with a separator in-between things (and ONLY in-between)

I have an integer n and want to parse n integers separated by commas, with no spaces.

I.e. if n == 3, I want to parse "3,5,4" to vec![3, 5, 4].

I tried the obvious thing:

named!{comma<&str, &str>, tag!(",")}
named!{
    nonnegative_integer<&str, usize>,
    map_res!(digit, FromStr::from_str)
}

fn dimensions(input: &str, ndim: usize) -> IResult<&str, Vec<usize>> {
    sep!(input, comma, count!(nonnegative_integer, ndim))
}

…but it is very, VERY obvious that sep! was never intended to be used with a parser like tag!(",") that can’t match the empty string.

  • call!(dimensions, 0) parses the string ",,"
  • call!(dimensions, 1) parses the string ",1,"
  • call!(dimensions, 2) parses… I don’t even know actually! (it doesn’t parse the string ",1,2,"!)

Nothing else in the macro list seems to be what I want. Am I missing something?

Okay, so I hacked this thing together. I think this does it?

fn dimensions(s: &str, ndim: usize) -> IResult<&str, Vec<usize>> {
    match separated_list!(s, tag!(","), nonnegative_integer) {
        IResult::Done(_, ref vec) if vec.len() != ndim => {
            IResult::Error(error_position!(ErrorKind::SeparatedList, s))
        },
        result => result,
    }
}

#[test]
fn test_count_separated() {
    assert_eq!(
        dimensions("1,2,3 ", 3),
        IResult::Done(" ", vec![1, 2, 3]),
    )
}

I’ve never used nom, but from here it looks like sep! is intended for whitespace delimitted text. Maybe separated_list is what you want?
EDIT: Looks like you found it. :slight_smile:

To be honest, my experience with nom so far has been a fair bit offputting. And not just due to its nonexistent support for trailing commas! (though that is a big part of it!) Basically it has all the hallmarks of a big project with not enough maintainers—which is surprising considering that it has over 150 contributors on github.

Its documentation is sparse, vague, and oftentimes in the wrong place (it seems to me that module shouldn’t even be public!). Edge cases are seldom mentioned if ever.

Many of its parsers are very strangely named:

  • What’s the parser that, given a &str alphabet, consumes 0 or more characters from the input until it reaches one that is not in the alphabet? Answer: is_a.
  • There is a parser called eat_separator. It has basically no documentation, but as far as I can tell it is identical to is_a (but with a poorer implementation that sometimes fails type inference).
  • digit and alpha also read 0 or more characters. If you want to read one character… I’m not even sure how you do that! Some combination of take_s! and verify! maybe?

Speaking of verify!, I just now figured that I could maybe use it to simplify my function above. Unfortunately, the closure takes the argument by value, so it doesn’t work on non-Copy types.

(it also fails to infer the closure’s argument type, for which I may submit a simple PR)

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.