0% found this document useful (0 votes)
168 views82 pages

Ast

Ast is A Generator for program modules that define the structure of Abstract Syntax Trees and provide general tree manipulating procedures. The generated module includes procedures to construct and destroy trees, to read and write trees from (to) files, and to traverse trees in some commonly used manners.

Uploaded by

mrangel
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
168 views82 pages

Ast

Ast is A Generator for program modules that define the structure of Abstract Syntax Trees and provide general tree manipulating procedures. The generated module includes procedures to construct and destroy trees, to read and write trees from (to) files, and to traverse trees in some commonly used manners.

Uploaded by

mrangel
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 82

Ast - A Generator for

Abstract Syntax Trees

J. Grosch

DR. JOSEF GROSCH

COCOLAB - DATENVERARBEITUNG

ACHERN, GERMANY

Cocktail 

Toolbox for Compiler Construction 

Ast - A Generator for Abstract Syntax Trees 

Josef Grosch 

Sept. 25, 2008 

Document No. 15 

Copyright © 2008 Dr. Josef Grosch 

Dr. Josef Grosch 


CoCoLab - Datenverarbeitung 
Höhenweg 6 
77855 Achern 
Germany 
Phone: +49-7841-669144 
Fax: +49-7841-669145 
Email: [email protected]
Ast 1

1. Introduction
Ast is a generator for program modules that define the structure of abstract syntax trees and
provide general tree manipulating procedures. The defined trees may be decorated with attributes of
arbitrary types. Besides trees, graphs can be handled, too. The structure of the trees is specified by a
formalism based on context-free grammars. The generated module includes procedures to construct
and destroy trees, to read and write trees from (to) files, and to traverse trees in some commonly
used manners. The mentioned readers and writers process text as well as binary tree representations.
Most procedures work for graphs as well.
The advantages of this approach are: record aggregates are provided which allow a concise
notation for node creation. It is possible to build trees by writing terms. An extension mechanism
avoids chain rules and allows, for example lists with elements of different types. Input/output proce-
dures for records and complete graphs are provided. The output procedures and the interactive
graph browser facilitate the debugging phase as they operate on a readable level and know the data
structure. The user does not have to care about algorithms for traversing graphs. He/she is freed
from the task of writing large amounts of relatively simple code. All of these features significantly
increase programmer productivity.
Ast is implemented in Modula-2 as well as in C. The tool generates C, C++, Java, or Modula-2
source code. Two versions of C++ source code can be generated which will be called simple C++
and proper C++ in this document. Simple C++ uses for the representation of the tree nodes the
same data structure as C: A union type of a set of struct types. Only one class is used whose mem-
bers are the data items which are global variables in C. Proper C++ uses class types and inheritance
for the representation of the tree nodes. Global variables are still used as in C.
The following sections define the specification language, explain the generated output, discuss
related approaches, and present some examples.

2. Specification Language
The structure of trees and directed graphs is specified by a formalism based on context-free
grammars. However, we primarily use the terminology of trees and types in defining the specifica-
tion language. Its relationship to context-free grammars is discussed later.

2.1. Node Types


A tree consists of nodes. A node may be related to other nodes in a so-called parent-child
relation. Then the first node is called a parent node and the latter nodes are called child nodes.
Nodes without a parent node are usually called root nodes, nodes without children are called leaf
nodes.
The structure and the properties of nodes are described by node types. Every node belongs to
a node type. A specification for a tree describes a finite number of node types. A node type speci-
fies the names of the child nodes and the associated node types as well as the names of the attributes
and the associated attribute types. A node type is introduced by a name which can be an identifier
or a string. The names of all node types have to be pairwise distinct. A node type can be regarded
as a nonterminal, a terminal, or an abstract entity. Nonterminals are characterized by the character
’=’ following its name, terminals by the character ’:’, and abstract node types by the characters ’:=’.
Undefined node types are implicitly defined to be terminals without attributes. The distinction
between nonterminals and terminals is only of interest if concrete syntax is described. In the case of
abstract syntax this distinction does not make sense and therefore nonterminal node types and even-
tually abstract ones suffice. Abstract node types are explained in section 2.6.
Ast 2

Example:
If = .
While = .
’()’ = .
Ident : .
":=" : .
SCOPE := .
The example defines the node types If, While, and ’()’ to be nonterminals, the node types Ident and
":=" to be terminals, and SCOPE to be an abstract node type.
The following names are reserved for keywords and can not be used for node types:
BEGIN CLOSE DECLARE DEMAND END
EVAL EXPORT FOR FUNCTION GLOBAL
IGNORE IMPORT IN INH INHERITED
INPUT LEFT LOCAL MODULE NONE
OUT OUTPUT PARSER PREC PROPERTY
REMOTE REV REVERSE RIGHT RULE
SCANNER SELECT STACK START SUBUNIT
SYN SYNTHESIZED THREAD TREE VIEW
VIRTUAL VOID

2.2. Children
Children describe subtrees. They are distinguished by selector names which have to be unam-
biguous within one node type. A child has a certain node type.
Example:
If = Expr: Expr Then: Stats Else: Stats .
While = Expr: Expr Stats: Stats .
The example introduces two node types called If and While. A node of type If has three children
which are selected by the names Expr, Then, and Else. The children have the node types Expr,
Stats, and Stats.
If a selector name is equal to the associated name of the node type it can be omitted. There-
fore, the above example can be abbreviated as follows:
If = Expr Then: Stats Else: Stats .
While = Expr Stats .

2.3. Attributes
Besides children, every node type can specify an arbitrary number of attributes of arbitrary
types. Like children, attributes are characterized by a selector name and a certain type. The descrip-
tions of attributes are enclosed in brackets. The types for attributes are given by names taken from
the target language or from the set of node types. The type of an attribute can be the general tree
pointer types tTREE and TREE, too. Missing types are assumed to be int or INTEGER depending
on the target language (C, C++, Java, or Modula-2). Children and attributes can be given in any
order.
If the type of an attribute is a node type, tTREE, or TREE, the attribute is called tree-valued,
otherwise it is not tree-valued. By default, ast does not follow a tree-valued attribute during a graph
traversal. All attributes are considered to be neither tree nor graph structured. Only the user knows
about this fact and therefore he/she should take care. Exceptions are the graph browser procedure
DrawTREE/Draw which can launch a separate browser on a subtree rooted at a tree-valued attribute
Ast 3

and the tree transformation tool puma which does type checking for tree-valued attributes. In some
cases this default treatment of tree-valued attributes can be changed in such a way that they are
treated like children (see description of the procedure ConfigureTREE/Configure).
Example (in C or C++):
Binary = Lop: Expr Rop: Expr [Operator: int] .
Unary = Expr [Operator] .
IntConst = [Value] .
RealConst = [Value: float] .

For example the node types IntConst and RealConst describe leaf nodes with an attribute
named Value of types int or float respectively. Binary and Unary are node types with an attribute
called Operator of type int.
Example (in C or C++):
Node = [a1: Binary] [a2: Tree] [a3: tTree] [a4: int] .

The attributes a1, a2, and a3 are tree-valued, while the attribute a4 is not tree-valued.

2.4. Declare Clause


The DECLARE clause allows the definition of children and attributes for several node types at
one time. Following the keyword DECLARE, a set of declarations can be given. The syntax is the
same as described above with the exception that several node type names may introduce a declara-
tion. If there already exists a declaration for a specified node type, the children and attributes are
added to this declaration. Otherwise a new node type is introduced.
Example:
DECLARE
Decls Stats Expr = [Level] [Env: tEnv] .
Expr = Code [Type: tType] .

2.5. Extensions
To allow several alternatives for the types of children an extension mechanism is used. A node
type may be associated with several other node types enclosed in angle brackets. The first node type
is called base or super type and the latter types are called derived types or subtypes. A derived type
can in turn be extended with no limitation of the nesting depth. The extension mechanism induces a
subtype relation between node types. This relation is transitive. Where a node of a certain node
type is required, either a node of this node type or a node of a subtype thereof is possible.
Example:
Stats = <
If = Expr Then: Stats Else: Stats .
While = Expr Stats .
> .

In the above example Stats is a base type describing nodes with neither children nor attributes.
It has two derived types called If and While. Where a node of type Stats is required, nodes of types
Stats, If, and While are possible. Where a node of type If is required, nodes of type If are possible,
only.
Besides extending the set of possible node types, the extension mechanism has the property of
extending the children and attributes of the nodes of the base type. The derived types possess the
Ast 4

children and attributes of the base type. They may define additional children and attributes. In
other words they inherit the structure of the base type. The selector names of all children and
attributes in an extension hierarchy have to be distinct. The syntax of extensions has been designed
this way in order to allow single inheritance, only. Multiple inheritance is available, too. It is
described in the next section.
Example:
Stats = Next: Stats [Pos: tPosition] <
If = Expr Then: Stats Else: Stats .
While = Expr Stats .
> .

Nodes of type Stats have one child selected by the name Next and one attribute named Pos.
Nodes of type While have three children with the selector names Next, Expr, and Stats and one
attribute named Pos.
A node of a base type like Stats usually does not occur in an abstract syntax tree for a complete
program. Still, ast defines this node type. It could be used as placeholder for unexpanded nontermi-
nals in incomplete programs which occur in applications like syntax directed editors.

2.6. Multiple Inheritance


The extension mechanism described in the previous section allows for single inheritance of
children and attributes. The syntax of the extensions has been designed to reflect the notation of
context-free grammars as close as possible. For multiple inheritance a different syntax and the con-
cept of abstract node types are used.
Abstract node types are characterized by the definition characters ’:=’ instead of ’=’ or ’:’
which are used for nonterminals or terminals. They are termed abstract because they describe only
the structure of nodes or parts thereof but nodes of this types do not exist. Therefore no code is gen-
erated by ast for abstract node types: no constant, no record type, no constructor procedure, etc..
Abstract node types can only be used as base types in combination with multiple inheritance.
For multiple inheritance the following syntax is used: The name of a node type may be fol-
lowed by a left arrow ’<-’ and a list of names. This construct is available for all three kinds of node
types: nonterminals, terminals, and abstract node types. The names after the left arrow have to
denote abstract node types. The meaning is that the node type inherits the structure of all listed
abstract node types. Multiple inheritance is possible from abstract node types to non abstract ones
and among abstract node types. Among non abstract node types only single inheritance is allowed.
Example:
DECLS := [Objects: tObjects THREAD] <
NODECLS := .
DECL := [Ident: tIdent INPUT] Next:DECLS .
> .
ENV := [Env: tEnv INH] .
USE <- ENV := [Ident: tIdent INPUT] [Object: tObject SYN] .
SCOPE <- ENV := [Objects: tObjects SYN] [NewEnv: tEnv SYN] .
Ast 5

Root = Proc .
Decls <- DECLS ENV = <
NoDecls = .
Decl <- DECL = <
Var = .
Proc <- SCOPE = Decls Stats .
> .
> .
Stats <- ENV = <
NoStats = .
Stat = <
Assign = Name Expr .
Call <- USE = .
> .
> .
Expr <- ENV = <
Plus = Lop: Expr Rop: Expr .
Const = [Value] .
Name <- USE = .
> .

The above example uses multiple inheritance and abstract node types to describe the identifica-
tion problem of programming languages. The node types written with all upper-case letters repre-
sent abstract node types. DECLS specifies lists of declared objects, SCOPE describes scopes or vis-
ibility regions, ENV stands for environment and is used to distribute scope information, and USE is
intended to be used at the application of identifiers. In the second part of the example, the abstract
node types are connected to nonterminal node types. Decls is the concrete node type to describe
lists of declarations. Decl represents one declaration and there are two alternatives, Var and Proc,
variables and procedures. A procedure introduces a scope and therefore it inherits from SCOPE. At
the node types Call and Name identifiers are used and thus they inherit from USE. Finally, the node
types Decls, Stats, and Expr are regions where the environment information has to be distributed
and they inherit from ENV.

2.7. Modules
The specification of node types can be grouped into an arbitrary number of modules. The
modules allow to combine parts of the specification that logically belong together. This feature can
be used to structure a specification or to extend an existing one. A module consists of target code
sections (see section 2.13.) and specifications of node types with attribute declarations. The infor-
mation given in the modules is merged in the following way: the target code sections are concate-
nated. If a node type has already been declared the given children, attributes, and subtypes are
added to the existing declaration. Otherwise a new node type is introduced. This way of modular-
ization offers several possibilities:
- Context-free grammar and attribute declarations (= node types) can be combined in one mod-
ule.
- The context-free grammar and the attribute declarations can be placed in two separate mod-
ules.
- The attribute declarations can be subdivided into several modules according to the tasks of
semantic analysis. For example, there would be modules for scope handling, type determina-
tion, and context conditions.
Ast 6

- The information can be grouped according to language concepts or nonterminals. For example,
there would be modules containing the grammar rules and the attribute declarations for decla-
rations, statements, and expressions.
Example:
MODULE my_version
Stats = [Env: tEnv] < /* add attribute */
While = Init: Stats Terminate: Stats . /* add children */
Repeat = Stats Expr . /* add node type */
> .
END my_version

2.8. Properties
The description of children and attributes can be refined by the association of so-called proper-
ties. These properties are expressed by the keywords listed in Table 1.
The properties have the following meanings: Input attributes (or children) receive a value at
node-creation time, whereas non-input attributes may receive their values at later times. Output
attributes are supposed to hold a value at the end of a node’s existence, whereas non-output
attributes may become undefined or unnecessary earlier. Synthesized and inherited describe the
kinds of attributes occurring in attribute grammars. They have no meaning for ast. The property
thread supports so-called threaded attributes: An attribute declaration [a THREAD] is equivalent to
the declaration of a pair of attributes with the suffixes In and Out: [aIn] [aOut]. These attributes
have to be accessed with their full name including the suffixes. The property reverse specifies how
lists should be reversed. It is discussed in section 2.12. The property ignore instructs ast to disre-
gard or omit an attribute or a child. It is useful in connection with the concept of views (see section
2.10.). The property virtual is meaningful in attribute grammars. It is used to describe dependencies
among attributes. However, no space will be allocated for those attributes and the attribute compu-
tations for those attributes will be omitted in the generated attribute evaluator. Within ast the prop-
erties input, reverse, and ignore are of interest, only.
Properties are specified either locally or globally. Local properties are valid for one individual
child or attribute. They are listed after the type of this item. Example:
Stats = Next: Stats IN REV [Pos: tPosition IN] [Level INH] .
Global properties are valid for all children and attributes defined in one or several modules. They
are valid in addition to the local properties that might be specified. In order to describe global

Table 1: Properties for Children and Attributes

long form short form


INPUT IN
OUTPUT OUT
SYNTHESIZED SYN
INHERITED INH
THREAD
REVERSE REV
IGNORE
VIRTUAL
Ast 7

properties, a module may contain several property clauses which are written in the following form:
PROPERTY properties [ FOR module_names ]
The listed properties become valid for the given modules. If the FOR part is missing, the properties
become valid for the module that contains the clause.
Example:
PROPERTY INPUT
PROPERTY SYN OUT FOR Mapping

Input attributes without initializer expression (see section 2.16.) are included into the parame-
ter list of the node constructor procedures (see section 3). The global property input replaces the
symbol ’->’ of former versions of ast. For compatibility reasons this symbol still works in a
restricted way: The symbol ’->’ could be included in a list of children and attributes as a shorthand
notation to separate input from non-input items. In a list without this symbol all children and
attributes are treated as input items. This meaning of the symbol ’->’ is still in effect as long as ast
does not encounter a global property clause. After encountering such a clause, local and global
properties are in effect only – the symbol ’->’ is ignored.
Example:
Stats = Next: Stats REV [Pos: tPosition] -> [Level INH] <
If = Expr Then: Stats Else: Stats .
While = -> Expr IN Stats IN .
> .
The node types of the example possess the children and attributes listed in Table 2.

Table 2: Example of Properties

node selector associated kind properties


type name type
Stats Next Stats child IN REV
Pos tPosition attribute IN
Level int attribute INH
If Next Stats child IN REV
Pos tPosition attribute IN
Level int attribute INH
Expr Expr child IN
Then Stats child IN
Else Stats child IN
While Next Stats child IN REV
Pos tPosition attribute IN
Level int attribute INH
Expr Expr child IN
Stats Stats child IN
Ast 8

2.9. Subunits
Usually, an ast specification is translated into one program module. This module receives the
name that immediately follows the keyword TREE. If several modules contain a name, the first one
is chosen. If none of the modules contains a name, the default name Tree is used. For target lan-
guages C, C++, and Modula it is possible to generate several modules out of an ast specification.
Then there is exactly one module called main unit that describes the tree structure and an arbitrary
number of modules called subunits. This is of interest if the generated source code becomes too
large for one compilation unit. Then either some or all desired procedures could be placed into sep-
arate subunits. In the extreme, there might be a subunit for every procedure. It is possible to have
two or more versions of one procedure (e. g. WriteTREE) where every one uses a different view
(see section 2.10.).
The names of the main unit and the subunits are described in the header of an ast specification:
[ TREE [ Name [ PREFIX Name ] ] ] [ SUBUNIT Name ] [ VIEW Name ]
The name after the keyword VIEW is necessary if abstract syntax trees are to be processed by pro-
gram modules generated with other tools such as e. g. puma [Groa] that need to know the definition
of the tree structure. Ast has an option that requests to write a binary version of the tree definition to
a file whose name is derived from the name given after the keyword VIEW by appending the suffix
".TS" (Default: "Tree.TS").
Every unit has to be generated by a separate run of ast. If a subunit name is present, then a
subunit is generated – otherwise a main unit is generated. The options select the procedures to be
included in the units. It is probably wise not to include the subunit name in an ast specification. If
this name is added "on the fly" with UNIX commands then different subunits can be generated from
one specification without the need to change it.
Example:
ast -dim spec.ast
echo SUBUNIT read | cat - spec.ast | ast -dir
echo SUBUNIT write | cat - spec.ast | ast -diw
or
echo TREE MyTree | cat - spec.ast | ast -dim
echo TREE MyTree SUBUNIT read | cat - spec.ast | ast -dir
echo TREE MyTree SUBUNIT write | cat - spec.ast | ast -diw

2.10. Views
An ast specification can be roughly seen as a collection of node types and associated children
and attributes. A so-called view selects a subset of this collection and it may attach further proper-
ties to some parts of this collection. In the current version of ast, views are not available if the target
language is Java.
The concept of views is necessary for instance if two programs communicate a common data
structure via a file. Every program might need additional data which should neither appear in the
other program nor in the file. In order to make this work both programs must agree upon the coding
of the node types in the shared part of the data structure. This is accomplished by using one com-
mon ast specification that contains the description of the complete data structure. Every program
uses a specific view and selects only those parts of the common specification that are of interest. See
Figure 1 for an example.
Another need for views arises if several attribute evaluators operate one after the other on one
tree. The output attributes of a preceding evaluator become the input attributes of a succeeding one.
Ast 9

specification
file: spec.ast
MODULE A
data specific
to program A
MODULE B
data specific
to program B
MODULE C
common data

program A program B

main unit Tree main unit Tree


generated generated
from A and C from B and C
subunit PutTree subunit GetTree
generated file generated
from C from C

other modules other modules

UNIX commands to generate the compilation units:


echo SELECT A C | cat - spec.ast | ast -dim
echo SUBUNIT PutTree SELECT C | cat - spec.ast | ast -dip
echo SELECT B C | cat - spec.ast | ast -dim
echo SUBUNIT GetTree SELECT C | cat - spec.ast | ast -dig

Fig. 1: Programs Sharing a Part of a Data Structure

Here it is necessary to be able to change the properties of attributes. In one view the attributes are
regarded as output and in the other one they are regarded as input. The usage of views for the speci-
fication of several attribute evaluators is described in [Grob].
Furthermore, views are necessary if abstract syntax trees are to be processed by program mod-
ules generated with other tools such as e. g. puma [Groa] that need to know the definition of the
tree structure. In general, there might be several tree processing modules and every one uses a dif-
ferent view. In this case, the views have to be communicated to the other tool in a file. ast has an
option that requests to write a binary version of the tree definition to a file whose name is derived
from the name given after the keyword VIEW by appending the suffix ".TS" (Default: "Tree.TS",
see section on "Subunits").
The concept of views is based on the global properties:
Ast 10

