How to make parboiled2 match the whole input?

470 Views Asked by At

I wrote the following hello-world parboiled2 parser:

class MyParser(val input: ParserInput) extends Parser {

 /*
  Expr     <- Sum
  Sum      <- Product ('+') Product)*
  Product  <- Value (('*') Value)*
  Value    <- Constant | '(' Expr ')'
  Constant <- [0-9]+
  */

  def Expr: Rule1[Int] = rule { Sum }

  def Sum: Rule1[Int] = rule { oneOrMore(Product).separatedBy(" + ") ~> ((products: Seq[Int]) => products.sum) }

  def Product: Rule1[Int] = rule { oneOrMore(Value).separatedBy(" * ") ~> ((values: Seq[Int]) => values.product) }

  def Value: Rule1[Int] = rule { Constant | ('(' ~ Expr ~ ')') }

  def Constant: Rule1[Int] = rule { capture(oneOrMore(Digit)) ~> ((digits: String) => digits.toInt) }

}

This works mostly as expected, e.g. it successfully parses "1 + 2" as 3.

If I give it invalid input such as "1 + (2", I would expect the parse to fail. But it actually succeeds, with 1 as the result.

It looks like parboiled2 is only parsing part of the input, and ignoring the remainder that it cannot parse. Is this expected behaviour? Is there any way to force the parser to parse the whole input and fail if it cannot do so?

2

There are 2 best solutions below

1
On BEST ANSWER

This is expected behavior. parboiled2 is a PEG parser, and as described in the Common Mistakes section in the documentation, it eats everything it can find.

To avoid such a problem, make sure that you expect the end-of-input symbol at the end of your string:

def Expr: Rule1[Int] = rule { Sum ~ EOI }
0
On

If I input “1+(2+3*4)+5”, the parse will fail. Defines another root rule and leaves the Expr as it was will do the trick:

def InputLine = rule { Expr ~ EOI }
def Expr: Rule1[Int] = rule { Sum }