14.1.4 Encapsulating the MSE grammar in a class
Scripting a parser is fast, but it can also get dirty. In fact, for any parser that has more than a handful of sub-parsers, it typically gets too dirty.
To solve the situation, PetitParser also offers the possibility of mapping a parser on a class. The idea is quite simple:
- the class must be a subclass of
PPCompositeParser
, and it holds the complete parser, - the methods of the class provide the productions, and
- the attributes of the class store the actual sub-parsers.
Let us take our example of producing an MSE grammar. We first create the class.
PPCompositeParser subclass: #ExampleMSEGrammar
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'MSEParserExample'
Afterwards, we take each production from the script and we transform it in a method. For example:
start := elements end.
gets transformed into:
start
^ elements end
This might look odd at first sight. Indeed, there is a little magic going on behind the scene. Let us take a closer look.
For every production, we create a method. In this case we create the start
method, which happens to be mandatory for telling the parser from which production it should start by default.
The odd thing is that in the method we refer directly to the elements
production via an instance variable (i.e., instead of calling a method). This happens because the PPCompositeParser uses reflection to lookup the corresponding method and to store the result in the instance variable. This leads to a better performance when the same production is used in two different places.
Given that we now refer to the elements
instance variable, we also have to create the corresponding method. To do this, we simple copy the production from the script:
elements
^ open , element star , close.
Repeating these steps for all the productions from the script, we obtain our grammar class. Afterwards, we can execute a parsing command via:
mseString := '(
(FAMIX.Package (id: 1)
(name ''PackageP''))
(FAMIX.Class (id: 2)
(name ''ClassA'')
(parentPackage (ref: 1)))
(FAMIX.Method (id: 3)
(name ''methodA'')
(declaredType (ref: 1))
(parentType (ref: 2)))
)'.
ExampleMSEGrammar parse: mseString