PROPERTY properties FOR module_names


allows the dynamic addition of properties.
PROPERTY IGNORE FOR module_names
allows the suppression of all definitions given in the listed modules. Additionally there is the so-
called select clause:
SELECT module_names
This is equivalent to:
PROPERTY IGNORE FOR module_names_not_given

It is wise to assign names to all modules of an ast specification, because otherwise they can not
be selected with the select clause. Furthermore, the property or select clauses that express views
should probably not be included in the file containing the ast specification. The reason is that this
form is not flexible, because it is relatively hard to change. It is better to add the one line that is nec-
essary for views "on the fly" using UNIX commands like echo and cat (see Figure 1).

2.11. Prefixing
A prefix can be added to the generated names that are derived from the names of the node
types. This mechanism for prefixing is available for the languages C and simple C++. An ast speci-
fication can describe a prefix after the keyword PREFIX in addition to the name for a tree specifica-
tion using the following syntax:
[ TREE [ Name [ PREFIX Name ] ] ]
The prefix is added to the following kinds of generated names:
generated name description
k<prefix><node type> named constant to encode a node type in C and C++
<prefix><node type> name of union member for a node type
n<prefix><node type> node constructor procedures with attribute initialization
according to the type specific operations or an initializer expression
m<prefix><node type> node constructor procedures with attribute initialization
from a parameter list for input attributes without initializer expression

Example:
TREE sql_tree PREFIX sql_ RULE
stmt = <
declare_stmt = ... .
select_stmt = ... .
> .

The generated code will include the following declarations:


// named constants
# define ksql_declare_stmt 2
# define ksql_select_stmt 3
// constructor procedures
extern tTree msql_declare_stmt (...);
extern tTree msql_select_stmt (...);
Ast 11

Note, the tools ag and puma are aware of the prefix. Therefore, in the specifications for these
tools the node types are written without prefix. The prefix is generated automatically by these tools.

2.12. Reversal of Lists


Recursive node types like Stats in the abstract grammar of the example below describe lists of
subtrees. There are at least two cases where it is convenient to be able to easily reverse the order of
the subtrees in a list. The facility provided by ast is a generalization of an idea presented in [Par88].

2.12.1. LR Parsers
Using LR parsers, one might be forced to parse a list using a left-recursive concrete grammar
rule because of the limited stack size. The concrete grammar rules of the following examples are
written in the input language of the parser generator Lark [Groc] which is similar to the one of Yacc
[Joh75]. The node constructor procedures within the semantic actions are the ones provided by ast
(see section 3).
Example:
concrete grammar (with tree building actions):
Stats: {$$ = mStats0 (); } .
Stats: Stats Stat ’;’ {$$ = mStats1 ($2, $1);} .
Stat : WHILE Expr DO Stats END {$$ = mWhile ($2, $4);} .

abstract grammar:
Stats = <
Stats0 = .
Stats1 = Stat Stats .
> .
A parser using the above concrete grammar would construct statement lists where the list elements
are in the wrong order, because the last statement in the source would be the first one in the list. The
WHILE rule represents a location where statement lists are used.
To easily solve this problem ast can generate a procedure to reverse lists. The specification has
to describe how this should be done. At most one child of every node type may be given the prop-
erty reverse. The child’s type has to be the node type itself or an associated base type. The gener-
ated list reversal procedure ReverseTREE then reverses a list with respect to this indicated child.
The procedure ReverseTREE has to be called exactly once for a list to be reversed. This is the case
at the location where a complete list is included as subtree (e. g. in a WHILE statement).
Example:
concrete grammar (with tree building actions):
Stats: {$$ = mStats0 (); } .
Stats: Stats Stat ’;’ {$$ = mStats1 ($2, $1);} .
Stat : WHILE Expr DO Stats END {$$ = mWhile ($2, ReverseTREE ($4));} .

abstract grammar:
Stats = <
Stats0 = .
Stats1 = Stat Stats REVERSE .
> .
Ast 12

It is possible to represent lists differently in an abstract syntax. A more sophisticated solution


is given in the next example. The procedure ReverseTREE handles this variant, too.
Example:
concrete grammar (with tree building actions):
Stats: {$$ = mNoStat ();} .
Stats: Stats IF Expr THEN Stats ELSE Stats END ’;’
{$$ = mIf ($3, ReverseTREE ($5), ReverseTREE ($7), $1);} .
Stats: Stats WHILE Expr DO Stats END ’;’
{$$ = mWhile ($3, ReverseTREE ($5), $1);} .

abstract grammar:
Stats = <
NoStat = .
Stat = Next: Stats REVERSE <
If = Expr Then: Stats Else: Stats .
While = Expr Stats .
> .
> .

2.12.2. LL Parsers
Using LL parsers a similar problem as in the LR case can arise if extended BNF is used. Lists
are parsed with an iteration which is turned into a loop statement as follows: (The identifiers Stats0,
Stats1, Stat0, and Stat1 in the concrete grammar rules denote the symbolic access to the L-attribu-
tion mechanism provided by Ell [GrV]. These identifiers should not be mixed up with the similar
ones used as node names in the abstract syntax.)
Example:
concrete grammar (with tree building actions):
Stats: {*Stats0=mStats0 ();} ( Stat ’;’ {*Stats0=mStats1 (Stat1, Stats0);} ) * .
Stat : WHILE Expr DO Stats END {*Stat0=mWhile (Expr1, ReverseTREE (Stats1));} .

abstract grammar:
Stats = <
Stats0 = .
Stats1 = Stat Stats REVERSE .
> .
The list elements (statements) are inserted in the wrong order within the first concrete grammar
rule. The order is corrected by a call of the procedure ReverseTREE in the second concrete gram-
mar rule.

2.13. Target Code


An ast specification may include several sections containing so-called target code. These sec-
tions follow the keywords TREE or SUBUNIT. Target code is code written in the target language
which is copied unchecked and unchanged to certain places in the generated module. It has to be
enclosed in braces ’{’ ’}’. Balanced braces within the target code are allowed. Unbalanced braces
have to be escaped by a preceding ’\’ character. In general, the escape character ’\’ escapes every-
thing within target code. Therefore, especially the escape character itself has to be escaped.
Ast 13

The meaning of the sections is as follows:


IMPORT: references to other target language modules, for example to provide definitions of
types for attribute declarations.
EXPORT: declarations to be included in addition to the declaration of the tree type.
GLOBAL: declarations to be included in the implementation module at global level. This is
where to put macro definitions to select code generation options.
LOCAL: same as GLOBAL within ast for C and Modula. For C++ see below.
BEGIN: initialization statements.
CLOSE: finalization statements.
The exact use of these sections varies according to the target language, as discussed below.

2.13.1. C and C++


