parsing a captured item

44 views
Skip to first unread message

Christian Sage

unread,
Mar 16, 2016, 3:24:39 PM3/16/16
to parboiled2.org User List
Hi,

I am parsing fixed length records. One of the fields is a record of 20 digits. That much is easy, I do

def cardNumber = rule(capture(20.times(CharPredicate.Digit))

So now I have a string on the stack. The problem I'm faced with is that the content of this field also needs to be parsed. Informally the rules are
  • discard any zeroes at the head of the string
  • take the next six digits and use them as the issuer code
  • take the remainder of the card and use them as the individual card's id relative to the issuer number.
The next field after the cardNumber is also numeric. Therefore the alternative strategy I tried

def prefixLength = rule(zeroOrMore(ch('0')))
def issuerCode = rule(capture(6.times(CharPredicate.Digit)) ~> (_.toInt))
def individualSuffix = rule(capture(oneOrMore(CharPredicate.Digit)) ~> (BigInt(_)))
def cardNumber = rule(&(20.times(CharPredicate.Digit)) ~ fuelCardPrefixLength ~ cardIssuerCode ~ individualSuffix) 

does not stop at the end of the cardNumber field. Rather, it also matches part of the subsequent field and thus produces an incorrect result.

How can I get around this? The only solution that I've come up with so far is to use a regex against the string the cardNumber rule has put on the stack in a parser action. However, that has the downside that I could not report a parsing error against the field content in a fashion consistent with other parsing errors.

If anyone has a recommendation for me how to deal with this properly, I'd appreciate it.

Thanks,
Christian

Mathias Doenitz

unread,
Mar 16, 2016, 3:37:52 PM3/16/16
to parboil...@googlegroups.com
Christian,

AFAICS the problem is the `oneOrMore(CharPredicate.Digit)` in your `individualSuffix` rule.
It will happily eat into subsequent fields and not care about your initial test for 20 digits.

However, there are a number of possible solutions to stop it from reading beyond the 20th digit.

One easy one would be something like this:

private var fieldEnd: Int = _
def cardNumber = rule(run(fieldEnd = cursor + 20) ~ fuelCardPrefixLength ~ cardIssuerCode ~ individualSuffix)
def individualSuffix = rule(capture(oneOrMore(test(cursor < fieldEnd) ~ CharPredicate.Digit)) ~> (BigInt(_)))

HTH and cheers,
Mathias

---
mat...@parboiled.org
http://www.parboiled.org
> --
> You received this message because you are subscribed to the Google Groups "parboiled2.org User List" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to parboiled-use...@googlegroups.com.
> Visit this group at https://groups.google.com/group/parboiled-user.
> To view this discussion on the web visit https://groups.google.com/d/msgid/parboiled-user/dc8d1e82-54fd-43ac-8bee-ffaef0e565fd%40googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Christian Sage

unread,
Mar 17, 2016, 6:11:55 PM3/17/16
to parboiled2.org User List
Mathias,

thank you very much for providing me with a solution so promptly! It works like a charm, and it had the pleasant side effect that I now understand at least one way in which the test action can be used profitably. As I am planning on writing a number of additional parsers over the next few weeks, I am sure it will come in handy.

Thanks again,

Cheers,
Christian

Mathias Doenitz

unread,
Mar 18, 2016, 3:44:44 AM3/18/16
to parboil...@googlegroups.com
Ok, I'm glad things are working out for you.

Cheers,
> --
> You received this message because you are subscribed to the Google Groups "parboiled2.org User List" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to parboiled-use...@googlegroups.com.
> Visit this group at https://groups.google.com/group/parboiled-user.
> To view this discussion on the web visit https://groups.google.com/d/msgid/parboiled-user/4299ba8f-f153-4911-97d6-64044c264bfb%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages