Lyubomyr Shaydariv Lyubomyr Shaydariv - 26 days ago 10
Java Question

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,
map
ping 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?

Answer

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.