The meaning of the sections is as follows:
IMPORT: #include directives or typedef declarations to be included in the header file.
EXPORT: declarations to be included in the header file after the declaration of the tree type
tTREE.
GLOBAL: declarations to be included in the implementation module at global level.
LOCAL: In C: the same as GLOBAL.
In simple C++: member declarations to be included in the class declaration.
In proper C++: declarations to be included at namespace level.
BEGIN: statements to initialize the declared data structures.
CLOSE: statements to finalize the declared data structures.
Example in C or C++:
TREE SyntaxTree
IMPORT {# include "Idents.h" }
EXPORT { typedef tSyntaxTree MyTree; }
GLOBAL {# include "Idents.h"
typedef struct { unsigned Line, Column; } tPosition; }
BEGIN { ... }
CLOSE { ... }
Ast 14

2.13.2. Modula-2
The meaning of the sections is as follows:
IMPORT: declarations to be included in the definition module at a place where IMPORT
statements are legal.
EXPORT: declarations to be included in the definition module after the declaration of the
tree type tTREE.
GLOBAL: declarations to be included in the implementation module at global level.
BEGIN: statements to initialize the declared data structures.
CLOSE: statements to finalize the declared data structures.
Example in Modula-2:
TREE SyntaxTree
IMPORT { FROM Idents IMPORT tIdent; }
EXPORT { TYPE MyTree = tSyntaxTree; }
GLOBAL { FROM Idents IMPORT tIdent;
TYPE tPosition = RECORD Line, Column: CARDINAL; END; }
BEGIN { ... }
CLOSE { ... }

2.13.3. Java
The meaning of the sections is as follows:
IMPORT: If no IMPORT sections are present ast includes import statements for classes in
the reuse package. If an IMPORT section is present then a statement equivalent to
the following should be included:
import de.cocolab.reuse.*;

EXPORT: additional members to be included in the TREE class.


GLOBAL: macro definitions to select code generation options.
BEGIN: statements to be executed when the TREE class is loaded.
CLOSE: statements to be executed when the TREE.close () method is called.
Example in Java:
TREE SyntaxTree
IMPORT { import de.cocolab.reuse.*;
import com.package.*; }
EXPORT { public int member; }
GLOBAL { # define readMyType(a) ... }
BEGIN { ... }
CLOSE { ... }

2.14. Common Node Fields


Sometimes it is desirable to include certain fields into all node types. For languages except
Java this can be done directly by defining the macro TREE_NodeHead in the IMPORT or EXPORT
target code sections. For Java, use the GLOBAL target code section. In C, simple C++, and Modula
these fields become members of the union member or variant named yyHead. In proper C++ and
Java these fields become members of the common super class. They can be accessed as shown in
the following examples:
Ast 15

Example in C or simple C++:


# define Tree_NodeHead int MyField1; MyType MyField2;
t->yyHead.MyField1 = ... ;

Example in proper C++:


# define Tree_NodeHead int MyField1; MyType MyField2;
t->MyField1 = ... ;

Example in Modula-2:
# define Tree_NodeHead MyField1: INTEGER; MyField2: MyType;
tˆ.yyHead.MyField1 := ... ;

Example in Java:
# define Tree_NodeHead public int MyField1; public MyType MyField2;
t.MyField1 = ... ;

Macros may be defined to process these additional fields during tree operations (see Table 3).
These macros are fully implemented only for C, C++, and Java. They are similar to the type spe-
cific operations described in the next section except that the argument is a reference to the node
rather than a reference to an individual attribute.
Table 3: Node Head Operations

operation macro name purpose


initialization beginNodeHead(a) initialize values during node creation
finalization closeNodeHead(a) finalize values at node destruction
text read readNodeHead(a) read values
text write writeNodeHead(a) write values
binary read getNodeHead(a) get values
binary write putNodeHead(a) put values
copy copyNodeHead(a, b) copy values
equal equalNodeHead(a, b) test whether values are equal
XML output writeXMLNodeHead(a) write values in XML format 
Note: In Java, the function of binary read/write is achieved via the serialization facility.

2.15. Type Specific Operations


Procedures generated by ast apply several operations to attributes: initialization, finalization,
text read and write, binary read and write, copy, test for equality, and XML output (see Table 4). 
Initialization is performed whenever a node is created. It can range from assigning an initial value
to the allocation of dynamic storage or the construction of complex data structures. Finalization is
performed immediately before a node is deleted and may e. g. release dynamically allocated space.
The read and write operations enable the readers and writers to handle the complete nodes including
all attributes, even those of user-defined types. The operation copy is needed in order to duplicate
values of attributes of user-defined types. By default, ast just copies the bytes of an attribute in order
to duplicate it. Therefore, pointer semantics is assumed for attributes of a pointer type. If value
semantics is needed, the user has to take care about this operation. The operation equal checks
Ast 16

whether two attributes are equal. It is used as atomic operation for the procedure that tests the equal-
ity of trees. The operation writeXML is used for XML output of attributes. 
The operations are type specific in the sense that every type has its own set of operations. All
attributes having the same type (target type name) are treated in the same way. If the target language
permits type aliases, choosing different type names for one type introduces subtypes and allows to
treat attributes of different subtypes differently. Type specific operations for the predefined types of
a target language and for the types defined in the library reuse [Groa, Grob, Nas] are predefined
within ast (see Appendices 9 to 13). For user-defined types, ast assumes default operations (see Ta-
ble 4). The procedures yyReadHex and yyWriteHex read and write the bytes of an attribute as hex-
adecimal values. The procedures yyGet and yyPut read and write the bytes of an attribute
unchanged (without conversion). The operations are defined by a macro mechanism. The proce-
dure yyIsEqual checks the bytes of two attributes for equality. TYPE is replaced by the concrete
type name. a is a formal macro parameter referring to the attribute. The predefined procedures
mentioned in Table 4 use the global variable yyf of type FILE * (C, C++) [Grob] or IO.tFile (Mod-
ula-2) [Groa] or the static variables yyin of type de.cocolab.reuse.CocktailReader and yyout of type
de.cocolab.reuse.CocktailWriter (Java) [Nas] describing the file used by the readers and writers.
Tree-valued attributes are always of type tTREE in C, C++ and Modula-2 and so the same set
of operations is used regardless of whether the attribute is declared to be of type tTREE or one of
the node types. In Java there is a separate set of operations for each node type.

Table 4: Type Specific Operations

operation macro name language default macro


initialization beginTYPE(a)
finalization closeTYPE(a)
text read readTYPE(a) C or C++ yyReadHex (& a, sizeof (a));
Modula-2 yyReadHex (a);
Java a = new TYPE (yyin.readL ());
text write writeTYPE(a) C or C++ yyWriteHex (& a, sizeof (a));
Modula-2 yyWriteHex (a);
Java yyout.write (a.toString ());
binary read getTYPE(a) C or C++ yyGet (& a, sizeof (a));
Modula-2 yyGet (a);
Java see note
binary write putTYPE(a) C or C++ yyPut (& a, sizeof (a));
Modula-2 yyPut (a);
Java see note
copy copyTYPE(a, b) Java a = (b);
equal equalTYPE(a, b) C or C++ memcmp (& a, & b, sizeof (a)) == 0
Modula-2 yyIsEqual (a, b)
Java (a.equals (b))
XML output writeXMLTYPE(a) C or C++ yyWriteHex (& a, sizeof (a)); 
Modula-2 yyWriteHex (a); 
Java 
Note: In Java, the function of binary read/write is achieved via the serialization facility.
Ast 17

It is possible to redefine the operations by including new macro definitions in the GLOBAL
section. The following example demonstrates the syntax for doing this. It shows how records of the
type tPosition might be handled and how subtypes can be used to initialize attributes of the same
type differently. Recall that ast already knows how to handle tPosition – what follows is only a
demonstration.
Example in C or C++:
IMPORT {
# include "Sets.h"
typedef struct { unsigned Line, Column; } tPosition;
typedef tSet Set100;
typedef tSet Set1000;
}
GLOBAL {
# define begintPosition(a) a.Line = 0; a.Column = 0;
# define readtPosition(a) fscanf (yyf, "%d,%d", & a.Line, & a.Column);
# define writetPosition(a) fprintf (yyf, "%d, %d", a.Line, a.Column);
# define beginSet100(a) MakeSet (& a, 100);
# define closeSet100(a) ReleaseSet (& a);
# define readSet100(a) ReadSet (yyf, & a);
# define writeSet100(a) WriteSet (yyf, & a);
# define beginSet1000(a) MakeSet (& a, 1000);
# define closeSet1000(a) ReleaseSet (& a);
# define readSet1000(a) ReadSet (yyf, & a);
# define writeSet1000(a) WriteSet (yyf, & a);
}

Example in Modula-2:
IMPORT {
FROM Sets IMPORT tSet;
TYPE tPosition = RECORD Line, Column: CARDINAL; END;
TYPE Set100 = tSet;
TYPE Set1000 = tSet;
}
GLOBAL {
FROM IO IMPORT ReadI, WriteI, WriteC;
FROM Sets IMPORT MakeSet, ReleaseSet, ReadSet, WriteSet;
# define begintPosition(a) a.Line := 0; a.Column := 0;
# define readtPosition(a) a.Line := ReadI (yyf); a.Column := ReadI (yyf);
# define writetPosition(a) WriteI (yyf, a.Line, 0); WriteC (yyf, ’ ’); \
WriteI (yyf, a.Column, 0);
# define beginSet100(a) MakeSet (a, 100);
# define closeSet100(a) ReleaseSet (a);
# define readSet100(a) ReadSet (yyf, a);
# define writeSet100(a) WriteSet (yyf, a);
# define beginSet1000(a) MakeSet (a, 1000);
# define closeSet1000(a) ReleaseSet (a);
# define readSet1000(a) ReadSet (yyf, a);
# define writeSet1000(a) WriteSet (yyf, a);
}
Ast 18

Example in Java:
IMPORT {
import de.cocolab.reuse.*; // including Set and Position
// The C preprocessor is used within ast, so we can define
// type aliases like this:
# define Set100 Set
# define Set1000 Set
}
GLOBAL {
// Note in Java we have the type "Position" rather than "tPosition".
# define beginPosition(a) a = new Position (0, 0);
# define readPosition(a) a.line = yyin.readI (); a.column = yyin.readI ();
# define writePosition(a) yyout.write (a.line); yyout.write (’ ’); \
yyout.write (a.column);
# define beginSet100(a) a = new Set (100);
# define closeSet100(a) a = null;
# define readSet100(a) a = readSet (yyin);
# define writeSet100(a) writeSet (yyout, a);
# define beginSet1000(a) a = new Set (1000);
# define closeSet1000(a) a = null;
# define readSet1000(a) a = readSet (yyin);
# define writeSet1000(a) writeSet (yyout, a);
}
EXPORT {
private Set readSet (CocktailReader in) {
// code to read a set
}
private void writeSet (CocktailWriter out, Set s) {
// code to write a set
}
}

In the case of C++ the modules Idents and StringM of the library reuse [Grob] are imple-
mented as classes. Thus several objects might be created from these classes. If attributes are
declared with the types tIdent or tStringRef then ast will generate code that uses some methods of
these classes. Now it has to be described to which of the objects a method call refers to. This is
handled in the following way. The generated code looks like this:
# ifndef Idents_PREFIX
# include "Global.h"
# define Idents_PREFIX gIdents.
# endif
# ifndef String_PREFIX
# include "Global.h"
# define String_PREFIX gStringM.
# endif
Idents_PREFIX NoIdent;
Idents_PREFIX MakeIdent (...);
Idents_PREFIX WriteIdent (...);
String_PREFIX PutString (...);
String_PREFIX WriteString (...);
Ast 19

By default, the globally created objects gIdents and gStringM from the library reuse are used. This
can be changed by providing definitions for the macros Idents_PREFIX and String_PREFIX in the
GLOBAL section. Example:
# include "Idents.h"
# include "StringM.h"
# define Idents_PREFIX my_idents_object.
# define String_PREFIX my_string_object->
Idents my_idents_object;
StringM * my_string_object = new StringM;

In the case of Java there is a similar consideration with respect to the Ident type and the associated
IdentTable class [Nas]. The generated code looks like this:
# ifndef Idents_PREFIX
# define Idents_PREFIX Global.idents.
# endif
Idents_PREFIX makeIdent (...);
The prefix is only needed when calling makeIdent to create an new Ident instance. All other opera-
tions are simply methods on an Ident object. By default, the statically created object idents from the
library class Global is used. This can be changed by providing a definition for the macro
Idents_PREFIX in the GLOBAL section. Example:
GLOBAL {
# define Idents_PREFIX my_idents_object.
}
EXPORT {
static IdentTable my_idents_object = new IdentTable (100);
}

2.16. Attribute Initialization


An initial value for an attribute can be specified by a type specific operation as described in the
previous chapter or by an attribute specific initializer expression. The latter is described by the fol-
lowing notation:
[ Name : Type := Expression ]
The expression is written in the syntax of the implementation language. It is evaluated at node cre-
ation time. Its value is assigned as initial value to the declared attribute. An attribute specific initial-
izer takes precedence over a type specific initialization. An attribute specific initializer implies the
property INPUT for this attribute. However, this attribute is not included in the list of parameters of
the constructor procedure.
Example:
[ count : int := 0 ]

2.17. Storage Management


The storage management for the nodes to be created is completely automatic, by default. Usu-
ally, the user does not have to care about it. For Java the operation new is used for node allocation,
deallocation is provided by the garbage collector. For proper C++ the operation new is used for
node allocation, too, deallocation has to be done by explicit calls of the function ReleaseTREE
which is based on the operation delete. For the languages C, simple C++, and Modula the
Ast 20

predefined storage management works as follows: Every generated tree module contains its own
heap manager which is designed in favour of speed. The constructor procedures use an in-line code
sequence to obtain storage (see below). The heap manager does not maintain free lists. It only
allows to free the complete heap of one module using the procedure ReleaseTREEModule. The pro-
cedure ReleaseTREE does not free the node space (except for proper C++), it only finalizes the
attributes of the nodes. The predefined behaviour can be changed by including two macro defini-
tions in the GLOBAL section.
For C the macros are initialized as follows:
# define yyALLOC(size1, size2) \
(TREE_PoolFreePtr -= size1) >= TREE_PoolStartPtr ? \
(tTREE) TREE_PoolFreePtr : TREE_Alloc (size2)
# define yyFREE(ptr, size)
For simple C++ the macros are initialized as follows:
# define yyALLOC(size1, size2) yyALLOCi (size1, size2)
# define yyFREE(ptr, size)
inline tTREE TREE::yyALLOCi (unsigned long yysize1, unsigned long yysize2)
{ return TREE_PoolFreePtr >= TREE_PoolStartPtr + yysize1 ?
(tTREE) (TREE_PoolFreePtr -= yysize1) : TREE_Alloc ((unsigned short) yysize2); }
For Modula-2 the macros are initialized as follows:
# define yyALLOC(ptr, size) ptr := yyPoolFreePtr; \
IF SYSTEM.ADDRESS (ptr) >= yyPoolMaxPtr THEN ptr := yyAlloc (); END; \
INC (yyPoolFreePtr, size);
# define yyFREE(ptr, size)
The following lines switch the heap manager to a global storage allocator with free lists:
# define yyALLOC(size1, size2) (tTREE) Alloc (size2)
# define yyFREE(ptr, size) Free (size, ptr);
Now ReleaseTREE will work as expected whereas ReleaseTREEModule does not work any more.
In the cases of C and simple C++, the macro yyALLOC has two size arguments: While ’size2’
specifies the number of bytes needed for the creation of a tree node, ’size1’ specifies the corre-
sponding aligned size. The aligned size ’size1’ might be increased in comparison to ’size2’ in order
to fullfill the alignment requirements of the current machine. yyALLOC has to define an expression
which yields a pointer to a storage area of at least ’size2’ bytes. The macro yyFREE is used to indi-
cate that ’size’ bytes at the location ’ptr’ can be released.

3. About the Generated Program Module


Ast is able to generate code for two types of target languages: procedural and object-oriented.
For the procedural languages C and Modula-2 node types are mapped to members of a variant or
union type, and the generated prodedures take an argument of this type. Procedure names are of the
form OperationTREE where TREE is replaced by the tree module name, default Tree. The map-
ping for simple C++ is similar to the C mapping, and so it is described under the heading "Procedu-
ral Mapping".
For the object-oriented languages proper C++ and Java node types are mapped to classes and
in general the procedures are methods applied to an instance and take no tree argument. Method
names are of the form Operation or operation. As the names are within a name scope there is no
need to qualify them with the name of the tree module.
Though the two schemes have much in common, there is enough difference to warrant separate
descriptions.
Ast 21

3.1. Procedural Mapping


A specification is translated by ast into a program module consisting of a definition part and an
implementation part. Only the definition part or header file respectively is sketched here – the
Appendices 4, 5, and 7 contain the general schemes. The definition part contains primarily type
declarations describing the structure of the tree nodes and the headings of the generated procedures.
Every non-abstract node type is turned into a constant declaration and a struct or record decla-
ration. That is quite simple, because node types and record declarations are almost the same con-
cepts except for the extension mechanism and some shorthand notations. All these records become
members of a union type or a variant record used to describe tree nodes in general. This variant
record has a tag field called Kind which stores the code of the node type. A pointer to the variant
record is a type representing trees. Like all generated names, this pointer type is derived from the
name of the specification. Table 5 briefly explains the exported objects (replace TREE by the name
of the generated module (see section 2.9.) and <node type> by all the names of node types). In
Modula-2 the names of the constants to code the node types and the names of the record variants are
identical to the names of the node types. In other languages only the names of the union members
are identical, the constant names are prefixed with the letter ’k’ standing for Kind.
In simple C++ a class named TREE is generated, additionally. All exported variables and pro-
cedures as well as the internal data structure are declared as members of this class. Thus it is possi-
ble to create and delete several tree objects which are independent from each other. This allows for a
detailed control of memory usage, for example. In proper C++ a class named yyTREE is generated
which serves as a base class for all node types.
The parameters of the procedures m<nodetype> have to be given in the order of the input
attributes without initializer expression in the specification. Attributes of the base type (recursively)
precede the ones of the derived type. The procedure ForallTREE allows for the execution of a pro-
cedure given as parameter for every element (node) of a list. Lists are indicated by the property
REVERSE - the child with this property is the pointer to the succeeding list element. The proce-
dures TraverseTREETD and TraverseTREEBU visit all nodes of a tree or a graph respectively. At
every node a procedure given as parameter is executed. An assignment of a tree or graph to a vari-
able of type tTREE can be done in two ways: The usual assignment operators ’=’ or ’:=’ yield
pointer semantics. The procedure CopyTREE yields value semantics by duplicating a given graph.
The procedure IsEqualTREE is used to test structural equivalence of trees. It is not designed to han-
dle general graphs.
The construction of the pointer and the union type above does not enforce the tree typing rules
through the types of the target language. In fact, it is possible to construct trees that violate the spec-
ification. The user is responsible to adhere to the type rules. Most of the generated procedures do
not care about the type rules. Moreover, type violations are possible and such erroneous trees are
handled correctly by all procedures. The procedure CheckTREE can be used to check if a tree is
properly typed. In case of typing errors the involved parent and child nodes are printed on standard
error.
The binary graph writer procedure PutTREE produces a binary file containing the graph in lin-
earized form. The nodes are written according to a depth first traversal. Edges are either represented
by concatenation of nodes or by symbolic (integer) labels. The following kinds of records specified
by C types are written to the output file:
Ast 22

Table 5: Generated Objects and Procedures (Class Members)

object/procedure description
k<node type> named constant to encode a node type in C and C++
<node type> named constant to encode a node type in Modula-2
<node type> name of union member or record variant for a node type
tTREE pointer type, refers to variant record type describing all node types
TREERoot variable of type tTREE, can serve as tree root
TREE_NodeName array mapping node types to names (strings) in C and C++
MakeTREE node constructor procedure without attribute initialization
TREE_IsType test a node for a certain type
n<node type> node constructor procedures with attribute initialization
according to the type specific operations or an initializer expression
m<node type> node constructor procedures with attribute initialization
from a parameter list for input attributes without initializer expression
ReleaseTREE node or graph finalization procedure,
all attributes are finalized, all node space can be deallocated
ReleaseTREEModule deallocation of all graphs managed by a module
WriteTREENode text node writer procedure
ReadTREE text graph reader procedure
WriteTREE text graph writer procedure
WriteTREEXML XML graph writer procedure
GetTREE binary graph reader procedure
PutTREE binary graph writer procedure
TraverseTREETD top down graph traversal procedure (reverse depth first)
TraverseTREEBU bottom up graph traversal procedure (depth first search)
ReverseTREE procedure to reverse lists
ForallTREE list traversal procedure
CopyTREE graph copy procedure
IsEqualTREE equality test procedure for trees
CheckTREE graph syntax checker procedure
QueryTREE graph browser procedure with text user interface
DrawTREE graph browser procedure with graphic user interface
ConfigureTREE procedure to configure some properties of the tree module
BeginTREE procedure to initialize user-defined data structures
CloseTREE procedure to finalize user-defined data structures
Ast 23

# define yyNil 0374


# define yyNoLabel 0375
# define yyLabelDef 0376
# define yyLabelUse 0377
typedef unsigned char TREE_tKind; /* less than 252 node types */
typedef unsigned short TREE_tKind; /* more than 251 node types */
typedef unsigned short TREE_tLabel;
struct { char yyNil ; } NoTree;
struct { char yyLabelUse; TREE_tLabel <label>; } LabelUse;
struct { char yyLabelDef; TREE_tLabel <label>;
TREE_tKind Kind; <attributes> } LabelDef;
struct { char yyNoLabel ; TREE_tKind Kind; <attributes> } NoLabel;
struct { char Kind ; <attributes> } Kind;
Record fields whose name starts with yy have a constant value as defined. <label> is an integer rep-
resenting a certain address. <attributes> are written with the type specific put macros which either
copy the bytes of an attribute unchanged or do whatever the user has specified. If the value of the
tag field Kind is less than 252 then the format Kind is used, otherwise the format NoLabel is used to
write unlabeled nodes.
Graphs that have been written to a file with the procedure PutTREE can be read from file into
memory with the procedure GetTREE.
The procedures WriteTREE and ReadTREE perform input and output of graphs similar to the
procedures PutTREE and GetTREE. The differences are that a human readable text notation is used
which causes the files to become considerably larger. The procedure WriteTREEXML performs out-
put of a graph in XML format.
The procedure ConfigureTREE allows the configuration of some parameters of the tree mod-
ule. It has two arguments of type string. The first argument describes the parameter and the second
argument gives its value. Table 6 describes the possible parameters, the possible values, and the cor-
responding default values.
By default the procedure CheckTREE complains about children having the value NoTREE. If
the parameter CheckReportNoTree is set to 0 then CheckTREE will allow children having the value
NoTREE. Also, by default the procedure CheckTREE will output nodes that violate the typing rules
using the procedure WriteTREENode. This can be switched off by setting the parameter CheckRe-
portNodes to 0.

Table 6: Parameters for the procedure ConfigureTREE/Configure

Parameter Value Default


CheckReportNoTree 0/1 1 = true
CheckReportNodes 0/1 1 = true
DrawBoxHeight n 20 pixels
DrawBoxWidth n 60 pixels
DrawDepth n 6 nodes
DrawLength n 256 nodes
DrawFileName filename
TreatTVAasChild 0/1 0 = false
Ast 24

The parameters whose names start with the prefix Draw control some aspects of the graphical
tree browser. The parameters DrawBoxHeight and DrawBoxWidth control the sizes of the rectan-
gles denoting tree nodes. The horizontal and vertical distances between the nodes as well as the
number of chatacters used to label a node are computed automatically depending on the size of a
rectangle. The parameter DrawDepth controls to which depth tree nodes should be displayed. The
parameter DrawLength controls to which length nodes in a list should be displayed. The parameter
DrawFileName can be used to specify the name of the source file for a tree or graph.
By default, tree-valued attributes are treated like normal attributes: They are not traversed dur-
ing any tree-traversing operations. By setting the parameter TreatTVAasChild to 1 tree-valued
attributes are treated like children in some of the I/O procedures: They are traversed and processed
by the procedures ReadTREE, WriteTREE, GetTREE, and PutTREE. The support for changing the
treatment of tree-valued attributes during runtime is provided by source code which is compiled
conditionally. If this support for tree-valued attributes is needed it has to be enabled by defining the
preprocessor symbol SUPPORT_TVA. This can be done either using a compiler option (-DSUP-
PORT_TVA, /DSUPPORT_TVA) or by including a directive in the IMPORT section:
# define SUPPORT_TVA

3.2. Object-Oriented Mapping


A specification is translated by ast into a program module consisting of a class declaration for
every non-abstract node type. Only the public interfaces are sketched here – the Appendices 6 and
8 contain the general schemes.
Every non-abstract node type is turned into a constant declaration and a class declaration.
That is quite simple, because node types and classes are the same concepts as are the extension
mechanism and single inheritance (abstract node types are not mapped to classes and so we do not
need multiple inheritance in the target language). All these classes have a common base class used
to describe tree nodes in general. In proper C++, this class has a member called Kind which holds
the code of the node type. In Java, this class has a method called yyKind which returns the code of
the node type. A reference to the base class is a type representing trees. Like all generated names,
this class type is derived from the name of the specification. Table 7 briefly explains the exported
objects (replace TREE by the name of the generated module (see section 2.9.) and <node type> by
all the names of node types). The names of the node classes are the same as in the specification, the
constant names are prefixed with the letter ’k’ standing for Kind.
The parameters of the constructors have to be given in the order of the input attributes without
initializer expression in the specification. Attributes of the base type (recursively) precede the ones
of the derived type. The procedure Forall/forall allows for the execution of a procedure given as
parameter for every element (node) of a list. Lists are indicated by the property REVERSE - the
child with this property is the reference to the succeeding list element. The procedures Traver-
seTD/traverseTD and TraverseBU/traverseBU visit all nodes of a tree or a graph respectively. At
every node a procedure given as parameter is executed.
In C++, procedure parameters are implemented by function pointers. In Java, procedure
parameters are implemented via an interface TREE.ProcTree which has a single method proc
(TREE yyt). This method has no ‘throws’ clause, and so it is necessary to catch all exceptions that
might be thrown by the code within the user procedure. If it is desired to retain the default action
for exceptions thrown within such procedures then the following scheme may be used to ’hide’ the
exception from the ProcTree interface.
Ast 25

Table 7: Generated Objects and Class Members

object/function/method description
C++ Java
k<node type> k<node type> named constant to encode a node type
<node type> <node type> name of class for a node type
yyTREE TREE class type, a supertype of all node types
tp<node type> - pointer type for every node class
tTREE - pointer type, refers to super class yyTREE
Root - variable of type tTREE, can serve as tree root
NodeName NodeNames array mapping node types to names (strings)
Make make node constructor procedure without attribute initialization
IsType isType test a node for a certain type
Release - node or graph finalization procedure,
all attributes are finalized, all node space is deallocated
<node type> () <node type> () node constructor procedures with attribute initialization
according to type specific operations or initializer expression
<node type> (...) <node type> (...) node constructor procedures with attribute initialization
from a parameter list for input attributes w/o initializer expr.
WriteNode writeNode text node writer procedure
Read read text graph reader procedure
Write write text graph writer procedure
WriteXML - XML graph writer procedure
Get get binary graph reader procedure
Put put binary graph writer procedure
TraverseTD traverseTD top down graph traversal procedure (reverse depth first)
TraverseBU traverseBU bottom up graph traversal procedure (depth first search)
Reverse reverse procedure to reverse lists
Forall forall list traversal procedure
Copy copy graph copy procedure
clone same as copy in Java
IsEqual isEqual equality test procedure for trees
Check check graph syntax checker procedure
Query query graph browser procedure with text user interface
Draw - graph browser procedure with graphic user interface
Configure - procedure to configure some properties of the tree module
Begin begin procedure to initialize user-defined data structures
Close close procedure to finalize user-defined data structures
Ast 26

import de.cocolab.reuse.MaskedException;
...
Tree t;
try {
t.traverseTD (new Tree.ProcTree () {
public void proc (Tree yyt) {
try {
// code which might throw an IOException
yyt.writeNode (out);
}
catch (java.io.IOException e) {
// wrap the exception in a MaskedException, which need not be caught
throw new MaskedException (e);
}
}
});
} catch (MaskedException e) {
// unwrap and rethrow exception, to get the usual stack trace and termination
throw (java.io.IOException) (e.exception);
}

An assignment of a tree or graph to a variable of type TREE can be done in two ways: The
usual assignment operator ’=’ yields reference semantics. The procedure Copy/copy yields value
semantics by duplicating a given graph.
The procedure IsEqual/isEqual is used to test structural equivalence of trees. It is not designed
to handle general graphs.
The construction of the node classes enforces the tree typing rules through the types of the tar-
get language. Therefore, it is not possible to construct trees that violate the specification except by
using NoTREE or null as a value for a child or by using type casts. The procedure Check/check can
be used to check if a tree is properly constructed. In case of typing errors the involved parent and
child nodes are printed on standard error.
The binary graph writer procedure Put/put produces a binary file containing the graph in lin-
earized form. In proper C++ the same format is used as in the cases of C and simple C++. In Java
this is implemented by object serialization.
Graphs that have been written to a file with the procedure Put/put can be read from file into
memory with the procedure Get/get.
The procedures Write/write and Read/read perform input and output of graphs in human read-
able text notation. The user is responsible for making sure that appropriate macros or procedures
are defined for converting any user defined attribute types to and from strings, see "Type Specific
Operations".
The procedure Configure allows the configuration of some parameters of the tree module. For
details see the chapter "Procedural Mapping".

3.3. Graph Browsers


The procedure QueryTREE/Query/query allows to browse a tree and to inspect one node at a
time using an (old-fashioned) text user interface. A node including the names and the values of its
attributes is printed on standard output. Then the user is prompted to provide one of the following
commands from standard input:
Ast 27

parent display parent node


goto <n> display node at line number <n>
quit quit procedure
<selector> display specified child (first match, abbreviation possible)
<selector><space> display specified child (exact match, no abbreviation)
All commands can be abbreviated to an unambiguous prefix. Usually, a first match strategy is used
to determine a child from its (abbreviated) selector name. With this search strategy, children whose
name is a prefix of others may not be accessible. If an unabbreviated selector name is supplied
together with a following space character an exact match strategy is used, which allows to access
every child. The empty command behaves like parent.
The procedure DrawTREE/Draw provides a browser for trees and graphs with a graphic user
interface. Nodes are displayed as labelled rectangles and edges (links to children) as arrows. A
clearly arranged layout is calculated automatically. Attributes can be viewed similarly to Query-
TREE/Query/query. The graphic browser provides scrolling, zooming, and direct access to the
nodes using menu and mouse operations.
See the screenshot for an example of the browser window. It displays the abstract syntax tree
(graph) for a sequence of three MiniLax statements. (The identifier skip denotes the call of a proce-
dure.)
IF n < 0 THEN n := 1 ELSE skip END;
n := 2;
skip;
The nodes for the three statements are arranged underneath each other at the left-hand side. The
children of the node If describe the condition, the THEN part, and the ELSE part of the IF state-
ment. The children of the node Assign describe the left-hand side and the right-hand side of the
assignment statement. The node Ident with the thick border has been selected. The attributes and
children of this node are shown in a subwindow.
The procedure DrawTREE/Draw can be generated for the languages C, C++, and Modula-2. For
compilation and linking the public domain package tcl/tk version 8.3 or higher is required. Under
Unix linking is done with commands such as for example:
cc -L$X11/lib -ltk83 -ltcl83 -lX11 -lm -ldl .../libreuse.a or
cc -L$X11/lib -ltk8.3 -ltcl8.3 -lX11 -lm .../libreuse.a -lc
Ast 28

Screenshot of the graphic browser


Ast 29

The following is the online help information describing the meaning of the mouse actions and menu
buttons:
----------------------------------------------------------------------------
Mouse actions in browser window Meaning
----------------------------------------------------------------------------
single click with any button on node select this node
single click with left button on edge select node at arrow head
single click with right button on edge select node at arrow tail
single click with any button on folding indicator
select node at arrow tail

double click with left button on node select and open this node =
show attributes in a subwindow
double click with middle button on node select and open this node =
show attributes in new subwindow
double click with right button on node select and close this node =
dismiss subwindow with attributes
double click with left button on edge select and open node at arrow head =
show attributes in a subwindow
double click with middle button on edge select and open node at arrow head =
show attributes in new subwindow
double click with right button on edge select and open node at arrow tail =
show attributes in a subwindow
double click with any button on folding indicator
select node at arrow tail and
start new browser at selected node

press, move, and release middle button move graph at high speed
----------------------------------------------------------------------------
Mouse actions in attribute window Meaning
----------------------------------------------------------------------------
single click with left button on child select and open child node =
(indicated by an asterisk ’*’) show attributes in a subwindow
single click with left button on attribute (tree-valued)
(indicated by a plus ’+’) start new browser at attribute value
single click with left button on attribute (not tree-valued)
trigger application specific action

press, move, and release middle button move attribute display at high speed
----------------------------------------------------------------------------
Mouse actions in source window Meaning
----------------------------------------------------------------------------
single click with left button on line select node corresponding to line and
position selected node at center

press, move, and release middle button move source at high speed
----------------------------------------------------------------------------
Menu actions in main window Meaning
----------------------------------------------------------------------------
goto select node at certain line number
root select root node
home position selected node at home
top position selected node at top
center position selected node at center
open open selected node =
show attributes in a subwindow
Ast 30

push open selected node =


show attributes in new subwindow
close dismiss subwindow with attributes
draw start a new browser at selected node
source display source file with highlighting
print dump window in Postscript to a file
help display this help information
config configure tree browser
exit exit tree browser
----------------------------------------------------------------------------
Zoom with the scale in the menu bar. Zooming is performed relative to the
center of the window. Zooming is logarithmic:

2.0 = 400 %
1.0 = 200 %
0.0 = 100 %
-1.0 = 50 %
-2.0 = 25 %

The labels of the nodes are the names of the node types.
They are truncated to the first characters in order to fit.
After opening a node the full information becomes visible in a subwindow.
Arrows at the right-hand side of a node or without a target node indicate
omitted subtrees. These arrows are called folding indicators.
By default, nodes to a maximal depth of 6 are shown.
For nodes in lists another limit with a default of 256 is valid.
These limits can be changed with the operation ’config’ from the menu.
The new limits will be effective for all subsequent ’draw’ operations.

The tree browser provides a call-back mechanism for the attributes shown in a subwindow.
There are tree-valued attributes and non-tree-valued attributes. Tree-valued attributes are marked
with the character ’+’. A mouse click on a tree-valued attribute starts a new copy of the tree
browser on this subtree. A mouse click on a non-tree-valued attribute calls the macro DrawAttr with
two arguments: a pointer to the current node and a string giving the name of the selected attribute.
By default, this macro does nothing. It can be defined to execute arbitrary code. In the following
example the macro DrawAttr is defined to call the procedure draw_attr. This procedure decides
upon its arguments what to do and it may call for example another tree browser procedure named
Drawmy_tree.
GLOBAL {
# include "my_tree.h"
# define DrawAttr(addr, name) draw_attr (addr, name)
static void draw_attr (tTree tree, char * name)
{
switch (tree->Kind) {
case knode:
if (strcmp (name, "attribute") == 0)
Drawmy_tree (tree->node.attribute);
}
}
}
Ast 31

4. Using the Generated Program Module


This section explains how to use the objects of the generated program module. Trees or graphs
are created by successively creating their nodes. The easiest way is to call the constructor proce-
dures m<node_type> or the constructor methods called <node_type>. These procedures and meth-
ods combine node creation, storage allocation, and attribute assignment. They provide a mecha-
nism similar to record aggregates. Nested calls of constructor procedures allow programming with
(ground) terms as in Prolog or LISP. In general, a node can be created by a call of one of the proce-
dures
MakeTREE, n<node type>, or m<node type>.
or by invoking methods
TREE.make (k<node type>), <node type> (), <node type> (...).
The type of a node can be retrieved by examination of the predefined tag field/member variable
called Kind/kind. Alternatively the function/member function TREE_IsType/isType can be used to
test whether a node has a certain type or a subtype thereof. In the procedural languages children
and attributes can be accessed using two record selections. The first one states the node type and the
second one gives the selector name of the desired item. In object-oriented languages children and
attributes are accessed directly, possibly after applying a type cast.
The following sections contain examples for the different implementation languages:
Ast 32

4.1. C
abstract syntax:
Expr = [Pos: tPosition] <
Binary = Lop: Expr Rop: Expr [Operator: int] .
Unary = Expr [Operator] .
IntConst = [Value] .
RealConst = [Value: float] .
> .

tree construction by a term:


# define Plus 1
# include "TREE.h"
tTREE t;
tPosition Pos;
t = mBinary (Pos, mIntConst (Pos, 2), mIntConst (Pos, 3), Plus);

tree construction during parsing:


Expr: Expr ’+’ Expr {$$.Tree = mBinary ($2.Pos, $1.Tree, $3.Tree, Plus);} .
Expr: ’-’ Expr {$$.Tree = mUnary ($1.Pos, $2.Tree, Minus); } .
Expr: IntConst {$$.Tree = mIntConst ($1.Pos, $1.IntValue); } .
Expr: RealConst {$$.Tree = mRealConst ($1.Pos, $1.RealValue); } .

tree construction using a statement sequence:


t = MakeTREE (kBinary);
t->Binary.Pos.Line = 0;
t->Binary.Pos.Column = 0;
t->Binary.Lop = MakeTREE (kIntConst);
t->Binary.Lop->IntConst.Pos = Pos;
t->Binary.Lop->IntConst.Value = 2;
t->Binary.Rop = MakeTREE (kIntConst);
t->Binary.Rop->IntConst.Pos = Pos;
t->Binary.Rop->IntConst.Value = 3;
t->Binary.Operator = Plus;

access of tag field, children, and attributes:


switch (t->Kind) {
case kExpr : ... t->Expr.Pos ...
case kBinary: ... t->Binary.Operator ...
... t->Binary.Lop ...
case kUnary : ... t->Unary.Expr->Expr.Pos ...
}
Ast 33

4.2. Simple C++


abstract syntax:
Expr = [Pos: tPosition] <
Binary = Lop: Expr Rop: Expr [Operator: int] .
Unary = Expr [Operator] .
IntConst = [Value] .
RealConst = [Value: float] .
> .

tree construction by a term:


# define Plus 1
# include "TREE.h"
TREE o; // create a tree object (manager)
tTREE t; // declare a pointer to tree nodes
tPosition Pos;
t = o.mBinary (Pos, o.mIntConst (Pos, 2), o.mIntConst (Pos, 3), Plus);

tree construction during parsing:


Expr: Expr ’+’ Expr {$$.Tree = o.mBinary ($2.Pos, $1.Tree, $3.Tree, Plus);} .
Expr: ’-’ Expr {$$.Tree = o.mUnary ($1.Pos, $2.Tree, Minus); } .
Expr: IntConst {$$.Tree = o.mIntConst ($1.Pos, $1.IntValue); } .
Expr: RealConst {$$.Tree = o.mRealConst ($1.Pos, $1.RealValue); } .

tree construction using a statement sequence:


t = o.MakeTREE (kBinary);
t->Binary.Pos.Line = 0;
t->Binary.Pos.Column = 0;
t->Binary.Lop = o.MakeTREE (kIntConst);
t->Binary.Lop->IntConst.Pos = Pos;
t->Binary.Lop->IntConst.Value = 2;
t->Binary.Rop = o.MakeTREE (kIntConst);
t->Binary.Rop->IntConst.Pos = Pos;
t->Binary.Rop->IntConst.Value = 3;
t->Binary.Operator = Plus;

access of tag field, children, and attributes:


switch (t->Kind) {
case kExpr : ... t->Expr.Pos ...
case kBinary: ... t->Binary.Operator ...
... t->Binary.Lop ...
case kUnary : ... t->Unary.Expr->Expr.Pos ...
}
Ast 34

4.3. Proper C++


abstract syntax:
Expr = [Pos: tPosition] <
Binary = Lop: Expr Rop: Expr [Operator: int] .
Unary = Expr [Operator] .
IntConst = [Value] .
RealConst = [Value: float] .
> .

tree construction by a term:


# define Plus 1
# include "TREE.h"
using namespace TREE; // allows IntConst instead of TREE::IntConst etc.
tTREE t; // declare a pointer to tree nodes of any type
Binary * b; // declare a pointer to tree nodes of type Binary
tPosition Pos;
b = new Binary (Pos, new IntConst (Pos, 2), new IntConst (Pos, 3), Plus);

tree construction during parsing:


Expr: Expr ’+’ Expr {$$.tree = new Binary ($2.Pos, $1.tree, $3.tree, Plus);} .
Expr: ’-’ Expr {$$.tree = new Unary ($1.Pos, $2.tree, Minus); } .
Expr: IntConst {$$.tree = new IntConst ($1.Pos, $1.IntValue); } .
Expr: RealConst {$$.tree = new RealConst ($1.Pos, $1.RealValue); } .

tree construction using a statement sequence:


b = (tpBinary) Make (kBinary);
b->Pos.Line = 0;
b->Pos.Column = 0;
b->Lop = (IntConst *) Make (kIntConst);
b->Lop->Pos = Pos;
((IntConst *) b->Lop)->Value = 2;
b->Rop = new IntConst ();
b->Rop->Pos = Pos;
((tpIntConst) b->Rop)->Value = 3;
b->Operator = Plus;

access of tag field, children, and attributes:


switch (t->Kind) {
case kExpr : ... ((tpExpr ) t)->Pos ...
case kBinary: ... ((tpBinary) t)->Operator ...
... ((tpBinary) t)->Lop ...
case kUnary : ... ((tpUnary ) t)->Expr->Pos ...
}
Ast 35

4.4. Modula-2
abstract syntax:
Expr = [Pos: tPosition] <
Binary = Lop: Expr Rop: Expr [Operator: INTEGER] .
Unary = Expr [Operator] .
IntConst = [Value] .
RealConst = [Value: REAL] .
> .

tree construction by a term:


CONST Plus = 1;
VAR t : tTREE;
Pos: tPosition;
t := mBinary (Pos, mIntConst (Pos, 2), mIntConst (Pos, 3), Plus);

tree construction during parsing:


Expr: Expr ’+’ Expr {$$.Tree := mBinary ($2.Pos, $1.Tree, $3.Tree, Plus);} .
Expr: ’-’ Expr {$$.Tree := mUnary ($1.Pos, $2.Tree, Minus); } .
Expr: IntConst {$$.Tree := mIntConst ($1.Pos, $1.IntValue); } .
Expr: RealConst {$$.Tree := mRealConst ($1.Pos, $1.RealValue); } .

tree construction using a statement sequence:


t := MakeTREE (Binary);
tˆ.Binary.Pos.Line := 0;
tˆ.Binary.Pos.Column := 0;
tˆ.Binary.Lop := MakeTREE (IntConst);
tˆ.Binary.Lopˆ.IntConst.Pos := Pos;
tˆ.Binary.Lopˆ.IntConst.Value := 2;
tˆ.Binary.Rop := MakeTREE (IntConst);
tˆ.Binary.Ropˆ.IntConst.Pos := Pos;
tˆ.Binary.Ropˆ.IntConst.Value := 3;
tˆ.Binary.Operator := Plus;

access of tag field, children, and attributes:


CASE tˆ.Kind OF
| Expr : ... tˆ.Expr.Pos ...
| Binary: ... tˆ.Binary.Operator ...
... tˆ.Binary.Lop ...
| Unary : ... tˆ.Unary.Exprˆ.Expr.Pos ...
END;
Ast 36

4.5. Java
abstract syntax:
Expr = [Pos: Position] <
Binary = Lop: Expr Rop: Expr [Operator: int] .
Unary = Expr [Operator] .
IntConst = [Value] .
RealConst = [Value: float] .
> .

tree construction by a term:


import TREE.*; // allows IntConst instead of TREE.IntConst etc.
static final int Plus = 1;
de.cocolab.reuse.Position Pos;
Tree t = new Binary (Pos, new IntConst (Pos, 2), new IntConst (Pos, 3), Plus);

tree construction during parsing:


Expr: Expr ’+’ Expr {$$ = new Binary ($2.position (), $1.asExpr (), $3.asExpr (),
Plus); }.
Expr: ’-’ Expr {$$ = new Unary ($1.position (), $2.asExpr (), Minus); }.
Expr: IntConst {$$ = new IntConst ($1.position (), $1.asIntValue ().value); }.
Expr: RealConst {$$ = new RealConst ($1.position (), $1.asRealValue ().value);}.

tree construction using a statement sequence:


Binary b = new Binary ();
b.Pos = new Position (0, 0);
b.Lop = new IntConst ();
b.Lop.Pos = Pos;
b.Lop.Value = 2;
b.Rop = new IntConst ();
b.Rop.Pos = Pos;
b.Rop.Value = 3;
b.Operator = Plus;

access of tag field, children, and attributes:


switch (t.kind) {
case TREE.Expr : Expr e = (Expr) t; ... e.Pos ...
case TREE.Binary: Binary b = (Binary) t; ... b.Operator ...
... b.Lop ...
case TREE.Unary : Unary u = (Unary) t; ... u.Expr.Pos ...
}

5. Related Research

5.1. Variant Records


Ast specifications and variant record types like in Pascal or Modula-2 are very similar. Every
node type in an ast specification corresponds to a single variant. In the generated code every node
type is translated into a record type. All record types become members of a variant record type rep-
resenting the type for the trees.
The differences are the following: Ast specifications are shorter than directly hand-written vari-
ant record types. Ast specifications are based on the formalism of context-free grammars (see sec-
tion 5.3.). The generator ast automatically provides operations on record types which would be
Ast 37

simple but voluminous to program by hand. The node constructor procedures allow to write record
aggregates and provide dynamic storage management. The reader and writer procedures supply
input and output for record types and even for complete linked data structures such as trees and
graphs. Even when the target language is an object-oriented language such as Java, ast still pro-
vides important advantages over hand coding.

5.2. Type Extensions


Type extensions have been introduced with the language Oberon [Wir88a, Wir88b, Wir88c].
The extension mechanism of ast is basically the same as in Oberon. The notions extension, base
type, and derived type are equivalent. Type extension is equivalent to inheritance in object-oriented
languages such as C++ and Java. Type tests and type guards can be easily programmed by inspect-
ing the tag field of a node. Ast does not provide assignment of subtypes to base types in the sense of
value semantics or a projection, respectively. The tool can be seen as a preprocessor providing type
extensions for C, C++, and Modula-2.
The second example in section 2.5. illuminates the relationship between ast and Oberon. The
node type Stats is a base type with two fields, a child and an attribute. It is extended e. g. by the
node type While with two more fields which are children.

5.3. Context-Free Grammars


As already mentioned, ast specifications are based on context-free grammars. Ast specifica-
tions extend context-free grammars by selector names for right-hand side symbols, attributes, the
extension mechanism, and modules. If these features are omitted, we basically arrive at context-free
grammars. This holds from the syntactic as well as from the semantic point of view. The names of
the node types represent both terminal or nonterminal symbols and rule names. Node types corre-
spond to grammar rules. The notions of derivation and derivation tree can be used similarly in both
cases. The selector names can be seen as syntactic sugar and the attributes as some kind of terminal
symbols. The extension mechanism is equivalent to a shorthand notation for factoring out common
rule parts in combination with implicit chain rules.
Example:
ast specification:
Stats = Next: Stats <
If = Expr Then: Stats Else: Stats .
While = Expr Stats .
> .

corresponding context-free grammar:


Stats = Stats .
Stats = Stats If .
Stats = Stats While .
If = Expr Stats Stats .
While = Expr Stats .

In the example above, Stats corresponds to a nonterminal. There are two rules or right-hand
sides for Stats which are named If and While. The latter would be regarded as nonterminals, too, if
a child of types If or While would be specified.
Ast 38

5.4. Attribute Grammars


Attribute grammars [Knu68, Knu71] and ast specifications are based on context-free grammars
and associate attributes with terminal and nonterminals symbols. Additionally ast allows attributes
which are local to rules. As the structure of the tree itself is known and transparent, subtrees can be
accessed or created dynamically and used as attribute values. The access of the right-hand side sym-
bols uses the selector names for symbolic access instead of the grammar symbol with an additional
subscript if needed. There is no need to map chain rules to tree nodes because of the extension
mechanism offered by ast. Attribute evaluation is outside the scope of ast. This can be done either
with the attribute evaluator generator ag [Grob] which understands ast specifications extended by
attribute computation rules and processes the trees generated by ast or by hand-written programs
that use an ast generated module. In the latter case attribute computations do not have to obey the
single assignment restriction for attributes. They can assign a value to an attribute zero, one, or sev-
eral times.

5.5. Interface Description Language (IDL)


The approach of ast is similar to the one of IDL [Lam87, NNG89]. Both specify attributed
trees as well as graphs. Node types without extensions are called nodes in IDL and node types with
extensions (base types) are called classes. Ast has simplified this to the single notion of a node type.
Attributes are treated similarly in both systems. Children and attributes are both regarded as
attributes, as structural and non-structural ones, with only little difference in between. Both systems
allow multiple inheritance of attributes, ast has a separate syntax for single inheritance and uses the
notion extension instead [Wir88a]. IDL knows the predefined types INTEGER, RATIONAL,
BOOLEAN, STRING, SEQ OF, and SET OF. It offers special operations for the types SEQ OF and
SET OF. Ast really has no built in types at all, it uses the ones of the target language and has a table
containing the type specific operations e. g. for reading and writing. Both ast and IDL allow
attributes of user-defined types. In ast, the type specific operations for predefined or user-defined
types are easily expressed by macros using the target language directly. IDL offers an assertion lan-
guage whereas ast does not. IDL provides a mechanism to modify existing specifications. The
module feature of ast can be used to extend existing specifications. From ast, readers and writers
are requested with simple command line options instead of complicated syntactic constructs. Ast
does not support representation specifications, because representations are much more easily
expressed using the types of the target language directly. Summarizing, we consider ast to have a
simpler specification method and to generate more powerful features like aggregates, reversal of
lists, and graph browsers.

5.6. Attribute Coupled Grammars


Attribute coupled grammars (ACG’s) [Gie88] or algebraic specifications [HHK88] have only
very little in common with ast specifications. They all view node types or rules as signatures of
functions. The name of the node type plays the role of the function name and describes the result
type. The types of the children and attributes correspond to the type of the function arguments. The
constructor procedures generated by ast reflect this view best.

5.7. Object-Oriented Languages


The extension mechanism of ast is exactly the same as single inheritance in object-oriented
languages like e. g. Simula [DMN70] or Smalltalk [Gol84]. The hierarchy introduced by the exten-
sion mechanism corresponds directly to the class hierarchy of object-oriented languages. The
notions base type and super class both represent the same concept. Messages and virtual
Ast 39

procedures are out of the scope of ast. Virtual procedures might be simulated with procedure-val-
ued attributes. Table 8 summarizes the corresponding notions of trees (ast), type extensions, and
object-oriented programming.

5.8. Tree Grammars


Conventional tree grammars are characterized by the fact that all right-hand sides start with a
terminal symbol. They are used for the description of string languages that represent trees in prefix
form. Ast specifications describe trees which are represented by (absolute) pointers from parent to
child nodes. If we shift the names of node types of ast specifications to the beginning of the right-
hand side and interpret them as terminals we arrive at conventional tree grammars. That is exactly
what is done by the ast tree/graph writers. They write a tree in prefix form and prepend every node
with the name of its node type. That is necessary to be able to perform the read operations.

6. Hints on Specifying Abstract Syntax


- Keep the abstract syntax as short and simple as possible.
- Try to normalize by representing only the most general form.
- Normalize to the general form e. g. by adding default values.
- Normalize several concrete representations to one abstract construct.
- Map concrete to abstract syntax by disregarding the concrete syntax rules and by concentrating
on the semantic structure of the abstract syntax.
- Map several concrete nonterminals to one abstract node type (e. g. Expr, Term, and Factor →
Expr)
- Allow all lists to be empty regardless of the concrete syntax. Otherwise you have to process
the list element at two places in exactly the same way. This causes programming overhead and
violates the law of singularity: "One thing only once!"
- Operators can be represented by different node types (e. g. Plus, Minus, Mult, ...) or by one
node type with an attribute describing the operator (e. g. Binary).
- Lists can be represented by separate nodes for the list and the elements (e. g. Stats and Stat) or
by nodes for the elements where every node has a child that refers to the next list element (see
last example in section 2.12.1.).

Table 8: Comparison of notions from the areas of trees, types, and object-oriented programming

trees types object-oriented programming


node type record type class
- base type superclass
- derived type subclass
attribute, child record field instance variable
tree node record variable object, instance
- extension inheritance
Ast 40

7. Examples
The Appendices 1 to 3 contain examples of ast specifications.
Appendix 1 contains the concrete syntax of ast’s specification language. The node types
enclosed in quotes or starting with the character ’t’ constitute the terminals for ast’s parser. The
extensions and the node types used for the latter describe the lexical grammar.
Appendix 2 contains the concrete syntax of a small example programming language called
MiniLAX [GrK88]. The attributes specified are the ones a parser would evaluate during parsing.
The Appendices 1 and 2 show how concrete grammars can be described with ast.
Appendix 3 contains an abstract syntax for MiniLAX. The attributes specified are input or
intrinsic ones whose values would be provided by the scanner and parser. The definition follows the
hints of the previous section. Terminal symbols without attributes are omitted. All binary and
unary operators are expressed by two nodes having one attribute to represent the operator. To sim-
plify things as much as possible all lists are allowed to be empty and procedure declarations as well
as calls always have a parameter list. The specification tries to keep the tree as small as possible.
The inheritance mechanism allows to avoid all chain rules. There are no nodes for sequences of dec-
larations, statements, etc.. Instead every node for a declaration or a statement has a field named Next
describing the successor entity. Except for expressions no separate nodes are used for identifiers.
The information is included as attribute in the node types Proc, Var, Formal, and Call. The source
position is stored only at the nodes where it might be needed during semantic analysis. The above
measures not only reduce the amount of storage but they also reduce run time because less informa-
tion has to be produced and processed.

8. Experiences
Ast can be used not only for abstract syntax trees in compilers but for every tree or graph like
data structure. In general the data structure can serve as interface between phases within a program
or between separate programs. In the latter case it would be communicated via a file using the gen-
erated reader and writer procedures.
Generated tree respectively graph modules have successfully been used in compilers e. g. for
MiniLAX [WGS89] and UNITY [Bie89] as well as for a Modula to C translator [Mar90]. The
modules for the internal data structure of ast itself and the attribute evaluator generator ag [Grob]
have also been generated by ast. Moreover, the symbol table module of the Modula to C translator
has been generated.
The advantage of this approach is that it saves considerably hand-coding of trivial declarations
and operations. Table 9 lists the sizes (numbers of lines) of some specifications and the generated
modules. Sums in the specification column are composed of the sizes for the definition of node
types and for user-supplied target code. Sums in the tree module column are composed of the sizes
for the definition part and for the implementation part. The reason for the large sizes of the tree
modules comes from the numerous node constructor procedures and from the graph browser in the
case of ag. These procedures proved to be very helpful for debugging purposes as they provide
readable output of complex data structures. The constructor procedures allow to write record aggre-
gates. Therefore, node creation and assignment of values to the components can be written very
compact. It is even possible to write (ground) terms as in Prolog or LISP by nested calls of the con-
structor procedures.
Ast 41

Table 9: Examples of Ast Applications

application specification tree module


MiniLAX 56 202 + 835 = 1037
Modula-2 240 583 + 3083 = 3666
UNITY 210 207 + 962 = 1169
Ag 78 + 347 = 425 317 + 1317 = 1634
Definition table 82 + 900 = 982 399 + 1431 = 1830

9. Usage
NAME
ast - generator for abstract syntax trees
SYNOPSIS
ast [ -options ] ... [ +options ] [ -ldirectory ] [ files ]
DESCRIPTION
Ast generates a program module to handle arbitrary attributed trees and graphs. A typical
application is the abstract syntax tree in a compiler. The input file contains a specification
which describes the structure of all possible trees or nodes respectively and the attributes of
the nodes. Ast generates type declarations to implement the tree and several procedures for
tree manipulation including text and binary readers and writers (see options below). If file is
omitted the specification is read from standard input.
OPTIONS

a generate all except -ceh (default)


n generate node constructors procedures n<node> (node)
m generate node constructors procedures m<node> (make)
f generate node/graph destroyer procedure ReleaseTREE (free)
F generate general destroyer procedure ReleaseTREEModule (FREE)
o generate text node writer procedure WriteTREENode (output)
r generate text graph reader procedure ReadTREE
w generate text graph writer procedure WriteTREE
+X generate XML graph writer procedure WriteTREEXML
g generate binary graph reader procedure GetTREE
p generate binary graph writer procedure PutTREE
t generate top down traversal procedure TraverseTREETD
(reverse depth first)
b generate bottom up traversal procedure TraverseTREEBU
(depth first)
R generate list reverser procedure ReverseTREE
Ast 42

R generate list iterator procedure ForallTREE


y generate graph copy procedure CopyTREE
= generate tree equality test procedure IsEqualTREE
k generate graph type checker procedure CheckTREE
q generate text graph browser procedure QueryTREE
e generate graphic graph browser procedure DrawTREE
_ generate array TREE_NodeName
d generate header file or definition module
i generate implementation part or module
s generate import statements
: generate lines not longer than 80 characters
4 generate tree/graph description in file TREE.TS
6 generate # line directives
7 touch output files only if necessary
8 report storage consumption
c generate C source code (default: Modula-2)
c+ generate simple C++ source code (using C data structure: union)
c++ generate proper C++ source code (using C++ data structure: class)
J generate Java source code
h print help information
-ldir specify the directory dir where ast finds its tables
+H print advanced help
+E generate tree equality test procedure IsEqualTREE
+I suppress informations
+P generate empty bodies for procedures not selected
FILES
if output is in C:
<module>.h header file of the generated graph module
<module>.c body of the generated graph module
yy<module>.h macro file defining type specific operations
Tree.tcl procedure definitions for procedure DrawTREE
if output is in C++:
<module>.h header file of the generated graph module
<module>.cxx body of the generated graph module
yy<module>.h macro file defining type specific operations
Tree.tcl procedure definitions for procedure DrawTREE
if output is in Modula-2:
Ast 43

<module>.md definition module of the generated graph module


<module>.mi implementation module of the generated graph module
<module>.imp import statements
Tree.tcl procedure definitions for procedure DrawTREE
if output is in Java:
<module>.java classes of the generated graph module
SEE ALSO

J. Grosch: "Ast - A Generator for Abstract Syntax Trees", CoCoLab Germany, Document
No. 15
J. Grosch: "Tool Support for Data Structures", Structured Programming, 12, 31-38 (1991)
J. Grosch: "Tool Support for Data Structures", CoCoLab Germany, Document No. 17

References
[Bie89] F. Bieler, An Interpreter for Chandy/Misra’s UNITY, internal paper, GMD
Forschungsstelle an der Universität Karlsruhe, 1989.
[DMN70] O. Dahl, B. Myrhaug and K. Nygaard, SIMULA 67 Common Base Language -
Publication S-22, Norwegian Computing Center, Oslo, 1970.
[Gie88] R. Giegerich, Composition and Evaluation of Attribute Coupled Grammars, Acta Inf.
25, (1988), 355-423.
[Gol84] A. Goldberg, Smalltalk-80: The Interactive Programming Environment, Addison
Wesley, Reading, MA, 1984.
[GrK88] J. Grosch and E. Klein, Übersetzerbau-Praktikum, Compiler Generation Report No. 9,
GMD Forschungsstelle an der Universität Karlsruhe, June 1988.
[Groa] J. Grosch, Puma - A Generator for the Transformation of Attributed Trees, Cocktail
Document No. 26, CoCoLab Germany.
[Grob] J. Grosch, Ag - An Attribute Evaluator Generator, Cocktail Document No. 16,
CoCoLab Germany.
[Groc] J. Grosch, Lark - An LR(1) Parser Generator With Backtracking, Cocktail Document
No. 32, CoCoLab Germany.
[GrV] J. Grosch and B. Vielsack, The Parser Generators Lalr and Ell, Cocktail Document No.
8, CoCoLab Germany.
[Groa] J. Grosch, Reusable Software - A Collection of Modula-Modules, Cocktail Document
No. 4, CoCoLab Germany.
[Grob] J. Grosch, Reusable Software - A Collection of C-Modules, Cocktail Document No. 30,
CoCoLab Germany.
[HHK88] J. Heering, P. R. H. Hendriks, P. Klint and J. Rekers, The Syntax Definition Formalism
SDF - Reference Manual, ESPRIT Project GIPE, Dec. 1988.
[Joh75] S. C. Johnson, Yacc — Yet Another Compiler-Compiler, Computer Science Technical
Report 32, Bell Telephone Laboratories, Murray Hill, NJ, July 1975.
Ast 44

[Knu68] D. E. Knuth, Semantics of Context-Free Languages, Mathematical Systems Theory 2, 2


(June 1968), 127-146.
[Knu71] D. E. Knuth, Semantics of Context-free Languages: Correction, Mathematical Systems
Theory 5, (Mar. 1971), 95-96.
[Lam87] D. A. Lamb, IDL: Sharing Intermediate Representations, ACM Trans. Prog. Lang. and
Systems 9, 3 (July 1987), 297-318.
[Mar90] M. Martin, Entwurf und Implementierung eines Übersetzers von Modula-2 nach C,
Diplomarbeit, GMD Forschungsstelle an der Universität Karlsruhe, Feb. 1990.
[Nas] T. Nash, Reusable Software - A Java Package, Cocktail Document No. 36, CoCoLab
Germany.
[NNG89] J. R. Nestor, J. M. Newcomer, P. Giannini and D. L. Stone, IDL: The Language and its
Implementation, Prentice Hall, Englewood Cliffs, 1989.
[Par88] J. C. H. Park, y+: A Yacc Preprocessor for Certain Semantic Actions, SIGPLAN
Notices 23, 6 (1988), 97-106.
[WGS89] W. M. Waite, J. Grosch and F. W. Schröer, Three Compiler Specifications, GMD-Studie
Nr. 166, GMD Forschungsstelle an der Universität Karlsruhe, Aug. 1989.
[Wir88a] N. Wirth, Type Extensions, ACM Trans. Prog. Lang. and Systems 10, 2 (Apr. 1988),
204-214.
[Wir88b] N. Wirth, From Modula to Oberon, Software—Practice & Experience 18, 7 (July
1988), 661-670.
[Wir88c] N. Wirth, The Programming Language Oberon, Software—Practice & Experience 18, 7
(July 1988), 671-690.
Ast 45

Appendix 1: Syntax of the Specification Language

RULE /* Ast: concrete syntax */

/* parser grammar */

Specification = <
= TreeCodes PropPart DeclPart RulePart Modules .
= ’MODULE’ Name TreeCodes PropPart DeclPart RulePart
’END’ Name Modules .
> .
TreeCodes = <
= SubUnit .
= ’TREE’ SubUnit Codes .
= ’TREE’ DottedName SubUnit Codes .
= ’TREE’ DottedName Name SubUnit Codes .
= ’TREE’ DottedName ’PREFIX’ Name SubUnit Codes .
> .
Codes = <
= .
= Codes ’EXPORT’ tTargetCode .
= Codes ’IMPORT’ tTargetCode .
= Codes ’GLOBAL’ tTargetCode .
= Codes ’LOCAL’ tTargetCode .
= Codes ’BEGIN’ tTargetCode .
= Codes ’CLOSE’ tTargetCode .
> .
SubUnit = <
= .
= SubUnit ’SUBUNIT’ Name .
= SubUnit ’VIEW’ Name .
> .
PropPart = Props .

Props = <
=
= Props ’PROPERTY’ Properties
= Props ’PROPERTY’ Properties ’FOR’ Names
= Props ’SELECT’ Names
> .
DeclPart = <
= .
= ’DECLARE’ Decls .
> .
Decls = <
= .
MoreNonterms = Decls Names ’=’ AttrDecls ’.’ .
MoreTerminals = Decls Names ’:’ AttrDecls ’.’ .
> .
Names = <
= .
= Names Name .
= Names ’,’ .
> .
RulePart = <
= .
= ’RULE’ Types .
Ast 46

> .
Types = <
= .
Nonterminal = Types Name BaseTypes ’=’ AttrDecls Extensions ’.’ .
Terminal = Types Name BaseTypes ’:’ AttrDecls Extensions ’.’ .
Abstract = Types Name BaseTypes ’:=’ AttrDecls Extensions ’.’ .
> .
BaseTypes = <
= .
= ’<-’ Names .
> .
Extensions = <
= .
= ’<’ Types ’>’ .
> .
AttrDecls = <
= .
ChildSelect = AttrDecls Name ’:’ Name Properties .
ChildNoSelect = AttrDecls Name Properties .
AttrTyped = AttrDecls ’[’ Name ’:’ Name Properties ’]’ .
AttrInteger = AttrDecls ’[’ Name Properties ’]’ .
AttrTypedInit = AttrDecls ’[’ Name ’:’ Name ’:=’ tExpression ’]’ .
AttrIntInit = AttrDecls ’[’ Name ’:=’ tExpression ’]’ .
> .
Properties = <
= .
= Properties ’INPUT’ .
= Properties ’OUTPUT’ .
= Properties ’SYNTHESIZED’ .
= Properties ’INHERITED’ .
= Properties ’THREAD’ .
= Properties ’REVERSE’ .
= Properties ’IGNORE’ .
= Properties ’VIRTUAL’ .
> .
Modules = <
= .
= Modules ’MODULE’ Name TreeCodes DeclPart RulePart ’END’ Name .
> .
Name = <
= tIdent .
= tString .
> .
DottedName = <
= Name .
= DottedName ’.’ Name . /* Java only */
> .

/* lexical grammar */

tIdent : <
= Letter .
= tIdent Letter .
= tIdent Digit .
= tIdent ’_’ .
> .
tString : <
Ast 47

= "’" Characters "’" .


= ’"’ Characters ’"’ .
> .
tTargetCode : ’{’ Characters ’}’ .

Comment : ’/*’ Characters ’*/’ .

Characters : <
= .
= Characters Character .
> .

tExpression : . /* target language expression */


Ast 48

Appendix 2: Concrete Syntax of the Example Language MiniLAX

RULE
Prog = PROGRAM tIdent ’;’ ’DECLARE’ Decls ’BEGIN’ Stats ’END’ ’.’ .
Decls = <
Decls1 = Decl .
Decls2 = Decls ’;’ Decl .
> .
Decl = <
Var = tIdent ’:’ Type .
Proc0 = PROCEDURE tIdent ’;’ ’DECLARE’ Decls ’BEGIN’ Stats ’END’ .
Proc = PROCEDURE tIdent ’(’ Formals ’)’ ’;’
’DECLARE’ Decls ’BEGIN’ Stats ’END’ .
> .
Formals = <
Formals1 = Formal .
Formals2 = Formals ’;’ Formal .
> .
Formal = <
Value = tIdent ’:’ Type .
Ref = VAR tIdent ’:’ Type .
> .
Type = <
Int = INTEGER .
Real = REAL .
Bool = BOOLEAN .
Array = ARRAY ’[’ Lwb: tIntegerConst ’..’ Upb: tIntegerConst ’]’ OF Type .
> .
Stats = <
Stats1 = Stat .
Stats2 = Stats ’;’ Stat .
> .
Stat = <
Assign = Adr ’:=’ Expr .
Call0 = tIdent .
Call = tIdent ’(’ Actuals ’)’ .
If = IF Expr THEN Then: Stats ELSE Else: Stats ’END’ .
While = WHILE Expr DO Stats ’END’ .
Read = READ ’(’ Adr ’)’ .
Write = WRITE ’(’ Expr ’)’ .
> .
Actuals = <
Expr1 = Expr .
Expr2 = Actuals ’,’ Expr .
> .
Expr = <
Less = Lop: Expr ’<’ Rop: Expr .
Plus = Lop: Expr ’+’ Rop: Expr .
Times = Lop: Expr ’*’ Rop: Expr .
Not = NOT Expr .
’()’ = ’(’ Expr ’)’ .
IConst = tIntegerConst .
RConst = tRealConst .
False = FALSE .
True = TRUE .
Adr = <
Name = tIdent .
Index = Adr ’[’ Expr ’]’ .
> .
> .
tIdent : [Ident: tIdent] .
tIntegerConst : [Integer ] .
tRealConst : [Real : double] .
Ast 49

Appendix 3: Abstract Syntax of the Example Language MiniLAX

TREE /* MiniLAX: abstract syntax */


IMPORT {
# include "Idents.h"
# include "Position.h"
}

RULE

MiniLax = Proc .
Decls = <
NoDecl = .
Decl = Next: Decls REV [Ident: tIdent] [Pos: tPosition] <
Proc = Formals Decls Stats .
Var = Type .
> .
> .
Formals = <
NoFormal = .
Formal = Next: Formals REV [Ident: tIdent] [Pos: tPosition] Type .
> .
Type = <
Integer = .
Real = .
Boolean = .
Array = Type [Lwb] [Upb] [Pos: tPosition] .
Ref = Type .
> .
Stats = <
NoStat = .
Stat = Next: Stats REV <
Assign = Adr Expr [Pos: tPosition] .
Call = Actuals [Ident: tIdent] [Pos: tPosition] .
If = Expr Then: Stats Else: Stats .
While = Expr Stats .
Read = Adr .
Write = Expr .
> .
> .
Actuals = <
NoActual = [Pos: tPosition] .
Actual = Next: Actuals REV Expr .
> .
Expr = [Pos: tPosition] <
Binary = Lop: Expr Rop: Expr [Operator] .
Unary = Expr [Operator] .
IntConst = [Value ] .
RealConst = [Value: double] .
BoolConst = [Value: rbool ] .
Adr = <
Index = Adr Expr .
Ident = [Ident: tIdent] .
> .
> .
Ast 50

Appendix 4: Generated Header File for C

# ifndef yyTREE /* throughout replace TREE by the name of the tree module */
# define yyTREE
<import_declarations>
# define rbool char
# define NoTREE (tTREE) NULL
# define k<type_1> 1
# define k<type_2> 2
...
typedef unsigned short TREE_tKind; /* or unsigned char */
typedef unsigned short TREE_tMark;
typedef unsigned short TREE_tLabel;
typedef union TREE_Node * tTREE;
typedef void (* TREE_tProcTree) (void);
typedef tTREE t<type_1>; /* for toplevel node types, only */
typedef tTREE t<type_2>;
...
<export_declarations>
# ifndef TREE_NodeHead
# define TREE_NodeHead
# endif
typedef struct { TREE_tKind yyKind; TREE_tMark yyMark; TREE_NodeHead }
TREE_tNodeHead;
typedef struct { TREE_tNodeHead yyHead;
<children_and_attributes_of_type_1> } y<type_1>;
typedef struct { TREE_tNodeHead yyHead;
<children_and_attributes_of_type_2> } y<type_2>;
...
union TREE_Node {
TREE_tKind Kind;
TREE_tNodeHead yyHead;
y<type_1> <type_1>;
y<type_2> <type_2>;
...
};
extern tTREE TREERoot;
extern unsigned long TREE_HeapUsed;
extern char * TREE_NodeName [];
extern void (* Tree_Exit) (void);
extern tTREE n<type_1> (void);
extern tTREE n<type_2> (void);
...
extern tTREE m<type_1> (<input_children_and_attributes_of_type_1>);
extern tTREE m<type_2> (<input_children_and_attributes_of_type_2>);
...
extern tTREE MakeTREE (TREE_tKind Kind);
extern rbool TREE_IsType (tTREE t, TREE_tKind Kind);
extern void ReleaseTREE (tTREE t);
extern void ReleaseTREEModule (void);
extern void WriteTREENode (FILE * f, tTREE t);
Ast 51

extern tTREE ReadTREE (FILE * f);


extern void WriteTREE (FILE * f, tTREE t);
extern void WriteTREEXML (FILE * f, tTREE t);
extern tTREE GetTREE (FILE * f);
extern void PutTREE (FILE * f, tTREE t);
extern void TraverseTREETD (tTREE t, void (* Procedure) (tTREE t));
extern void TraverseTREEBU (tTREE t, void (* Procedure) (tTREE t));
extern tTREE ReverseTREE (tTREE t);
extern void ForallTREE (tTREE t, void (* Procedure) (tTREE t));
extern tTREE CopyTREE (tTREE t);
extern rbool IsEqualTREE (tTREE t1, tTREE t2);
extern rbool CheckTREE (tTREE t);
extern void QueryTREE (tTREE t);
extern void DrawTREE (tTREE t);
extern void ConfigureTREE (char * Parameter, char * Value);
extern void BeginTREE (void);
extern void CloseTREE (void);
# endif
Ast 52

Appendix 5: Generated Header File for Simple C++

# ifndef yyTREE // throughout replace TREE by the name of the tree module
# define yyTREE
<import_declarations>
# define rbool char
# define NoTREE (tTREE) 0L
# define k<type_1> 1
# define k<type_1> 2
...
typedef unsigned short TREE_tKind; // or unsigned char
typedef unsigned short TREE_tMark;
typedef unsigned short TREE_tLabel;
typedef union TREE_Node * tTREE;
typedef void (* TREE_tProcTree) (void);
typedef tTREE t<type_1>; // for toplevel node types, only
typedef tTREE t<type_2>;
...
<export_declarations>
# ifndef TREE_NodeHead
# define TREE_NodeHead
# endif
typedef struct { TREE_tKind yyKind; TREE_tMark yyMark; TREE_NodeHead }
TREE_tNodeHead;
typedef struct { TREE_tNodeHead yyHead;
<children_and_attributes_of_type_1> } y<type_1>;
typedef struct { TREE_tNodeHead yyHead;
<children_and_attributes_of_type_2> } y<type_2>;
...
union TREE_Node {
TREE_tKind Kind;
TREE_tNodeHead yyHead;
y<type_1> <type_1>;
y<type_2> <type_2>;
...
};
extern const char * const TREE_NodeName [];
Ast 53

# define TREE_BASE_CLASS
class TREE TREE_BASE_CLASS {
public:
tTREE TREERoot;
unsigned long TREE_HeapUsed;
void (* TREE_Exit) (void);
tTREE n<type_1> (void);
tTREE n<type_2> (void);
...
tTREE m<type_1> (<input_children_and_attributes_of_type_1>);
tTREE m<type_2> (<input_children_and_attributes_of_type_2>);
...
tTREE MakeTREE (TREE_tKind Kind);
rbool TREE_IsType (tTREE t, TREE_tKind Kind);
void ReleaseTREE (tTREE t);
void ReleaseTREEModule(void);
void WriteTREENode (FILE * f, tTREE t);
tTREE ReadTREE (FILE * f);
void WriteTREE (FILE * f, tTREE t);
void WriteTreeXML (FILE * f, tTREE t);
tTREE GetTREE (FILE * f);
void PutTREE (FILE * f, tTREE t);
void TraverseTREETD (tTREE t, TREE_tProcTree Proc);
void TraverseTREEBU (tTREE t, TREE_tProcTree Proc);
tTREE ReverseTREE (tTREE t);
void ForallTREE (tTREE t, TREE_tProcTree Proc);
tTREE CopyTREE (tTREE t);
rbool IsEqualTREE (tTREE t1, tTREE t2);
rbool CheckTREE (tTREE t);
void QueryTREE (tTREE t);
void DrawTREE (tTREE t);
void ConfigureTREE (char * Parameter, char * Value);
void BeginTREE (void);
void CloseTREE (void);
TREE (void);
˜TREE (void);
};
# endif
Ast 54

Appendix 6: Generated Header File for Proper C++

# ifndef yyTREE // throughout replace TREE by the name of the tree module
# define yyTREE
<import_declarations>
namespace TREE {
# define NoTREE 0
static const int k<type_1> = 1;
static const int k<type_2> = 2;
...
typedef class yyTREE * tTREE;
typedef void (* yytProcTREE) (tTREE);
typedef unsigned short yytKind; // or unsigned char
typedef unsigned short yytMark;
typedef class <type_1> * tp<type_1>;
typedef class <type_2> * tp<type_2>;
...
extern tTREE Root;
extern const char * const NodeName [];
extern Errors * ErrorsObj;
extern tTREE Make (yytKind);
extern bool IsType (tTREE, yytKind);
extern void Release (tTREE);
extern void WriteNode (FILE *, tTREE);
extern tTREE Read (FILE *);
extern void Write (FILE *, tTREE);
extern void WriteXML (FILE *, tTREE);
extern tTREE Get (FILE *);
extern void Put (FILE *, tTREE);
extern void TraverseTD (tTREE, yytProcTREE Proc);
extern void TraverseBU (tTREE, yytProcTREE Proc);
extern tTREE Reverse (tTREE);
extern void Forall (tTREE, yytProcTREE Proc);
extern tTREE Copy (tTREE);
extern bool IsEqual (tTREE, tTREE);
extern bool Check (tTREE);
extern void Query (tTREE);
extern void Draw (tTREE);
extern void Configure (char * Parameter, char * Value);
extern void yyInit (tTREE);
extern void Begin ();
extern void Close ();
Ast 55

class yyTREE {
public:
yytKind Kind;
yytMark yyMark;
TREE_NodeHead
yyTREE ();
bool IsType (yytKind);
void Release ();
void WriteNode (FILE *);
void Write (FILE *);
void WriteXML (FILE *);
void Put (FILE *);
void TraverseTD (yytProcTREE Proc);
void TraverseBU (yytProcTREE Proc);
tTREE Reverse ();
void Forall (yytProcTREE Proc);
tTREE Copy ();
bool IsEqual (tTREE);
bool Check ();
void Query ();
void Draw ();
};
class <type_1> : public cTREE {
public:
<children_and_attributes_of_type_1>
<type_1> ();
<type_1> (<input_children_and_attributes_of_type_1>);
};
class <type_2> : public <type_1> {
public:
<children_and_attributes_of_type_2>
<type_2> ();
<type_2> (<input_children_and_attributes_of_type_1>);
};
...
};
<export_declarations>
# endif
Ast 56

Appendix 7: Generated Definition Module for Modula-2

DEFINITION MODULE TREE;


IMPORT IO; (* throughout replace TREE by the name of the tree module *)
<import_declarations>
CONST
NoTREE = NIL;
<type_1> = 1;
<type_2> = 2;
...
TYPE
tTREE = POINTER TO yyNode;
tProcTree = PROCEDURE (tTREE);
t<type_1> = tTREE; (* for toplevel node types, only *)
t<type_2> = tTREE;
...
<export_declarations>
TYPE
yytNodeHead = RECORD yyKind, yyMark: SHORTCARD; END;
TYPE
y<type_1> = RECORD yyHead: yytNodeHead;
<children_and_attributes_of_type_1> END;
y<type_2> = RECORD yyHead: yytNodeHead;
<children_and_attributes_of_type_2> END;
...
yyNode = RECORD
CASE : SHORTCARD OF
| 0 : Kind: SHORTCARD;
| ... : yyHead: yytNodeHead;
| <type_1> : <type_1> : y<type_1>;
| <type_2> : <type_2> : y<type_2>;
...
END;
END;
VAR TREERoot : tTREE;
VAR HeapUsed : LONGCARD;
VAR yyExit : PROC;
PROCEDURE n<type_1> (): tTREE;
PROCEDURE n<type_2> (): tTREE;
...
PROCEDURE m<type_1> (<input_children_and_attributes_of_type_1>): tTREE;
PROCEDURE m<type_2> (<input_children_and_attributes_of_type_2>): tTREE;
...
PROCEDURE MakeTREE (Kind: SHORTCARD): tTREE;
PROCEDURE IsType (Tree: tTREE; Kind: SHORTCARD): BOOLEAN;
PROCEDURE ReleaseTREE (Tree: tTREE);
PROCEDURE ReleaseTREEModule;
PROCEDURE WriteTREENode (f: IO.tFile; Tree: tTREE);
PROCEDURE ReadTREE (f: IO.tFile): tTREE;
PROCEDURE WriteTREE (f: IO.tFile; Tree: tTREE);
PROCEDURE GetTREE (f: IO.tFile): tTREE;
PROCEDURE PutTREE (f: IO.tFile; Tree: tTREE);
Ast 57

PROCEDURE TraverseTREETD (Tree: tTREE; Proc: tProcTree);


PROCEDURE TraverseTREEBU (Tree: tTREE; Proc: tProcTree);
PROCEDURE ReverseTREE (Tree: tTREE): tTREE;
PROCEDURE ForallTREE (Tree: tTREE; Proc: tProcTree);
PROCEDURE CopyTREE (Tree: tTREE): tTREE;
PROCEDURE IsEqualTREE (Tree1, Tree2: tTREE): BOOLEAN;
PROCEDURE CheckTREE (Tree: tTREE): BOOLEAN;
PROCEDURE QueryTREE (Tree: tTREE);
PROCEDURE DrawTREE (Tree: tTREE);
PROCEDURE ConfigureTREE (VAR Parameter, Value: ARRAY OF CHAR);
PROCEDURE BeginTREE;
PROCEDURE CloseTREE;
END TREE.
Ast 58

Appendix 8: Generated Declarations for Java

// throughout replace TREE by the name of the tree module


package <package_name>; // If the tree module is a dotted name
<import_declarations>
<global_declarations>
<local_declarations>
public class TREE implements java.io.Serializable {
static final TREE NoTREE = null;
/* for ’forall’, ’traverseBU’, ’traverseTD’ */
public static interface ProcTree {
public void proc (TREE t);
}
<export_declarations>
/* methods available on all tree nodes */
/* some are available as static methods taking an argument of type TREE */
public int yyKind ();
static void writeNode (de.cocolab.reuse.CocktailWriter yyout)
throws java.io.IOException { ... }
public void traverseTD (ProcTree Proc) { ... }
public void traverseBU (ProcTree Proc) { ... }
public TREE reverse () { ... }
public void forall (ProcTree Proc) throws Throwable { ... }
public boolean isType (int Kind) { ... }
public static boolean check (TREE t) throws java.io.IOException { ... }
public void query () throws java.io.IOException { ... }
public boolean isEqual (TREE t) { ... }
public static void write (de.cocolab.reuse.CocktailWriter yyout)
throws java.io.IOException { ... }
public static TREE read (de.cocolab.reuse.CocktailReader yyin)
throws java.io.IOException { ... }
public void put (java.io.OutputStream s) { ... }
public void put (String fileName) { ... }
public static TREE get (java.io.InputStream s) { ... }
public static TREE get (String fileName) { ... }
public static TREE copy (TREE t) { ... }
public java.lang.Object clone () { ... }
static final int k<type_1> = 1;
static final int k<type_2> = 2;
...
public static class <type_1> extends TREE {
<children_and_attributes_of_type_1>
public <type_1> () { ... }
public <type_1> (<children_and_attributes_of_type_1>) { ... }
}
public static class <type_2> extends TREE {
<children_and_attributes_of_type_1>
public <type_2> () { ... }
public <type_2> (<children_and_attributes_of_type_2>) { ... }
}
...
static final String [] NodeNames = {
Ast 59

"NoTree", "<type_1>", "<type_2>", ... };


public static TREE make (int Kind) { ... }
public static void begin () {
<begin_code>
}
public static void close () {
<close_code>
}
static { begin (); }
}
Ast 60

Appendix 9: Predefined Type Specific Operations for C

/* int */
# define beginint(a)
# define closeint(a)
# define readint(a) fscanf (yyf, "%d", & a); 
# define writeint(a) fprintf (yyf, "%d", a); 
# define getint(a) yyGet ((char *) & a, sizeof (a));
# define putint(a) yyPut ((char *) & a, sizeof (a));
# define copyint(a, b)
# define equalint(a, b) (a) == (b)
# define writeXMLint(a) fprintf (yyf, "%d", a); 
/* short */
# define beginshort(a)
# define closeshort(a)
# define readshort(a) fscanf (yyf, "%hd", & a); 
# define writeshort(a) fprintf (yyf, "%hd", a); 
# define getshort(a) yyGet ((char *) & a, sizeof (a));
# define putshort(a) yyPut ((char *) & a, sizeof (a));
# define copyshort(a, b)
# define equalshort(a, b) (a) == (b)
# define writeXMLshort(a) fprintf (yyf, "%hd", a); 
/* long */
# define beginlong(a)
# define closelong(a)
# define readlong(a) fscanf (yyf, "%ld", & a); 
# define writelong(a) fprintf (yyf, "%ld", a); 
# define getlong(a) yyGet ((char *) & a, sizeof (a));
# define putlong(a) yyPut ((char *) & a, sizeof (a));
# define copylong(a, b)
# define equallong(a, b) (a) == (b)
# define writeXMLlong(a) fprintf (yyf, "%ld", a); 
/* unsigned */
# define beginunsigned(a)
# define closeunsigned(a)
# define readunsigned(a) fscanf (yyf, "%u", & a); 
# define writeunsigned(a) fprintf (yyf, "%u", a); 
# define getunsigned(a) yyGet ((char *) & a, sizeof (a));
# define putunsigned(a) yyPut ((char *) & a, sizeof (a));
# define copyunsigned(a, b)
# define equalunsigned(a, b) (a) == (b)
# define writeXMLunsigned(a) fprintf (yyf, "%u", a); 
/* float */
# define beginfloat(a)
# define closefloat(a)
# define readfloat(a) fscanf (yyf, "%g", & a); 
# define writefloat(a) fprintf (yyf, "%g", a); 
# define getfloat(a) yyGet ((char *) & a, sizeof (a));
# define putfloat(a) yyPut ((char *) & a, sizeof (a));
# define copyfloat(a, b)
# define equalfloat(a, b) (a) == (b)
# define writeXMLfloat(a) fprintf (yyf, "%g", a); 
/* double */
# define begindouble(a)
# define closedouble(a)
# define readdouble(a) fscanf (yyf, "%g", & a); 
# define writedouble(a) fprintf (yyf, "%g", a); 
Ast 61

# define getdouble(a) yyGet ((char *) & a, sizeof (a));


# define putdouble(a) yyPut ((char *) & a, sizeof (a));
# define copydouble(a, b)
# define equaldouble(a, b) (a) == (b)
# define writeXMLdouble(a) fprintf (yyf, "%g", a); 
/* rbool */
# define beginrbool(a)
# define closerbool(a)
# define readrbool(a) a = fgetc (yyf) == ’T’;
# define writerbool(a) fputc (a ? ’T’ : ’F’, yyf); 
# define getrbool(a) yyGet ((char *) & a, sizeof (a));
# define putrbool(a) yyPut ((char *) & a, sizeof (a));
# define copyrbool(a, b)
# define equalrbool(a, b) (a) == (b)
# define writeXMLrbool(a) fputc (a ? ’T’ : ’F’, yyf); 
/* char */
# define beginchar(a)
# define closechar(a)
# define readchar(a) a = fgetc (yyf);
# define writechar(a) fputc (a, yyf); 
# define getchar(a) yyGet ((char *) & a, sizeof (a));
# define putchar(a) yyPut ((char *) & a, sizeof (a));
# define copychar(a, b)
# define equalchar(a, b) (a) == (b)
# define writeXMLchar(a) yyWriteCharXML (a); 
/* vchar */ 
# define beginvchar(a) 
# define closevchar(a) 
# define readvchar(a) fscanf (yyf, vFc, & a); 
# define writevchar(a) fprintf (yyf, vFc, a); 
# define getvchar(a) yyGet ((char *) & a, sizeof (a)); 
# define putvchar(a) yyPut ((char *) & a, sizeof (a)); 
# define copyvchar(a, b) 
# define equalvchar(a, b) (a) == (b) 
# define writeXMLvchar(a) yyWriteCharXML (a); 
/* tString */
# define begintString(a)
# define closetString(a)
# define readtString(a)
# define writetString(a) fputs (a, yyf); 
# define gettString(a)
# define puttString(a)
# define copytString(a, b)
# define equaltString(a, b) strcmp (a, (b)) == 0
# define writeXMLtString(a) yyWriteStringXML (a); 
/* tStringRef */
# define begintStringRef(a) a = NoString;
# define closetStringRef(a)
# define readtStringRef(a) { char yys [1024]; \
fgets (yys, 1024, yyf); \ 
ungetc (’\n’, yyf); \ 
a = PutString (yys, strlen (yys) - 1); }
# define writetStringRef(a) WriteString (yyf, a);
# define gettStringRef(a) { char yys [1024]; \
fgets (yys, 1024, yyf); \ 
a = PutString (yys, strlen (yys) - 1); }
# define puttStringRef(a) { WriteString (yyf, a); \
Ast 62

