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.
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'.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With