Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent greedyness with PetitParser?

I'm trying to implement the BNF for EPD in Pharo/PetitParser.

digit18 := $1 asParser / $2 asParser / $3 asParser / $4 asParser / $5 asParser / $6 asParser / $7 asParser / $8 asParser.
piecePromotion := $N asParser / $B asParser / $R asParser / $Q asParser.
promotion := ($= asParser) , piecePromotion.
fileLetter := ($a asParser / $b asParser / $c asParser / $d asParser / $e asParser / $f asParser / $g asParser / $h asParser).
targetSquare := fileLetter , digit18.
disambiguation := fileLetter / digit18.
pieceCode := ($N asParser / $B asParser / $R asParser / $Q asParser / $K asParser) optional.
castles := $O asParser, $- asParser, $O asParser, (($- asParser, $O asParser) optional) .
sanMove := (pieceCode, disambiguation optional, targetSquare, promotion optional, ($+ asParser / $# asParser) optional) "/ castles". "commented out because I'd be getting another error with this enabled"

I then try and parse like this:

element := PPUnresolvedParser new.

element def: ( sanMove ).
mse := element end.
mse parse: 'Re4'.

But I get this error:

$h expected at 2 // btw these indexes seem to start at 0

If I try Ree4 as input it parses successfully as #($R $e #($e $4) nil nil). This makes me think that the optional flag on disambiguation isn't working properly, and that the parser doesn't try and see if it parses without parsing the "e" as a disambiguation, even though it can be. It leads to not being able to parse mandatory targetSquare though, so I don't understand why PetitParser gives up.

like image 300
Janus Troelsen Avatar asked Oct 08 '22 13:10

Janus Troelsen


1 Answers

Parsing Expression Grammars (the kind of parser technology PetitParser is using) are greedy. This means they never backtrack as soon as something has been successfully consumed. In your case the rule "disambiguation" is successfully consumed, thus the parser never retries skipping it even if the parser later gets stuck.

BNF and PEG grammars look similar, but they have very different semantics. Therefor you cannot just translate a BNF grammar rule by rule to PetitParser. You have to carefully arrange the choices (order matters in PEGs). In your particular example you could change sanMove to:

sanMove := pieceCode, ((targetSquare, promotion optional, ($+ asParser / $# asParser) optional) / (disambiguation optional, targetSquare, promotion optional, ($+ asParser / $# asParser) optional)).

With that rule both of your tests parse:

sanMove end parse: 'Ree4'.
sanMove end parse: 'Re4'.
like image 50
Lukas Renggli Avatar answered Oct 12 '22 01:10

Lukas Renggli