fputc (’\n’, yyf); } 


# define copytStringRef(a, b)
# define equaltStringRef(a, b) (a) == (b)
# define writeXMLtStringRef(a) yyWriteStringXML (StGetCStr (a)); 
/* tWStringRef */ 
# define begintWStringRef(a) a = NoWString; 
# define closetWStringRef(a) 
# define readtWStringRef(a) 
# define writetWStringRef(a) WriteWString (yyf, a); 
# define gettWStringRef(a) 
# define puttWStringRef(a) { WriteWString (yyf, a); fputc (’\n’, yyf); } 
# define copytWStringRef(a, b) 
# define equaltWStringRef(a, b) (a) == (b) 
# define writeXMLtWStringRef(a) yyWriteWStringXML (StGetWCStr (a)); 
/* vtStringRef */ 
# define beginvtStringRef(a) a = vNoString; 
# define closevtStringRef(a) 
# define readvtStringRef(a) 
# define writevtStringRef(a) vWriteString (yyf, a); 
# define getvtStringRef(a) 
# define putvtStringRef(a) { vWriteString (yyf, a); fputc (’\n’, yyf); } 
# define copyvtStringRef(a, b) 
# define equalvtStringRef(a, b) (a) == (b) 
# define writeXMLvtStringRef(a) yyWriteStringXML (vStGetCStr (a)); 
/* tIdent */
# define begintIdent(a) a = NoIdent;
# define closetIdent(a)
# define readtIdent(a) a = yyReadIdent ();
# define writetIdent(a) WriteIdent (yyf, a);
# define gettIdent(a) yyGetIdent (& a);
# define puttIdent(a) yyPutIdent (a);
# define copytIdent(a, b)
# define equaltIdent(a, b) (a) == (b)
# define writeXMLtIdent(a) yyWriteStringXML (GetCStr (a)); 
/* tWIdent */ 
# define begintWIdent(a) a = NoWIdent; 
# define closetWIdent(a) 
# define readtWIdent(a) 
# define writetWIdent(a) WriteWIdent (yyf, a); 
# define gettWIdent(a) 
# define puttWIdent(a) { WriteWIdent (yyf, a); fputc (’\n’, yyf); } 
# define copytWIdent(a, b) 
# define equaltWIdent(a, b) (a) == (b) 
# define writeXMLtWIdent(a) yyWriteWStringXML (GetWCStr (a)); 
/* vtIdent */ 
# define beginvtIdent(a) a = vNoIdent; 
# define closevtIdent(a) 
# define readvtIdent(a) 
# define writevtIdent(a) vWriteIdent (yyf, a); 
# define getvtIdent(a) 
# define putvtIdent(a) { vWriteIdent (yyf, a); fputc (’\n’, yyf); } 
# define copyvtIdent(a, b) 
# define equalvtIdent(a, b) (a) == (b) 
# define writeXMLvtIdent(a) yyWriteStringXML (vGetCStr (a)); 
/* tSet */
# define begintSet(a)
# define closetSet(a)
Ast 63

