Search code examples
javajparsec

How do I extract all values from a sequenced parser in JParsec?


I have a JParsec parser that consists of a few "smaller" parsers, and I would like to extract the total value parsed with those parsers. Let's say:

//                          V-- because of sequence()
private static final Parser<Object> definitionParser = sequence(
        substitute, separator, digits4, separator, digits2, separator, description.optional()
);

Some of the parsers above are dummy and are used to delimit data fields. The dummy parsers are substitute and separator, and I'm not going to extract anything from them. However, the rest of the parsers makes the interest to me:

private static final Parser<Short> digits4 = ...; // 4 hex digits, just a short value
private static final Parser<Byte> digits2 = ...; // 2 hex digits, just a byte value
private static final Parser<String> description = ...; // arbitrary string

However, mapping the substituteDefinition parser requires a Map<Object, Definition> interface implementation propagating the result of the very last sequenced parser description.optional() to the Map implementation, and the incoming argument is a String:

private static final Parser<Definition> definitionParser = sequence(
        substitute, separator, digits4, separator, digits2, separator, description.optional()
).map(new Map<Object, Definition>() {
    @Override
    public Definition map(final Object o) {
        ... o is a String here because description.optional() is the last one
    }
});

Obviously, I can only extract description.optional() value here, but I can't find a way to reach the digits4 and digits2 parsers results. I'm wondering: is it possible to extract digits4, digits2 and description values into a single Definition object using the approach above? I was thinking of a Definition builder implementation and passing it through the parsers chain somehow too. Or should it be rethought, and if so, then how?


Solution

  • If you want to use the return values of some or all of your parsers, then sequence(Parser<?> ... parsers) is not the combinator you should use. Depending on the number of parsers you want to combine, you can use one of:

    • The overriden sequence() combinators from 2 to 5 parsers, to which you can apply the appropriate map(),
    • The list() combinator which returns your parsers' results in a List<Object>,
    • The array() combinator which returns an Object[]
    • The tuple() combinators which return from 2 to 5-tuples.

    For separator tokens, you could benefit from using Parser.sepBy() or Parser.followedBy(): This would allow you to have a shorter sequence() with only relevant results.