Ast
Ast
J. Grosch
COCOLAB - DATENVERARBEITUNG
ACHERN, GERMANY
Cocktail
Josef Grosch
Document No. 15
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.
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.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.
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
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.
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
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
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 = ... .
> .
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.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
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.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.*;
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
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.
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);
}
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.
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
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
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".
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
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.1. C
abstract syntax:
Expr = [Pos: tPosition] <
Binary = Lop: Expr Rop: Expr [Operator: int] .
Unary = Expr [Operator] .
IntConst = [Value] .
RealConst = [Value: float] .
> .
4.4. Modula-2
abstract syntax:
Expr = [Pos: tPosition] <
Binary = Lop: Expr Rop: Expr [Operator: INTEGER] .
Unary = Expr [Operator] .
IntConst = [Value] .
RealConst = [Value: REAL] .
> .
4.5. Java
abstract syntax:
Expr = [Pos: Position] <
Binary = Lop: Expr Rop: Expr [Operator: int] .
Unary = Expr [Operator] .
IntConst = [Value] .
RealConst = [Value: float] .
> .
5. Related Research
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.
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
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.
Table 8: Comparison of notions from the areas of trees, types, and object-oriented programming
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
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
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
/* 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 Character .
> .
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
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
# 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
# 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
# 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
/* 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
/* 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 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
// 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 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
# 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 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
// 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