# define readtSet(a) ReadSet (yyf, & a);


# define writetSet(a) WriteSet (yyf, & a);
# define gettSet(a)
# define puttSet(a)
# define copytSet(a, b)
# define equaltSet(a, b) IsEqual (& a, & b)
# define writeXMLtSet(a) WriteSet (yyf, & a); 
/* tPosition */
# define begintPosition(a) a = NoPosition;
# define closetPosition(a)
# define readtPosition(a) ReadPosition (yyf, & a);
# define writetPosition(a) WritePosition (yyf, a);
# define gettPosition(a) yyGet ((char *) & a, sizeof (a));
# define puttPosition(a) yyPut ((char *) & a, sizeof (a));
# define copytPosition(a, b)
# define equaltPosition(a, b) Compare (a, b) == 0
# define writeXMLtPosition(a) WritePosition (yyf, a); 
/* NodeHead */
# define beginNodeHead(a)
# define closeNodeHead(a)
# define readNodeHead(a)
# define writeNodeHead(a)
# define getNodeHead(a)
# define putNodeHead(a)
# define copyNodeHead(a, b)
# define equalNodeHead(a, b) rtrue
# define writeXMLNodeHead(a) 
Ast 64

Appendix 10: Predefined Type Specific Operations for Simple C++

/* int */
# define beginint(a)
# define closeint(a)
# define readint(a) fscanf (yyf, "%d", & a);
# define writeint(a) fprintf (yyf, "%d", a);
# define getint(a) yyGet ((char *) & a, sizeof (a));
# define putint(a) yyPut ((char *) & a, sizeof (a));
# define copyint(a, b)
# define equalint(a, b) (a) == (b)
# define writeXMLint(a) fprintf (yyf, "%d", a); 
/* short */
# define beginshort(a)
# define closeshort(a)
# define readshort(a) fscanf (yyf, "%hd", & a);
# define writeshort(a) fprintf (yyf, "%hd", a);
# define getshort(a) yyGet ((char *) & a, sizeof (a));
# define putshort(a) yyPut ((char *) & a, sizeof (a));
# define copyshort(a, b)
# define equalshort(a, b) (a) == (b)
# define writeXMLshort(a) fprintf (yyf, "%hd", a); 
/* long */
# define beginlong(a)
# define closelong(a)
# define readlong(a) fscanf (yyf, "%ld", & a);
# define writelong(a) fprintf (yyf, "%ld", a);
# define getlong(a) yyGet ((char *) & a, sizeof (a));
# define putlong(a) yyPut ((char *) & a, sizeof (a));
# define copylong(a, b)
# define equallong(a, b) (a) == (b)
# define writeXMLlong(a) fprintf (yyf, "%ld", a); 
/* unsigned */
# define beginunsigned(a)
# define closeunsigned(a)
# define readunsigned(a) fscanf (yyf, "%u", & a);
# define writeunsigned(a) fprintf (yyf, "%u", a);
# define getunsigned(a) yyGet ((char *) & a, sizeof (a));
# define putunsigned(a) yyPut ((char *) & a, sizeof (a));
# define copyunsigned(a, b)
# define equalunsigned(a, b) (a) == (b)
# define writeXMLunsigned(a) fprintf (yyf, "%u", a); 
/* float */
# define beginfloat(a)
# define closefloat(a)
# define readfloat(a) fscanf (yyf, "%g", & a);
# define writefloat(a) fprintf (yyf, "%g", a);
# define getfloat(a) yyGet ((char *) & a, sizeof (a));
# define putfloat(a) yyPut ((char *) & a, sizeof (a));
# define copyfloat(a, b)
# define equalfloat(a, b) (a) == (b)
# define writeXMLfloat(a) fprintf (yyf, "%g", a); 
/* double */
# define begindouble(a)
# define closedouble(a)
# define readdouble(a) fscanf (yyf, "%g", & a);
# define writedouble(a) fprintf (yyf, "%g", a);
Ast 65

