The compiler is based on the Antlr4 project as discussed in Chapter 3. Antlr4 gen- erates a parser for an the input file according to a grammar file [22], [73]. The input is converted into a ParseTree which can be ’walked’ to operate on the data. A regular TreeWalker as defined by Antlr4 will walk the tree in a certain order; first the child-nodes and then the node itself. Listeners that observe the TreeWalker will have user-defined enter and exit methods. These are called when a certain node is encountered. A visitor class can also walk the ParseTree but the user will have control over the order in which the children and node are visited. The visitor can also define the type that is returned visiting the children In general, they offer more control to the user.
It is also possible to override the basic TreeNode class with a user defined class. The TreeNode contains context for the current node. Overriding enables easy ac- cess to important user-defined objects such as the H-Array table and theiProctable. The class that is used to construct the Tree is called PDLParseRuleContext and it features access to the template engine, the H-Array and the encounterediProcs. A Class Diagram is shown in Fig. 4.4.
The compiler consist of a main class that reads from a file and instantiates the ParseTree along with a listener and a visitor, as can be seen in Fig. 4.3. The listener is implemented to perform semantic checks on the input. The visitor class extends the necessary methods in the base visitor class to generate the output of the com- piler. They access the stored information, or context, in the PDLParseRuleContext tree node. To illustrate: the responsibilities of checkers are handling the annota- tions in Subsection 4.1.4 and the semantics of the commands, while the generator is responsible for the operations described in Subsection 4.1.5, although there is no strict border.
4.2.1
Checking the Input
Checkers are systems that can be employed to enforce the semantics of PDL. The setup is designed to walk the tree with a standard TreeWalker object which is ob- served by different Listener objects. A Checker is such a listener and its methods are called when a certain node is entered or exited [73]. Multiple listeners can be added in the same pass so each checker can have its own responsibility.
and calling ofiProcs. Next to checking, the system also provides the generator with necessary information such as the amount of parameters and default values of the procedure. This is stored in the IProcTable which keeps a list of IProcEntry objects. The table in turn is stored statically in the context tree nodes (PDLParseRuleCon- text). When aniCall is encountered, the checker makes sure that the target pro- cedure exists, and adds any necessary default arguments to the call. The checker warns the user when a procedure is unknown or when the call is erroneous, i.e. wrong amount of arguments.
4.2.2
Generating the Output
The generator is responsible for generating the framework code based on the input program. After the ParseTree is walked and decorated by the checkers, the gen- erator takes this annotated tree and uses the information stored in it to create the output [73]. The generator is an extension of the PDLVisitor class. A visitor is used to override the order in which the tree is traversed. The use of a visitor has as downside that all code must be in the same class. This may lead to a single object responsible for all the operations of the generator, but for now the code quality re- mains manageable.
The class consist of several visit-methods as can be seen in Fig. 4.3 which override methods in the base-class. For every ParseRule in the PDL grammar a
visit<Rule Name>method is automatically generated by Antlr4 in the Visitor base-
class. The grammar has been altered to extract important information from the input stream among which the function names for example. This information is stored in the ParseTree context nodes along with the information provided by the checkers. Visit-functions gather this data and load a code-template with the template engine. The template is then filled in and returned at the end of the function. Visit-functions can visit the children of the node and in doing so receive the returned data of their visit-function. In this manner, the Pdl source root node will receive the templates from itsIproc def children, which in turn gets the rendered code from visiting any PDL command. This could be anIwrite def, which needs to parse the arguments that are given to it. It does so by visiting its children which are an instrument path and a PDL number. The instrument is searched in the H-Array based on the full path text (remember the namespacing workaround) and the PDL number is parsed and stored as an integer. This are then stored in the tree node, read by theIwrite def, put into a template and returned.
Figure 4.3: Antlr4 generated classes (in the blue arced box) are extended by a checker and generator classes.
Figure 4.4: PDLParserRuleContext contains the instrument and procedure symbol tables and is the base class for all tree nodes.
4.2.3
Template Engine
The code generator class accesses the template engine via the PDLParseRuleCon- text tree nodes. Using a template engine for any code generation is a good idea to preserve syntax and layout of the output target. A template consists of a standard piece of code with place-holders that need to be ’filled’ by the engine. This process is called rendering a template. A header and code template in C is made for every PDL command in the compiler. The generated code is returned through the tree and rendered in thePdl sourceroot node to the output files.
4.2.4
Validation of the Compiler
As stated, this research uses the grammar that is part of the IJTAG standard [5]. This grammar is correct since it is part of the standard. Before proceeding, that assumption was validated by generating a parser with Antlr and feeding this some PDL files from the Bastion benchmark set [23], [27]. The generated syntax tree was analysed by hand for correctness.
The development and addition of listeners to the parser was also checked. The same benchmark PDL file was used for this purpose. The listeners store relevant information for the compiler in the context class. The listeners also determine if an instrument exists in the H-Array. This process was also checked by hand for the benchmark file. Errors that are detected by the listeners where also checked by re- moving instruments from the H-Array and removingiProcs from the file.
Using the template engine simplifies the validation of the output; every template can be individually tested. The templates were validated checking them syntactically with stubs as render context. These renders where checked by hand and compiled. The templates are connected to the generator class. The whole code generation process is then executed to see if the PDL was correctly translated to the frame- work. This framework includes the original PDL as comment for easy reference and manual testing. The framework is compiled and applied on the DM with success.