Logo

14.1.3 Dealing with cyclic dependencies when scripting the MSE grammar

In Section 14.1.1 we see in details how we approach the definition of parsers to deal with the names. Once the grammar is clear, the process is straightforward and can be repeated for any production rules.

The full MSE parser can be seen below.

open := $( asParser trim.
close := $) asParser trim.
string := ($' asParser ,
('''''' asParser / $' asParser negate) star flatten ,
$' asParser) trim.
natural := #digit asParser plus flatten.
e := ($e asParser / $E asParser) , ($- asParser / $+ asParser) optional , natural.
number := ($- asParser optional , natural ,
($. asParser , natural , e optional) optional) flatten trim.
boolean := ('true' asParser / 'false' asParser) flatten trim.
primitive := string / number / boolean.
simpleName := #word asParser star flatten.
elementName := (simpleName , ($. asParser , simpleName) optional) token trim.
nameReference := (open , 'ref:' asParser , elementName , close) token.
integerReference := (open , 'ref:' asParser , natural trim , close) token.
reference := integerReference / nameReference.
element := PPUnresolvedParser new.
attributeValue := (primitive / reference / element) star.
attribute := (open , simpleName , attributeValue , close) trim.
id := (open , 'id:' asParser , natural trim , close) trim.
element def: ( (open , elementName , id optional , attribute star , close) trim).
elements := open , element star , close.
start := elements end.

You can test the parser with a valid MSE string:

mseString := '(
(FAMIX.Package
(name ''PackageP''))
(FAMIX.Class
(name ''ClassA''))
(FAMIX.Method
(name ''methodM''))
)'
.
start parse: mseString

A particularity of this example is the use of PPUnresolvedParser . When scripting a grammar, every production is held in a parser variable. However, in our case, we have cyclic definitions:

  • the element parser depending on the attributeValue parser (through attribute), and
  • the attributeValue parser depending on the element one.

This is the case where PPUnresolvedParser comes to the rescue. We first define one of the parser as undefined, refer to it from the second parser, and then we define the first rule using def:.

This example provides a good coverage of the various operators available in PetitParser. More detailed information about these can be found in Section 14.2.

Add a Note