# define getdouble(a) yyGet ((char *) & a, sizeof (a));


# define putdouble(a) yyPut ((char *) & a, sizeof (a));
# define copydouble(a, b)
# define equaldouble(a, b) (a) == (b)
# define writeXMLdouble(a) fprintf (yyf, "%g", a); 
/* rbool */
# define beginrbool(a)
# define closerbool(a)
# define readrbool(a) a = fgetc (yyf) == ’T’;
# define writerbool(a) fputc (a ? ’T’ : ’F’, yyf);
# define getrbool(a) yyGet ((char *) & a, sizeof (a));
# define putrbool(a) yyPut ((char *) & a, sizeof (a));
# define copyrbool(a, b)
# define equalrbool(a, b) (a) == (b)
# define writeXMLrbool(a) fputc (a ? ’T’ : ’F’, yyf); 
/* bool */
# define beginbool(a)
# define closebool(a)
# define readbool(a) a = fgetc (yyf) == ’T’;
# define writebool(a) fputc (a ? ’T’ : ’F’, yyf);
# define getbool(a) yyGet ((bool *) & a, sizeof (a));
# define putbool(a) yyPut ((bool *) & a, sizeof (a));
# define copybool(a, b)
# define equalbool(a, b) (a) == (b)
# define writeXMLbool(a) fputc (a ? ’T’ : ’F’, yyf); 
/* char */
# define beginchar(a)
# define closechar(a)
# define readchar(a) a = fgetc (yyf);
# define writechar(a) fputc (a, yyf);
# define getchar(a) yyGet ((char *) & a, sizeof (a));
# define putchar(a) yyPut ((char *) & a, sizeof (a));
# define copychar(a, b)
# define equalchar(a, b) (a) == (b)
# define writeXMLchar(a) yyWriteCharXML (a); 
/* vchar */ 
# define beginvchar(a) 
# define closevchar(a) 
# define readvchar(a) fscanf (yyf, vFc, & a); 
# define writevchar(a) fprintf (yyf, vFc, a); 
# define getvchar(a) yyGet ((char *) & a, sizeof (a)); 
# define putvchar(a) yyPut ((char *) & a, sizeof (a)); 
# define copyvchar(a, b) 
# define equalvchar(a, b) (a) == (b) 
# define writeXMLvchar(a) yyWriteCharXML (a); 
/* tString */
# define begintString(a)
# define closetString(a)
# define readtString(a)
# define writetString(a) fputs (a, yyf);
# define gettString(a)
# define puttString(a)
# define copytString(a, b)
# define equaltString(a, b) strcmp (a, (b)) == 0
# define writeXMLtString(a) yyWriteStringXML (a); 
/* tStringRef */
# define begintStringRef(a) a = String_PREFIX NoString;
Ast 66

# define closetStringRef(a)
# define readtStringRef(a) { char yys [1024]; \
fgets (yys, 1024, yyf); \
ungetc (’\n’, yyf); \
a = String_PREFIX PutString (yys, strlen (yys) - 1); }
# define writetStringRef(a) String_PREFIX WriteString (yyf, a);
# define gettStringRef(a) { char yys [1024]; \
fgets (yys, 1024, yyf); \
a = String_PREFIX PutString (yys, strlen (yys) - 1); }
# define puttStringRef(a) { String_PREFIX WriteString (yyf, a); \
fputc (’\n’, yyf); }
# define copytStringRef(a, b)
# define equaltStringRef(a, b) (a) == (b)
# define writeXMLtStringRef(a) yyWriteStringXML (String_PREFIX StGetCStr (a)); 
/* tWStringRef */ 
# define begintWStringRef(a) a = String_PREFIX NoWString; 
# define closetWStringRef(a) 
# define readtWStringRef(a) 
# define writetWStringRef(a) String_PREFIX WriteWString (yyf, a); 
# define gettWStringRef(a) 
# define puttWStringRef(a) { String_PREFIX WriteWString (yyf, a); \ 
fputc (’\n’, yyf); } 
# define copytWStringRef(a, b) 
# define equaltWStringRef(a, b) (a) == (b) 
# define writeXMLtWStringRef(a) yyWriteWStringXML (String_PREFIX StGetWCStr (a)); 
/* vtStringRef */ 
# define beginvtStringRef(a) a = String_PREFIX vNoString; 
# define closevtStringRef(a) 
# define readvtStringRef(a) 
# define writevtStringRef(a) String_PREFIX vWriteString (yyf, a); 
# define getvtStringRef(a) 
# define putvtStringRef(a) { String_PREFIX vWriteString (yyf, a); \ 
fputc (’\n’, yyf); } 
# define copyvtStringRef(a, b) 
# define equalvtStringRef(a, b) (a) == (b) 
# define writeXMLvtStringRef(a) yyWriteStringXML (String_PREFIX vStGetCStr (a)); 
/* tIdent */
# define begintIdent(a) a = Idents_PREFIX NoIdent;
# define closetIdent(a)
# define readtIdent(a) a = yyReadIdent ();
# define writetIdent(a) Idents_PREFIX WriteIdent (yyf, a);
# define gettIdent(a) yyGetIdent (& a);
# define puttIdent(a) yyPutIdent (a);
# define copytIdent(a, b)
# define equaltIdent(a, b) (a) == (b)
# define writeXMLtIdent(a) yyWriteStringXML (Idents_PREFIX GetCStr (a)); 
/* tWIdent */ 
# define begintWIdent(a) a = Idents_PREFIX NoWIdent; 
# define closetWIdent(a) 
# define readtWIdent(a) 
# define writetWIdent(a) Idents_PREFIX WriteWIdent (yyf, a); 
# define gettWIdent(a) 
# define puttWIdent(a) { Idents_PREFIX WriteWIdent (yyf, a); \ 
fputc (’\n’, yyf); } 
# define copytWIdent(a, b) 
# define equaltWIdent(a, b) (a) == (b) 
# define writeXMLtWIdent(a) yyWriteWStringXML (Idents_PREFIX GetWCStr (a)); 
Ast 67

/* vtIdent */ 
# define beginvtIdent(a) a = Idents_PREFIX vNoIdent; 
# define closevtIdent(a) 
# define readvtIdent(a) 
# define writevtIdent(a) Idents_PREFIX vWriteIdent (yyf, a); 
# define getvtIdent(a) 
# define putvtIdent(a) { Idents_PREFIX vWriteIdent (yyf, a); \ 
fputc (’\n’, yyf); } 
# define copyvtIdent(a, b) 
# define equalvtIdent(a, b) (a) == (b) 
# define writeXMLvtIdent(a) yyWriteStringXML (Idents_PREFIX vGetCStr (a)); 
/* tSet */
# define begintSet(a)
# define closetSet(a)
# define readtSet(a) ReadSet (yyf, & a);
# define writetSet(a) WriteSet (yyf, & a);
# define gettSet(a)
# define puttSet(a)
# define copytSet(a, b)
# define equaltSet(a, b) IsEqual (& a, & b)
# define writeXMLtSet(a) WriteSet (yyf, & a); 
/* tPosition */
# define begintPosition(a) a = NoPosition;
# define closetPosition(a)
# define readtPosition(a) ReadPosition (yyf, & a);
# define writetPosition(a) WritePosition (yyf, a);
# define gettPosition(a) yyGet ((char *) & a, sizeof (a));
# define puttPosition(a) yyPut ((char *) & a, sizeof (a));
# define copytPosition(a, b)
# define equaltPosition(a, b) Compare (a, b) == 0
# define writeXMLtPosition(a) WritePosition (yyf, a); 
/* NodeHead */
# define beginNodeHead(a)
# define closeNodeHead(a)
# define readNodeHead(a)
# define writeNodeHead(a)
# define getNodeHead(a)
# define putNodeHead(a)
# define copyNodeHead(a, b)
# define equalNodeHead(a, b) true
# define writeXMLNodeHead(a) 
Ast 68

Appendix 11: Predefined Type Specific Operations for Proper C++

// int
# define beginint(a)
# define closeint(a)
# define readint(a) fscanf (yyf, "%d", & a);
# define writeint(a) fprintf (yyf, "%d", a);
# define getint(a) yyGet ((char *) & a, sizeof (a));
# define putint(a) yyPut ((char *) & a, sizeof (a));
# define copyint(a, b)
# define equalint(a, b) (a) == (b)
# define writeXMLint(a) fprintf (yyf, "%d", a); 
// short
# define beginshort(a)
# define closeshort(a)
# define readshort(a) fscanf (yyf, "%hd", & a);
# define writeshort(a) fprintf (yyf, "%hd", a);
# define getshort(a) yyGet ((char *) & a, sizeof (a));
# define putshort(a) yyPut ((char *) & a, sizeof (a));
# define copyshort(a, b)
# define equalshort(a, b) (a) == (b)
# define writeXMLshort(a) fprintf (yyf, "%hd", a); 
// long
# define beginlong(a)
# define closelong(a)
# define readlong(a) fscanf (yyf, "%ld", & a);
# define writelong(a) fprintf (yyf, "%ld", a);
# define getlong(a) yyGet ((char *) & a, sizeof (a));
# define putlong(a) yyPut ((char *) & a, sizeof (a));
# define copylong(a, b)
# define equallong(a, b) (a) == (b)
# define writeXMLlong(a) fprintf (yyf, "%ld", a); 
// unsigned
# define beginunsigned(a)
# define closeunsigned(a)
# define readunsigned(a) fscanf (yyf, "%u", & a);
# define writeunsigned(a) fprintf (yyf, "%u", a);
# define getunsigned(a) yyGet ((char *) & a, sizeof (a));
# define putunsigned(a) yyPut ((char *) & a, sizeof (a));
# define copyunsigned(a, b)
# define equalunsigned(a, b) (a) == (b)
# define writeXMLunsigned(a) fprintf (yyf, "%u", a); 
// float
# define beginfloat(a)
# define closefloat(a)
# define readfloat(a) fscanf (yyf, "%g", & a);
# define writefloat(a) fprintf (yyf, "%g", a);
# define getfloat(a) yyGet ((char *) & a, sizeof (a));
# define putfloat(a) yyPut ((char *) & a, sizeof (a));
# define copyfloat(a, b)
# define equalfloat(a, b) (a) == (b)
# define writeXMLfloat(a) fprintf (yyf, "%g", a); 
// double
# define begindouble(a)
# define closedouble(a)
# define readdouble(a) fscanf (yyf, "%g", & a);
# define writedouble(a) fprintf (yyf, "%g", a);
Ast 69

# define getdouble(a) yyGet ((char *) & a, sizeof (a));


# define putdouble(a) yyPut ((char *) & a, sizeof (a));
# define copydouble(a, b)
# define equaldouble(a, b) (a) == (b)
# define writeXMLdouble(a) fprintf (yyf, "%g", a); 
// rbool
# define beginrbool(a)
# define closerbool(a)
# define readrbool(a) a = fgetc (yyf) == ’T’;
# define writerbool(a) fputc (a ? ’T’ : ’F’, yyf);
# define getrbool(a) yyGet ((char *) & a, sizeof (a));
# define putrbool(a) yyPut ((char *) & a, sizeof (a));
# define copyrbool(a, b)
# define equalrbool(a, b) (a) == (b)
# define writeXMLrbool(a) fputc (a ? ’T’ : ’F’, yyf); 
// bool 
# define beginbool(a) 
# define closebool(a) 
# define readbool(a) a = fgetc (yyf) == ’T’; 
# define writebool(a) fputc (a ? ’T’ : ’F’, yyf); 
# define getbool(a) yyGet ((bool *) & a, sizeof (a)); 
# define putbool(a) yyPut ((bool *) & a, sizeof (a)); 
# define copybool(a, b) 
# define equalbool(a, b) (a) == (b) 
# define writeXMLbool(a) fputc (a ? ’T’ : ’F’, yyf); 
// char
# define beginchar(a)
# define closechar(a)
# define readchar(a) a = fgetc (yyf);
# define writechar(a) fputc (a, yyf);
# define getchar(a) yyGet ((char *) & a, sizeof (a));
# define putchar(a) yyPut ((char *) & a, sizeof (a));
# define copychar(a, b)
# define equalchar(a, b) (a) == (b)
# define writeXMLchar(a) yyWriteCharXML (a); 
// vchar 
# define beginvchar(a) 
# define closevchar(a) 
# define readvchar(a) fscanf (yyf, vFc, & a); 
# define writevchar(a) fprintf (yyf, vFc, a); 
# define getvchar(a) yyGet ((char *) & a, sizeof (a)); 
# define putvchar(a) yyPut ((char *) & a, sizeof (a)); 
# define copyvchar(a, b) 
# define equalvchar(a, b) (a) == (b) 
# define writeXMLvchar(a) yyWriteCharXML (a); 
// tString
# define begintString(a)
# define closetString(a)
# define readtString(a)
# define writetString(a) fputs (a, yyf);
# define gettString(a)
# define puttString(a)
# define copytString(a, b)
# define equaltString(a, b) strcmp (a, (b)) == 0
# define writeXMLtString(a) yyWriteStringXML (a); 
// tStringRef
# define begintStringRef(a) a = String_PREFIX NoString;
Ast 70

# define closetStringRef(a)
# define readtStringRef(a) { char yys [1024]; \
fgets (yys, 1024, yyf); \
ungetc (’\n’, yyf); \
a = String_PREFIX PutString (yys, strlen (yys) - 1); }
# define writetStringRef(a) String_PREFIX WriteString (yyf, a);
# define gettStringRef(a) { char yys [1024]; \
fgets (yys, 1024, yyf); \
a = String_PREFIX PutString (yys, strlen (yys) - 1); }
# define puttStringRef(a) { String_PREFIX WriteString (yyf, a); \
fputc (’\n’, yyf); }
# define copytStringRef(a, b)
# define equaltStringRef(a, b) (a) == (b)
# define writeXMLtStringRef(a) yyWriteStringXML (String_PREFIX StGetCStr (a)); 
// tWStringRef 
# define begintWStringRef(a) a = String_PREFIX NoWString; 
# define closetWStringRef(a) 
# define readtWStringRef(a) 
# define writetWStringRef(a) String_PREFIX WriteWString (yyf, a); 
# define gettWStringRef(a) 
# define puttWStringRef(a) { String_PREFIX WriteWString (yyf, a); \ 
fputc (’\n’, yyf); } 
# define copytWStringRef(a, b) 
# define equaltWStringRef(a, b) (a) == (b) 
# define writeXMLtWStringRef(a) yyWriteWStringXML (String_PREFIX StGetWCStr (a)); 
// vtStringRef 
# define beginvtStringRef(a) a = String_PREFIX vNoString; 
# define closevtStringRef(a) 
# define readvtStringRef(a) 
# define writevtStringRef(a) String_PREFIX vWriteString (yyf, a); 
# define getvtStringRef(a) 
# define putvtStringRef(a) { String_PREFIX vWriteString (yyf, a); \ 
fputc (’\n’, yyf); } 
# define copyvtStringRef(a, b) 
# define equalvtStringRef(a, b) (a) == (b) 
# define writeXMLvtStringRef(a) yyWriteStringXML (String_PREFIX vStGetCStr (a)); 
// tIdent
# define begintIdent(a) a = Idents_PREFIX NoIdent;
# define closetIdent(a)
# define readtIdent(a) a = yyReadIdent ();
# define writetIdent(a) Idents_PREFIX WriteIdent (yyf, a);
# define gettIdent(a) yyGetIdent (& a);
# define puttIdent(a) yyPutIdent (a);
# define copytIdent(a, b)
# define equaltIdent(a, b) (a) == (b)
# define writeXMLtIdent(a) yyWriteStringXML (Idents_PREFIX GetCStr (a)); 
// tWIdent 
# define begintWIdent(a) a = Idents_PREFIX NoWIdent; 
# define closetWIdent(a) 
# define readtWIdent(a) 
# define writetWIdent(a) Idents_PREFIX WriteWIdent (yyf, a); 
# define gettWIdent(a) 
# define puttWIdent(a) { Idents_PREFIX WriteWIdent (yyf, a); \ 
fputc (’\n’, yyf); } 
# define copytWIdent(a, b) 
# define equaltWIdent(a, b) (a) == (b) 
# define writeXMLtWIdent(a) yyWriteWStringXML (Idents_PREFIX GetWCStr (a)); 
Ast 71

// vtIdent 
# define beginvtIdent(a) a = Idents_PREFIX vNoIdent; 
# define closevtIdent(a) 
# define readvtIdent(a) 
# define writevtIdent(a) Idents_PREFIX vWriteIdent (yyf, a); 
# define getvtIdent(a) 
# define putvtIdent(a) { Idents_PREFIX vWriteIdent (yyf, a); \ 
fputc (’\n’, yyf); } 
# define copyvtIdent(a, b) 
# define equalvtIdent(a, b) (a) == (b) 
# define writeXMLvtIdent(a) yyWriteStringXML (Idents_PREFIX vGetCStr (a)); 
// tSet
# define begintSet(a)
# define closetSet(a)
# define readtSet(a) ReadSet (yyf, & a);
# define writetSet(a) WriteSet (yyf, & a);
# define gettSet(a)
# define puttSet(a)
# define copytSet(a, b)
# define equaltSet(a, b) IsEqual (& a, & b)
# define writeXMLtSet(a) WriteSet (yyf, & a); 
// tPosition
# define begintPosition(a) a = NoPosition;
# define closetPosition(a)
# define readtPosition(a) ReadPosition (yyf, & a);
# define writetPosition(a) WritePosition (yyf, a);
# define gettPosition(a) yyGet ((char *) & a, sizeof (a));
# define puttPosition(a) yyPut ((char *) & a, sizeof (a));
# define copytPosition(a, b)
# define equaltPosition(a, b) Compare (a, b) == 0
# define writeXMLtPosition(a) WritePosition (yyf, a); 
// NodeHead
# define beginNodeHead(a)
# define closeNodeHead(a)
# define readNodeHead(a)
# define writeNodeHead(a)
# define getNodeHead(a)
# define putNodeHead(a)
# define copyNodeHead(a, b)
# define equalNodeHead(a, b) true
# define writeXMLNodeHead(a) 
Ast 72

Appendix 12: Predefined Type Specific Operations for Modula-2


(* INTEGER *)
# define beginINTEGER(a)
# define closeINTEGER(a)
# define readINTEGER(a) a := IO.ReadI (yyf);
# define writeINTEGER(a) IO.WriteI (yyf, a, 0);
# define getINTEGER(a) yyGet (a);
# define putINTEGER(a) yyPut (a);
# define copyINTEGER(a, b)
# define equalINTEGER(a, b) a = b
# define writeXMLINTEGER(a) IO.WriteI (yyf, a, 0); 
(* SHORTINT *)
# define beginSHORTINT(a)
# define closeSHORTINT(a)
# define readSHORTINT(a) a := IO.ReadI (yyf);
# define writeSHORTINT(a) IO.WriteI (yyf, a, 0);
# define getSHORTINT(a) yyGet (a);
# define putSHORTINT(a) yyPut (a);
# define copySHORTINT(a, b)
# define equalSHORTINT(a, b) a = b
# define writeXMLSHORTINT(a) IO.WriteI (yyf, a, 0); 
(* LONGINT *)
# define beginLONGINT(a)
# define closeLONGINT(a)
# define readLONGINT(a) a := IO.ReadI (yyf);
# define writeLONGINT(a) IO.WriteI (yyf, a, 0);
# define getLONGINT(a) yyGet (a);
# define putLONGINT(a) yyPut (a);
# define copyLONGINT(a, b)
# define equalLONGINT(a, b) a = b
# define writeXMLLONGINT(a) IO.WriteI (yyf, a, 0); 
(* CARDINAL *)
# define beginCARDINAL(a)
# define closeCARDINAL(a)
# define readCARDINAL(a) a := IO.ReadI (yyf);
# define writeCARDINAL(a) IO.WriteCard (yyf, a, 0);
# define getCARDINAL(a) yyGet (a);
# define putCARDINAL(a) yyPut (a);
# define copyCARDINAL(a, b)
# define equalCARDINAL(a, b) a = b
# define writeXMLCARDINAL(a) IO.WriteCard (yyf, a, 0); 
(* SHORTCARD *)
# define beginSHORTCARD(a)
# define closeSHORTCARD(a)
# define readSHORTCARD(a) a := IO.ReadI (yyf);
# define writeSHORTCARD(a) IO.WriteCard (yyf, a, 0);
# define getSHORTCARD(a) yyGet (a);
# define putSHORTCARD(a) yyPut (a);
# define copySHORTCARD(a, b)
# define equalSHORTCARD(a, b) a = b
# define writeXMLSHORTCARD(a) IO.WriteCard (yyf, a, 0); 
(* LONGCARD *)
# define beginLONGCARD(a)
# define closeLONGCARD(a)
# define readLONGCARD(a) a := IO.ReadI (yyf);
# define writeLONGCARD(a) IO.WriteCard (yyf, a, 0);
Ast 73

# define getLONGCARD(a) yyGet (a);


# define putLONGCARD(a) yyPut (a);
# define copyLONGCARD(a, b)
# define equalLONGCARD(a, b) a = b
# define writeXMLLONGCARD(a) IO.WriteCard (yyf, a, 0); 
(* REAL *)
# define beginREAL(a)
# define closeREAL(a)
# define readREAL(a) a := IO.ReadR (yyf);
# define writeREAL(a) IO.WriteR (yyf, a, 0, 6, 1);
# define getREAL(a) yyGet (a);
# define putREAL(a) yyPut (a);
# define copyREAL(a, b)
# define equalREAL(a, b) a = b
# define writeXMLREAL(a) IO.WriteR (yyf, a, 0, 6, 1); 
(* LONGREAL *)
# define beginLONGREAL(a)
# define closeLONGREAL(a)
# define readLONGREAL(a) a := IO.ReadR (yyf);
# define writeLONGREAL(a) IO.WriteR (yyf, a, 0, 6, 1);
# define getLONGREAL(a) yyGet (a);
# define putLONGREAL(a) yyPut (a);
# define copyLONGREAL(a, b)
# define equalLONGREAL(a, b) a = b
# define writeXMLLONGREAL(a) IO.WriteR (yyf, a, 0, 6, 1); 
(* BOOLEAN *)
# define beginBOOLEAN(a)
# define closeBOOLEAN(a)
# define readBOOLEAN(a) a := IO.ReadB (yyf);
# define writeBOOLEAN(a) IO.WriteB (yyf, a);
# define getBOOLEAN(a) yyGet (a);
# define putBOOLEAN(a) yyPut (a);
# define copyBOOLEAN(a, b)
# define equalBOOLEAN(a, b) a = b
# define writeXMLBOOLEAN(a) IO.WriteB (yyf, a); 
(* CHAR *)
# define beginCHAR(a)
# define closeCHAR(a)
# define readCHAR(a) a := IO.ReadC (yyf);
# define writeCHAR(a) IO.WriteC (yyf, a);
# define getCHAR(a) yyGet (a);
# define putCHAR(a) yyPut (a);
# define copyCHAR(a, b)
# define equalCHAR(a, b) a = b
# define writeXMLCHAR(a) IO.WriteC (yyf, a); 
(* BITSET *)
# define beginBITSET(a)
# define closeBITSET(a)
# define readBITSET(a) yyReadHex (a);
# define writeBITSET(a) yyWriteHex (a);
# define getBITSET(a) yyGet (a);
# define putBITSET(a) yyPut (a);
# define copyBITSET(a, b)
# define equalBITSET(a, b) a = b
# define writeXMLCHAR(a) 
(* BYTE *)
# define beginBYTE(a)
Ast 74

# define closeBYTE(a)
# define readBYTE(a) yyReadHex (a);
# define writeBYTE(a) yyWriteHex (a);
# define getBYTE(a) yyGet (a);
# define putBYTE(a) yyPut (a);
# define copyBYTE(a, b)
# define equalBYTE(a, b) a = b
# define writeXMLBYTE(a) yyWriteHex (a); 
(* WORD *)
# define beginWORD(a)
# define closeWORD(a)
# define readWORD(a) yyReadHex (a);
# define writeWORD(a) yyWriteHex (a);
# define getWORD(a) yyGet (a);
# define putWORD(a) yyPut (a);
# define copyWORD(a, b)
# define equalWORD(a, b) a = b
# define writeXMLWORD(a) yyWriteHex (a); 
(* ADDRESS *)
# define beginADDRESS(a)
# define closeADDRESS(a)
# define readADDRESS(a) yyReadHex (a);
# define writeADDRESS(a) yyWriteHex (a);
# define getADDRESS(a) yyGet (a);
# define putADDRESS(a) yyPut (a);
# define copyADDRESS(a, b)
# define equalADDRESS(a, b) a = b
# define writeXMLADDRESS(a) yyWriteHex (a); 
(* tString *)
# define begintString(a)
# define closetString(a)
# define readtString(a) Strings.ReadL (yyf, a);
# define writetString(a) Strings.WriteL (yyf, a);
# define gettString(a) yyGet (a);
# define puttString(a) yyPut (a);
# define copytString(a, b)
# define equaltString(a, b) Strings.IsEqual (a, b)
# define writeXMLtString(a) Strings.WriteL (yyf, a); 
(* tWString *) 
# define begintWString(a) 
# define closetWString(a) 
# define readtWString(a) WStrings.ReadL (yyf, a); 
# define writetWString(a) WStrings.WriteL (yyf, a); 
# define gettWString(a) yyGet (a); 
# define puttWString(a) yyPut (a); 
# define copytWString(a, b) 
# define equaltWString(a, b) WStrings.IsEqual (a, b) 
# define writeXMLtWString(a) WStrings.WriteL (yyf, a); 
(* tStringRef *)
# define begintStringRef(a) a := StringM.NoString;
# define closetStringRef(a)
# define readtStringRef(a)
# define writetStringRef(a) StringM.WriteString (yyf, a);
# define gettStringRef(a)
# define puttStringRef(a)
# define copytStringRef(a, b)
# define equaltStringRef(a, b) a = b
Ast 75

# define writeXMLtStringRef(a) StringM.WriteString (yyf, a); 


(* tWStringRef *) 
# define begintWStringRef(a) a := StringM.NoString; 
# define closetWStringRef(a) 
# define readtWStringRef(a) 
# define writetWStringRef(a) StringM.WriteWString (yyf, a); 
# define gettWStringRef(a) 
# define puttWStringRef(a) 
# define copytWStringRef(a, b) 
# define equaltWStringRef(a, b) (a) = (b) 
# define writeXMLtWStringRef(a) StringM.WriteWString (yyf, a); 
(* tIdent *)
# define begintIdent(a) a := Idents.NoIdent;
# define closetIdent(a)
# define readtIdent(a) a := yyReadIdent ();
# define writetIdent(a) Idents.WriteIdent (yyf, a);
# define gettIdent(a) yyGetIdent (a);
# define puttIdent(a) yyPutIdent (a);
# define copytIdent(a, b)
# define equaltIdent(a, b) a = b
# define writeXMLtIdent(a) Idents.WriteIdent (yyf, a); 
(* tWIdent *) 
# define begintWIdent(a) a := Idents.NoWIdent; 
# define closetWIdent(a) 
# define readtWIdent(a) 
# define writetWIdent(a) Idents.WriteWIdent (yyf, a); 
# define gettWIdent(a) 
# define puttWIdent(a) 
# define copytWIdent(a, b) 
# define equaltWIdent(a, b) (a) = (b) 
# define writeXMLtWIdent(a) Idents.WriteWIdent (yyf, a); 
(* tText *)
# define begintText(a)
# define closetText(a)
# define readtText(a)
# define writetText(a) Texts.WriteText (yyf, a);
# define gettText(a)
# define puttText(a)
# define copytText(a, b)
# define equaltText(a, b) FALSE
# define writeXMLtText(a) Texts.WriteText (yyf, a); 
(* tSet *)
# define begintSet(a)
# define closetSet(a)
# define readtSet(a) Sets.ReadSet (yyf, a);
# define writetSet(a) Sets.WriteSet (yyf, a);
# define gettSet(a)
# define puttSet(a)
# define copytSet(a, b)
# define equaltSet(a, b) Sets.IsEqual (a, b)
# define writeXMLtSet(a) Sets.WriteSet (yyf, a); 
(* tRelation *)
# define begintRelation(a)
# define closetRelation(a)
# define readtRelation(a) Relation.ReadRelation (yyf, a);
# define writetRelation(a) Relation.WriteRelation (yyf, a);
# define gettRelation(a)
Ast 76

# define puttRelation(a)
# define copytRelation(a, b)
# define equaltRelation(a, b) Relation.IsEqual (a, b)
# define writeXMLtRelation(a) Relation.WriteRelation (yyf, a); 
(* tPosition *)
# define begintPosition(a) a := Position.NoPosition;
# define closetPosition(a)
# define readtPosition(a) Position.ReadPosition (yyf, a);
# define writetPosition(a) Position.WritePosition (yyf, a);
# define gettPosition(a) yyGet (a);
# define puttPosition(a) yyPut (a);
# define copytPosition(a, b)
# define equaltPosition(a, b) Position.Compare (a, b) = 0
# define writeXMLtPosition(a) Position.WritePosition (yyf, a); 
(* NodeHead *)
# define beginNodeHead(a)
# define closeNodeHead(a)
# define readNodeHead(a)
# define writeNodeHead(a)
# define getNodeHead(a)
# define putNodeHead(a)
# define copyNodeHead(a, b)
# define equalNodeHead(a, b) TRUE
# define writeXMLNodeHead(a) 
Ast 77

Appendix 13: Predefined Type Specific Operations for Java

Note: The operations getTYPE and putTYPE are not used.

// boolean
# define beginboolean(a)
# define closeboolean(a)
# define readboolean(a) a = yyin.readB ();
# define writeboolean(a) yyout.write (a);
# define copyboolean(a, b) a = (b);
# define equalboolean(a, b) (a) == (b)
# define writeXMLboolean(a) yyout.write (a); 
// byte
# define beginbyte(a)
# define closebyte(a)
# define readbyte(a) a = (byte) yyin.readI ();
# define writebyte(a) yyout.write ((int) a);
# define copybyte(a, b) a = (b);
# define equalbyte(a, b) (a) == (b)
# define writeXMLbyte(a) yyout.write ((int) a); 
// char
# define beginchar(a)
# define closechar(a)
# define readchar(a) a = (char) yyin.read ();
# define writechar(a) yyout.write (a);
# define copychar(a, b) a = (b);
# define equalchar(a, b) (a) == (b)
# define writeXMLchar(a) yyout.write (a); 
// double
# define begindouble(a)
# define closedouble(a)
# define readdouble(a) a = Double.parseDouble (yyin.readL (). \
toString ());
# define writedouble(a) yyout.write (Double.toString (a)); \
yyout.writeNl ();
# define copydouble(a, b) a = (b);
# define equaldouble(a, b) (a) == (b)
# define writeXMLdouble(a) yyout.write (Double.toString (a)); 
// float
# define beginfloat(a)
# define closefloat(a)
# define readfloat(a) a = yyin.readR ();
# define writefloat(a) yyout.write (a, 0, 0, 0);
# define copyfloat(a, b) a = (b);
# define equalfloat(a, b) (a) == (b)
# define writeXMLfloat(a) yyout.write (a, 0, 0, 0); 
// int
# define beginint(a)
# define closeint(a)
# define readint(a) a = yyin.readI ();
# define writeint(a) yyout.write (a);
# define copyint(a, b) a = (b);
# define equalint(a, b) (a) == (b)
# define writeXMLint(a) yyout.write (a); 
// long
# define beginlong(a)
Ast 78

# define closelong(a)
# define readlong(a) a = Long.parseLong (yyin.readL ().toString ());
# define writelong(a) yyout.write (Long.toString (a)); \
yyout.writeNl ();
# define copylong(a, b) a = (b);
# define equallong(a, b) (a) == (b)
# define writeXMLlong(a) yyout.write (Long.toString (a)); 
// short
# define beginshort(a)
# define closeshort(a)
# define readshort(a) a = (short) yyin.readI ();
# define writeshort(a) yyout.write ((int) a);
# define copyshort(a, b) a = (b);
# define equalshort(a, b) (a) == (b)
# define writeXMLshort(a) yyout.write ((int) a); 
// Ident
# define beginIdent(a)
# define closeIdent(a)
# define readIdent(a) a = Idents_PREFIX makeIdent (yyin.readL ());
# define writeIdent(a) yyout.write (a); 
# define copyIdent(a, b) a = (b);
# define equalIdent(a, b) (a.equals (b))
# define writeXMLIdent(a) yyout.write (a); 
// Position
# define beginPosition(a) a = Position.NoPosition;
# define closePosition(a)
# define readPosition(a) a = new Position (yyin);
# define writePosition(a) yyout.write (a);
# define copyPosition(a, b) a = (b);
# define equalPosition(a, b) (a.compareTo (b) == 0)
# define writeXMLPosition(a) yyout.write (a); 
// NodeHead
# define beginNodeHead(a)
# define closeNodeHead(a) **not used**
# define readNodeHead(a)
# define writeNodeHead(a)
# define copyNodeHead(a, b)
# define equalNodeHead(a, b) true
# define writeXMLNodeHead(a) 
Ast 1

Contents

1. Introduction ............................................................................................................ 1
2. Specification Language ........................................................................................... 1
2.1. Node Types ............................................................................................................. 1
2.2. Children .................................................................................................................. 2
2.3. Attributes ................................................................................................................ 2
2.4. Declare Clause ........................................................................................................ 3
2.5. Extensions ............................................................................................................... 3
2.6. Multiple Inheritance ............................................................................................... 4
2.7. Modules .................................................................................................................. 5
2.8. Properties ................................................................................................................ 6
2.9. Subunits .................................................................................................................. 8
2.10. Views ...................................................................................................................... 8
2.11. Prefixing .................................................................................................................. 10
2.12. Reversal of Lists ..................................................................................................... 11
2.12.1. LR Parsers ............................................................................................................... 11
2.12.2. LL Parsers ............................................................................................................... 12
2.13. Target Code ............................................................................................................. 12
2.13.1. C and C++ ............................................................................................................... 13
2.13.2. Modula-2 ................................................................................................................ 14
2.13.3. Java ......................................................................................................................... 14
2.14. Common Node Fields ............................................................................................. 14
2.15. Type Specific Operations ........................................................................................ 15
2.16. Attribute Initialization ............................................................................................ 19
2.17. Storage Management .............................................................................................. 19
3. About the Generated Program Module ................................................................... 20
3.1. Procedural Mapping ............................................................................................... 21
3.2. Object-Oriented Mapping ....................................................................................... 24
3.3. Graph Browsers ...................................................................................................... 26
4. Using the Generated Program Module ................................................................... 31
4.1. C .............................................................................................................................. 32
4.2. Simple C++ ............................................................................................................. 33
4.3. Proper C++ ............................................................................................................. 34
4.4. Modula-2 ................................................................................................................ 35
4.5. Java ......................................................................................................................... 36
5. Related Research .................................................................................................... 36
5.1. Variant Records ....................................................................................................... 36
5.2. Type Extensions ...................................................................................................... 37
5.3. Context-Free Grammars ......................................................................................... 37
5.4. Attribute Grammars ................................................................................................ 38
Ast 2

5.5. Interface Description Language (IDL) ................................................................... 38


5.6. Attribute Coupled Grammars ................................................................................. 38
5.7. Object-Oriented Languages .................................................................................... 38
5.8. Tree Grammars ....................................................................................................... 39
6. Hints on Specifying Abstract Syntax ...................................................................... 39
7. Examples ................................................................................................................ 40
8. Experiences ............................................................................................................. 40
9. Usage ...................................................................................................................... 41
References .............................................................................................................. 43
Appendix 1: Syntax of the Specification Language ............................................... 45
Appendix 2: Concrete Syntax of the Example Language MiniLAX ...................... 48
Appendix 3: Abstract Syntax of the Example Language MiniLAX ...................... 49
Appendix 4: Generated Header File for C .............................................................. 50
Appendix 5: Generated Header File for Simple C++ ............................................. 52
Appendix 6: Generated Header File for Proper C++ .............................................. 54
Appendix 7: Generated Definition Module for Modula-2 ...................................... 56
Appendix 8: Generated Declarations for Java ........................................................ 58
Appendix 9: Predefined Type Specific Operations for C ........................................ 60
Appendix 10: Predefined Type Specific Operations for Simple C++ ..................... 64
Appendix 11: Predefined Type Specific Operations for Proper C++ ..................... 68
Appendix 12: Predefined Type Specific Operations for Modula-2 ........................ 72
Appendix 13: Predefined Type Specific Operations for Java ................................. 77

You might also like