Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
Crown Copyright (c) 1997, 1998
This TenDRA(r) Computer Program is subject to Copyright
owned by the United Kingdom Secretary of State for Defence
acting through the Defence Evaluation and Research Agency
(DERA). It is made available to Recipients with a
royalty-free licence for its use, reproduction, transfer
to other parties and amendment for any purpose not excluding
product development provided that any such use et cetera
shall be deemed to be acceptance of the following conditions:-
(1) Its Recipients shall ensure that this Notice is
reproduced upon any copies or amended versions of it;
(2) Any amended version of it shall be clearly marked to
show both the nature of and the organisation responsible
for the relevant amendment or amendments;
(3) Its onward transfer from a recipient to another
party shall be deemed to be that party's acceptance of
these conditions;
(4) DERA gives no warranty or assurance as to its
quality or suitability for any purpose and DERA accepts
no liability whatsoever in relation to any use to which
it may be put.
*/
/*
TYPE SYSTEM SPECIFICATION
The main type system for the program is specified using the calculus
tool. This generates two representations of the type algebra, c_class.
The files in obj_tok describe the output type system abstractly in terms
of the TenDRA '#pragma token' constructs. These may be used to apply
extremely rigorous type checking to the program. The files in obj_c
give the implementation of the type system in C terms. The
implementation specific parts of the program, including the memory
management, is handled in obj_c/c_class.c.
*/
ALGEBRA c_class (1.1) :
/*
PRIMITIVE TYPES
These are the primitive types from which the algebra is built. int,
unsigned and string are self-explanitory. BITSTREAM_P represents a
series of bits, and is used to hold any compiled code. PPTOKEN_P
represents a list of preprocessing tokens, and is used in macro
definitions etc.
*/
int = "int" ;
unsigned = "unsigned" ;
string = "character *" ;
ulong_type (ulong) = "unsigned long" ;
BITSTREAM_P (bits) = "struct bits_tag *" ;
PPTOKEN_P (pptok) = "struct pptok_tag *" ;
/*
TYPE REPRESENTING A CONST-VOLATILE SPECIFIER
The const and volatile type qualifiers are represented by an
enumeration type (which is actually a bit pattern). An additional
value is used to indicate lvalues. Type linkage specifiers are also
included.
*/
enum !CV_SPEC (cv) = {
none = 0,
const = 1 << 0,
volatile = 1 << 1,
lvalue = 1 << 2,
c = 1 << 3,
cpp = 1 << 4,
qual = const | volatile,
mask = qual | lvalue,
language = c | cpp
} ;
/*
TYPE REPRESENTING A BUILT-IN TYPE
The built-in types have two representations - as a BASE_TYPE (see below)
or as a BUILTIN_TYPE. The latter is used primarily as an index into
various tables.
*/
enum !BUILTIN_TYPE (ntype) = {
none = 0,
char = 1,
schar = 2,
uchar = 3,
sshort = 4,
ushort = 5,
sint = 6,
uint = 7,
slong = 8,
ulong = 9,
sllong = 10,
ullong = 11,
float = 12,
double = 13,
ldouble = 14,
void = 15,
bottom = 16,
bool = 17,
ptrdiff_t = 18,
size_t = 19,
wchar_t = 20,
ellipsis = 21
} ;
/*
TYPE REPRESENTING A BASIC TYPE
The basic types are represented by an enumeration type (which is
actually a bit pattern except for the named type component). The
composite bitpatterns for the maximally valid combinations are also
given. The elaborated type keys are also represented by this type.
*/
enum !BASE_TYPE (btype) = {
none = 0,
class = 1,
struct_ = 2,
union_ = 3,
enum_ = 4,
alias = 5,
any = 6,
named = 7,
signed = 1 << 3,
unsigned = 1 << 4,
char = 1 << 5,
short = 1 << 6,
int = 1 << 7,
long = 1 << 8,
long2 = 1 << 9,
float = 1 << 10,
double = 1 << 11,
void = 1 << 12,
bottom = 1 << 13,
bool = 1 << 14,
ptrdiff_t = 1 << 15,
size_t = 1 << 16,
wchar_t = 1 << 17,
schar = signed | char,
uchar = unsigned | char,
sshort = signed | short | int,
ushort = unsigned | short | int,
sint = signed | int,
uint = unsigned | int,
slong = signed | long | int,
ulong = unsigned | long | int,
llong = long | long2,
sllong = signed | llong | int,
ullong = unsigned | llong | int,
ldouble = long | double,
ellipsis = 1 << 18,
star = 1 << 19,
template = 1 << 20,
typename = 1 << 21,
args = 1 << 22,
keyword = float | double | void | bool | wchar_t,
other = keyword | bottom | ptrdiff_t | size_t,
arith = int | float,
scalar = int | float | star
} ;
/*
TYPE REPRESENTING AN INTEGRAL TYPE
An integral type can be one of the built-in types, an integral promotion
type or an arithmetic conversion type. The promotion type for each
integral type is stored as part of its definition.
*/
union INT_TYPE (itype) = {
TYPE prom ;
LIST TYPE cases ;
BUILTIN_TYPE unprom = "ntype_none" ;
ulong_type itok = "LINK_NONE" ;
ulong_type ntok = "LINK_NONE" ;
ulong_type diag = "LINK_NONE" ;
} + {
basic -> {
/* Built-in types */
BASE_TYPE rep ;
BUILTIN_TYPE no ;
},
bitfield -> {
/* Bitfield types */
TYPE sub ;
BASE_TYPE rep ;
NAT size ;
DECL_SPEC info ;
},
promote -> {
/* Promotion types */
INT_TYPE arg ;
},
arith -> {
/* Arithmetic conversion types */
INT_TYPE arg1, arg2 ;
},
literal -> {
/* Target dependent literal type */
NAT nat ;
int spec ;
int form, suff ;
IDENTIFIER tok ;
},
token -> {
/* Tokenised integral type */
IDENTIFIER tok ;
LIST TOKEN args ;
}
} ;
/*
TYPE REPRESENTING A FLOATING POINT TYPE
A floating point type can be one of the built-in types or an arithmetic
conversion type. Note that all floating point types are their own
arithmetic promotions, but they do have distinct argument promotions.
*/
union FLOAT_TYPE (ftype) = {
TYPE arg_prom ;
ulong_type ftok = "LINK_NONE" ;
ulong_type ntok = "LINK_NONE" ;
ulong_type diag = "LINK_NONE" ;
LIST FLOAT small = "NULL_list ( FLOAT )" ;
} + {
basic -> {
/* Built-in types */
BASE_TYPE rep ;
BUILTIN_TYPE no ;
},
arg_promote -> {
/* Argument promotion types */
FLOAT_TYPE arg ;
},
arith -> {
/* Arithmetic conversion types */
FLOAT_TYPE arg1, arg2 ;
},
token -> {
/* Tokenised floating type */
IDENTIFIER tok ;
LIST TOKEN args ;
}
} ;
/*
TYPE REPRESENTING CLASS INFORMATION
This type is a bitpattern giving information about a class, for example,
if it is complete, whether it has base classes, non-static data members,
and so on.
*/
enum !CLASS_INFO (cinfo) = {
none = 0,
complete = 1 << 0, /* completed definition */
defined = 1 << 1, /* started definition */
struct_ = 1 << 2, /* structure class */
union_ = 1 << 3, /* union class */
template = 1 << 4, /* template class */
token = 1 << 5, /* tokenised structure */
pod = 1 << 6, /* plain ol' data structure */
nested = 1 << 7, /* is nested class */
rescan = 1 << 8, /* expands by rescanning */
recursive = 1 << 9, /* recursively defined */
incomplete = 1 << 10, /* warn if not completed */
base = 1 << 11, /* has base class */
multiple_base = 1 << 12, /* multiple inheritance */
virtual_base = 1 << 13, /* has virtual base class */
templ_base = 1 << 14, /* has template base class */
ambiguous = 1 << 15, /* has ambiguous base class */
empty = 1 << 16, /* empty class */
private = 1 << 17, /* non-public data member */
const = 1 << 18, /* const data member */
static = 1 << 19, /* static data member */
function = 1 << 20, /* function member */
params = 1 << 21, /* layout dependent on parameters */
polymorphic = 1 << 22, /* virtual function */
poly_base = 1 << 23, /* polymorphic base class */
abstract = 1 << 24, /* pure function */
trivial_constr = 1 << 25, /* trivial constructor */
trivial_destr = 1 << 26, /* trivial destructor */
trivial_copy = 1 << 27, /* trivial copy constructor */
trivial_assign = 1 << 28, /* trivial assign operator */
const_copy = 1 << 29, /* const copy constructor */
const_assign = 1 << 30, /* const assign operator */
usr_constr = 1 << 31, /* user defined constructor */
key = struct_ | union_,
non_aggregate = private | usr_constr | base | polymorphic | token,
force_copy = static | function | params,
trivial_make = trivial_constr | trivial_copy | trivial_assign,
trivial = trivial_make | trivial_destr,
implicit = trivial | const_copy | const_assign,
default = implicit | empty | pod
} ;
/*
TYPE REPRESENTING CLASS USAGE INFORMATION
This type is a bitpattern used to represent information on how a class
is used.
*/
enum !CLASS_USAGE (cusage) = {
none = 0,
address = 1 << 0, /* address of incomplete */
destr = 1 << 1, /* destroyed incomplete */
delete = 1 << 2, /* deleted incomplete */
delete_array = 1 << 3 /* deleted incomplete array */
} ;
/*
TYPE REPRESENTING A CLASS TYPE
A class type has an associated type name, type key (class, struct or
union), and class information. The hash table entries for the
constructors and destructors, plus a list of the conversion functions
on the class are maintained within the class definition. The class
members themselves are just a namespace. The base classes are
represented by a graph.
*/
union CLASS_TYPE (ctype) = {
IDENTIFIER name ;
CLASS_INFO info ;
CLASS_USAGE usage ;
NAMESPACE member ;
GRAPH base ;
unsigned no_bases ;
TYPE prev ;
TYPE form = "NULL_type" ;
IDENTIFIER constr = "NULL_id" ;
IDENTIFIER destr = "NULL_id" ;
VIRTUAL virt = "NULL_virt" ;
LIST GRAPH vbase = "NULL_list ( GRAPH )" ;
LIST IDENTIFIER conv = "NULL_list ( IDENTIFIER )" ;
LIST CLASS_TYPE chums = "NULL_list ( CLASS_TYPE )" ;
LIST IDENTIFIER pals = "NULL_list ( IDENTIFIER )" ;
LIST IDENTIFIER nest = "NULL_list ( IDENTIFIER )" ;
ulong_type tok1 = "LINK_NONE" ;
ulong_type tok2 = "LINK_NONE" ;
} + {
basic -> {
/* empty */
}
} ;
/*
TYPE REPRESENTING A GRAPH OF DERIVED CLASSES
This type is used to represent the directed acyclic graphs of base
classes associated with a class. Each node of this graph consists
of a class and a list of its base classes. The access of each base
class, and other information, is recorded. Identical virtual bases
are linked using the equal field. The next field is used to link
all instances of a class within a graph, whether virtual or not.
*/
union GRAPH (graph) = {
CLASS_TYPE head ;
DECL_SPEC access ;
LIST GRAPH tails = "NULL_list ( GRAPH )" ;
GRAPH top = "NULL_graph" ;
GRAPH equal = "NULL_graph" ;
GRAPH up = "NULL_graph" ;
unsigned no = "0" ;
OFFSET off = "NULL_off" ;
LIST IDENTIFIER member = "NULL_list ( IDENTIFIER )" ;
ulong_type tok1 = "LINK_NONE" ;
ulong_type tok2 = "LINK_NONE" ;
} + {
basic -> {
/* empty */
}
} ;
/*
TYPE REPRESENTING A VIRTUAL FUNCTION
This type is used to represent a virtual function table or an entry
of such a table. Each table entry has an associated function plus
information on where this was inherited from and any difference in
the return type of an overriding function.
*/
union VIRTUAL (virt) = {
IDENTIFIER func ;
ulong_type no ;
GRAPH base ;
VIRTUAL next = "NULL_virt" ;
} + {
table -> {
/* Virtual function table */
OFFSET off ;
LIST VIRTUAL entries = "NULL_list ( VIRTUAL )" ;
ulong_type tok = "LINK_NONE" ;
ulong_type tbl = "LINK_NONE" ;
ulong_type rtti = "LINK_NONE" ;
int rtti_used = "0" ;
},
simple -> {
/* New virtual function */
},
override -> {
/* Overriding virtual function */
GRAPH ret ;
IDENTIFIER orig ;
GRAPH src ;
},
inherit -> {
/* Inherited primary function */
},
complex -> {
/* Inherited overriding virtual function */
GRAPH ret ;
IDENTIFIER orig ;
GRAPH src ;
},
link -> {
/* Symbolic link */
PTR VIRTUAL to ;
}
} ;
/*
TYPE REPRESENTING AN ENUMERATION TYPE
An enumeration type has an associated type name, a class information
field (which is only used to indicate whether the type is complete),
and an underlying integral type. The enumerators themselves are given
as a simple list (note that they do not form a namespace).
*/
union ENUM_TYPE (etype) = {
IDENTIFIER name ;
CLASS_INFO info ;
TYPE rep ;
TYPE form = "NULL_type" ;
LIST IDENTIFIER values = "NULL_list ( IDENTIFIER )" ;
EXP value = "NULL_exp" ;
ulong_type plus = "0" ;
} + {
basic -> {
/* empty */
}
} ;
/*
TYPE REPRESENTING A TYPE
The types are represented by a union type, with one field per type
class. Any type may be qualified using const, volatile and lvalue.
*/
union TYPE (type) = {
/* Type qualifiers */
CV_SPEC qual ;
IDENTIFIER name = "NULL_id" ;
} + {
pre -> {
/* Pre-types (used during parsing only) */
BASE_TYPE rep ;
QUALIFIER nqual ;
},
integer -> {
/* Integer types */
INT_TYPE rep ;
INT_TYPE sem ;
},
floating -> {
/* Floating point types */
FLOAT_TYPE rep ;
FLOAT_TYPE sem ;
},
top, bottom -> {
/* The void types */
},
ptr, ref -> {
/* Pointer and reference types */
TYPE sub ;
},
ptr_mem -> {
/* Pointer to member types */
CLASS_TYPE of ;
TYPE sub ;
},
func -> {
/* Function types */
TYPE ret ;
LIST TYPE ptypes ;
int ellipsis ;
CV_SPEC mqual ;
LIST TYPE mtypes ;
NAMESPACE pars ;
LIST IDENTIFIER pids ;
LIST TYPE except ;
},
array -> {
/* Array types */
TYPE sub ;
NAT size ;
},
bitfield -> {
/* Bitfield types */
INT_TYPE defn ;
},
compound -> {
/* Class types */
CLASS_TYPE defn ;
},
enumerate -> {
/* Enumeration types */
ENUM_TYPE defn ;
},
token -> {
/* Tokenised type */
IDENTIFIER tok ;
LIST TOKEN args ;
INSTANCE app = "NULL_inst" ;
},
templ -> {
/* Template type */
TOKEN sort ;
TYPE defn ;
int fix ;
},
instance -> {
/* Template member type */
IDENTIFIER id ;
DECL_SPEC access ;
},
dummy -> {
/* Dummy special token type */
int tok ;
},
error -> {
/* Error propagation type */
}
} ;
/*
TYPE REPRESENTING A DECLARATION SPECIFIER
The basic declaration specifiers (i.e. the storage class specifiers,
the function specifiers, friend and typedef) are represented by an
enumeration type (which is actually a bit pattern). Other declaration
and linkage information is also included, including access specifiers.
The fact that public < protected < private is used extensively. The
values none, static and extern are also used to specify the linkage of
an object.
*/
enum DECL_SPEC (dspec) = {
none = 0,
used = 1 << 0,
called = 1 << 1,
defn = 1 << 2,
inherit = 1 << 3,
alias = 1 << 4,
done = 1 << 5,
static = 1 << 6,
extern = 1 << 7,
auto = 1 << 8,
register = 1 << 9,
mutable = 1 << 10,
inline = 1 << 11,
virtual = 1 << 12,
explicit = 1 << 13,
friend = 1 << 14,
typedef = 1 << 15,
public = 1 << 16,
protected = 2 << 16,
private = 3 << 16,
public2 = public << 2,
protected2 = protected << 2,
private2 = private << 2,
c = 1 << 20,
cpp = 1 << 21,
ignore = 1 << 22,
implicit = 1 << 23,
instance = 1 << 24,
main = 1 << 25,
pure = 1 << 26,
reserve = 1 << 27,
temp = 1 << 28,
template = 1 << 29,
token = 1 << 30,
trivial = 1 << 31,
linkage = static | extern,
storage = static | extern | auto | register | mutable,
function = inline | virtual | explicit,
keyword = storage | function | friend | typedef,
duplicate = keyword,
access = public | protected | private,
access2 = public2 | protected2 | private2,
language = c | cpp,
other = ignore | implicit | instance | main | pure |
reserve | temp | template | token | trivial
} ;
/*
TYPE REPRESENTING A HASH TABLE ENTRY
Identifier names are represented by a hash table entry. There are
several different types of names (simp1e strings, destructor names and
so on). Each hash table entry contains, in effect, a built-in namespace
member indicating the underlying meaning of this name. It also contains
the hash value for this entry.
*/
union HASHID (hashid) = {
IDENTIFIER id = "NULL_id" ;
IDENTIFIER cache = "NULL_id" ;
HASHID next ;
ulong_type hash ;
} + {
name, ename -> {
/* Simple and extended identifiers */
string text ;
},
constr, destr, conv -> {
/* Constructors, destructors and conversion functions */
TYPE type ;
IDENTIFIER tid ;
},
op -> {
/* Overloaded operator name */
int lex ;
},
anon -> {
/* Unnamed identifier */
ulong_type uniq ;
}
} ;
/*
TYPE REPRESENTING AN IDENTIFIER QUALIFIER
These values are used to identify how an identifier is qualified,
either unqualified, a nested qualifier (for example A::B::n), a full
nested qualifier (for example ::A::B::n), or a top nested qualifier
(for example ::n). A value to mark resolved overloaded functions
is also included.
*/
enum !QUALIFIER (qual) = {
none = 0,
nested = 1,
full = 2,
top = 3,
mark = 4,
explicit = nested | full | top
} ;
/*
TYPE REPRESENTING AN IDENTIFIER
Identifiers have an associated hash table entry, indicating the
identifier name, plus a location indicating where the identifier was
first declared. The various things an identifier can represent are
given by the different fields of the union.
*/
union IDENTIFIER (id) = {
HASHID name ;
DECL_SPEC storage ;
NAMESPACE parent ;
LOCATION loc ;
IDENTIFIER alias = "%0" ;
ulong_type no = "LINK_NONE" ;
ulong_type dump = "LINK_NONE" ;
} + {
dummy -> {
/* Undefined identifier */
},
keyword, iso_keyword, reserved -> {
/* Keywords */
},
builtin -> {
/* Built-in operators */
TYPE ret ;
LIST TYPE ptypes ;
},
obj_macro -> {
/* Object-like macros */
PPTOKEN_P defn ;
},
func_macro -> {
/* Function-like macros */
PPTOKEN_P defn ;
LIST HASHID params ;
unsigned no_params ;
},
predicate -> {
/* Predicates */
LIST PPTOKEN_P values = "NULL_list ( PPTOKEN_P )" ;
},
class_name, enum_name,
class_alias, enum_alias, type_alias -> {
/* Type names */
TYPE defn ;
BASE_TYPE rep = "btype_none" ;
},
nspace_name, nspace_alias -> {
/* Namespace names */
NAMESPACE defn ;
},
variable, parameter, stat_member -> {
/* Variables (including static members) */
TYPE type ;
EXP init = "NULL_exp" ;
EXP term = "NULL_exp" ;
},
weak_param -> {
/* Non-prototype function parameters */
},
function, mem_func, stat_mem_func -> {
/* Functions (including member functions) */
TYPE type ;
IDENTIFIER over ;
TYPE form = "NULL_type" ;
LIST CLASS_TYPE chums = "NULL_list ( CLASS_TYPE )" ;
EXP defn = "NULL_exp" ;
},
member -> {
/* Simple data member */
TYPE type ;
OFFSET off = "NULL_off" ;
GRAPH base = "NULL_graph" ;
},
enumerator -> {
/* Enumerators */
TYPE etype ;
EXP value ;
},
label -> {
/* Labels */
int op ;
EXP stmt = "NULL_exp" ;
EXP gotos = "NULL_exp" ;
LIST VARIABLE vars = "NULL_list ( VARIABLE )" ;
},
token -> {
/* Tokens */
TOKEN sort ;
IDENTIFIER alt ;
},
ambig -> {
/* Ambiguous identifier */
LIST IDENTIFIER ids ;
int over ;
},
undef -> {
/* Undefined identifier */
TYPE form = "NULL_type" ;
},
pending -> {
/* Pending identifier (used in spec input) */
unsigned itag ;
TYPE type ;
}
} ;
/*
TYPE REPRESENTING A NAMESPACE MEMBER
There are two kinds of namespace member, small and large, corresponding
to the two kinds of namespace. The important section consists of two
identifiers, giving the current meanings of a name in the namespace.
Everything else is just links to other members.
*/
union MEMBER (member) = {
IDENTIFIER id = "NULL_id" ;
IDENTIFIER alt = "NULL_id" ;
MEMBER next ;
} + {
small -> {
/* empty */
},
large -> {
MEMBER tnext ;
}
} ;
/*
TYPE REPRESENTING A NAMESPACE
There are two kinds of namespace, small and large. The small namespaces
are the block and parameter spaces, the large ones are the class and
namespace spaces. Every namespace has an associated namespace name
(which may be the null identifier). A small namespace consists of a
simple linked list of small members (in reverse order of declaration).
A large namespace consists of a hash table of large members, plus a list
of all such members in order of declaration. The distinct orders reflect
the distinct purposes the two kinds of namespace are put to.
*/
union NAMESPACE (nspace) = {
IDENTIFIER name ;
MEMBER last = "NULL_member" ;
MEMBER prev = "NULL_member" ;
NAMESPACE parent ;
LIST NAMESPACE use = "NULL_list ( NAMESPACE )" ;
LIST NAMESPACE join = "NULL_list ( NAMESPACE )" ;
STACK IDENTIFIER set = "NULL_stack ( IDENTIFIER )" ;
ulong_type dump = "LINK_NONE" ;
} + {
block, param, dummy, label, templ -> {
/* Member list */
},
named, unnamed, global, ctype -> {
/* Member hash table */
MEMBER first = "NULL_member" ;
LIST IDENTIFIER extra = "NULL_list ( IDENTIFIER )" ;
ulong_type size ;
PTR MEMBER table ;
}
} ;
/*
TYPE REPRESENTING AN INTEGER LITERAL
An integer literal (or constant integral expression) is represented by
a sign and a list of numbers in the range [0,0xffff] representing the
value base 0x10000. The case where there is only one digit is dealt
with separately. It is also possible to form an integer literal from
an expression.
*/
union NAT (nat) = {
/* empty */
} + {
small -> {
/* Single digit */
unsigned value ;
},
large -> {
/* Multiple digits */
LIST unsigned values ;
},
calc -> {
/* Calculated value */
EXP value ;
ulong_type tok = "LINK_NONE" ;
},
neg -> {
/* Negation */
NAT arg ;
},
token -> {
/* Tokenised expression */
IDENTIFIER tok ;
LIST TOKEN args ;
}
} ;
/*
TYPE REPRESENTING A FLOATING POINT LITERAL
A floating point literal is represented by a sign, two strings giving
the digits before and after the decimal point, plus an exponent.
*/
union FLOAT (flt) = {
ulong_type tok = "LINK_NONE" ;
} + {
simple -> {
/* Simple literal */
string int_part ;
string frac_part ;
NAT exponent ;
}
} ;
/*
TYPE REPRESENTING A STRING LITERAL
A string literal consists of a sequence of characters of a given length.
Characters, wide characters, strings and wide strings are represented
by distinct fields of the union.
*/
union STRING (str) = {
STRING next = "NULL_str" ;
} + {
simple -> {
/* Simple literals */
ulong_type len ;
string text ;
unsigned kind ;
ulong_type tok = "LINK_NONE" ;
}
} ;
/*
TYPE REPRESENTING A TEST
Test and comparison operators are represented by an enumeration type
which happen to be in the same order as the TDF encoding. Complementary
tests (for example '<' and '>=') always add up to the same value, negate.
*/
enum !NTEST (ntest) = {
eq = 0,
greater = 1,
greater_eq = 2,
less = 3,
less_eq = 4,
not_eq = 5,
not_greater = 6,
not_greater_eq = 7,
not_less = 8,
not_less_eq = 9,
not_not_eq = 10,
none = 11,
negate = eq + not_eq,
not = not_eq,
not_not = not_not_eq
} ;
/*
TYPE REPRESENTING A ROUNDING MODE
Floating point rounding modes are represented by an enumeration type
which happen to be in the same order as the TDF encoding.
*/
enum !RMODE (rmode) = {
as_state = 0,
to_nearest = 1,
to_larger = 2,
to_smaller = 3,
to_zero = 4
} ;
/*
TYPE REPRESENTING AN EXPRESSION OR STATEMENT
The expressions and statements are represented by a union type, with one
field per expression or statement class. Every expression has an
associated type.
*/
union EXP (exp) = {
/* Expression type */
TYPE type ;
} + {
identifier, member, ambiguous, undeclared -> {
/* Identifiers */
IDENTIFIER id ;
QUALIFIER qual ;
},
int_lit -> {
/* Integer literal */
NAT nat ;
unsigned etag ;
},
float_lit -> {
/* Floating point literal */
FLOAT flt ;
},
char_lit -> {
/* Character literal */
STRING str ;
int digit ;
},
string_lit -> {
/* String literal */
STRING str ;
},
value -> {
/* Undefined value */
},
null, zero -> {
/* Null value (null pointer, zero etc.) */
},
paren, copy -> {
/* Identity operation (used for parentheses) */
EXP arg ;
},
assign -> {
/* Assignment */
EXP ref, arg ;
},
init -> {
/* Initialisation */
IDENTIFIER id ;
EXP arg ;
},
preinc -> {
/* Pre-increment */
EXP ref, op ;
int becomes ;
},
postinc -> {
/* Post-increment */
EXP ref, value, op ;
},
indir -> {
/* Indirection */
EXP ptr ;
int index = "0" ;
},
contents -> {
/* Contents */
EXP ptr ;
},
address -> {
/* Address of an object */
EXP arg ;
},
address_mem -> {
/* Address of a member */
EXP arg ;
int paren ;
},
func -> {
/* Function call */
EXP fn ;
LIST EXP args ;
unsigned extra = "0" ;
},
func_id -> {
/* Named function call */
IDENTIFIER id ;
LIST EXP args ;
EXP virt ;
unsigned extra = "0" ;
},
call -> {
/* Member function call */
EXP ptr, arg ;
GRAPH base ;
},
negate, compl, not, abs -> {
/* Simple unary operations */
EXP arg ;
},
plus, minus, mult, div, rem,
and, or, xor, log_and, log_or,
lshift, rshift, max, min -> {
/* Simple binary operations */
EXP arg1, arg2 ;
},
test -> {
/* Compare against null value */
NTEST tst ;
EXP arg ;
},
compare -> {
/* Compare two values */
NTEST tst ;
EXP arg1, arg2 ;
},
cast -> {
/* Casts */
unsigned conv ;
EXP arg ;
},
base_cast -> {
/* Base class conversion */
unsigned conv ;
EXP arg ;
OFFSET off ;
},
dyn_cast -> {
/* Dynamic cast */
EXP arg ;
EXP except ;
},
add_ptr -> {
/* Add a value to a pointer */
EXP ptr ;
OFFSET off ;
int virt ;
},
offset_size -> {
/* Size of offset */
OFFSET off ;
TYPE step ;
int pad ;
},
constr -> {
/* Constructor call */
EXP call, obj, alt ;
int info ;
},
destr -> {
/* Destructor call */
EXP call, obj ;
EXP count = "NULL_exp" ;
},
alloc -> {
/* Allocator call */
EXP call, init ;
EXP garbage, size ;
},
dealloc -> {
/* Deallocator call */
EXP term, call ;
EXP arg, size ;
},
rtti -> {
/* Run-time type information */
EXP arg ;
EXP except ;
int op ;
},
rtti_type -> {
/* Run-time type information */
TYPE arg ;
int op ;
},
rtti_no -> {
/* Arithmetic type number */
TYPE arg ;
},
dynamic -> {
/* Dynamic initialiser */
EXP arg ;
},
aggregate -> {
/* Aggregate initialisers */
LIST EXP args ;
LIST OFFSET offs ;
},
initialiser -> {
/* Compound initialisers */
LIST EXP args ;
LIST OFFSET offs ;
int kind ;
unsigned virt, base ;
},
nof -> {
/* Array initialisers */
EXP start ;
NAT size ;
EXP pad ;
EXP end ;
},
comma -> {
/* Comma operator */
LIST EXP args ;
},
set, unused -> {
/* Variable analysis operators */
EXP arg ;
},
reach, unreach -> {
/* Flow analysis operators */
EXP parent = "NULL_exp" ;
EXP body ;
},
sequence -> {
/* Sequence of statements */
EXP parent = "NULL_exp" ;
LIST EXP first, last ;
NAMESPACE decl ;
int block ;
},
solve_stmt -> {
/* Label complex */
EXP parent = "NULL_exp" ;
EXP body ;
LIST IDENTIFIER labels = "NULL_list ( IDENTIFIER )" ;
LIST IDENTIFIER vars = "NULL_list ( IDENTIFIER )" ;
},
decl_stmt -> {
/* Declaration statement */
EXP parent = "NULL_exp" ;
IDENTIFIER id ;
EXP body ;
},
if_stmt -> {
/* If statement */
EXP parent = "NULL_exp" ;
EXP cond ;
EXP true_code, false_code ;
IDENTIFIER label ;
},
while_stmt -> {
/* While and for statements */
EXP parent = "NULL_exp" ;
EXP cond ;
EXP body = "NULL_exp" ;
IDENTIFIER break_lab ;
IDENTIFIER cont_lab ;
IDENTIFIER loop_lab ;
LIST IDENTIFIER cond_id = "NULL_list ( IDENTIFIER )" ;
},
do_stmt -> {
/* Do statements */
EXP parent = "NULL_exp" ;
EXP cond ;
EXP body = "NULL_exp" ;
IDENTIFIER break_lab ;
IDENTIFIER cont_lab ;
IDENTIFIER loop_lab ;
},
switch_stmt -> {
/* Switch statement */
EXP parent = "NULL_exp" ;
EXP control ;
EXP body ;
LIST NAT cases = "NULL_list ( NAT )" ;
LIST IDENTIFIER case_labs = "NULL_list ( IDENTIFIER )" ;
IDENTIFIER default_lab = "NULL_id" ;
int exhaust ;
IDENTIFIER break_lab ;
},
hash_if -> {
/* #if statement */
EXP parent = "NULL_exp" ;
EXP cond ;
EXP true_code, false_code ;
EXP last = "NULL_exp" ;
},
return_stmt -> {
/* Return statement */
EXP parent = "NULL_exp" ;
EXP value ;
},
goto_stmt -> {
/* Goto statement (including break and continue statements) */
EXP parent = "NULL_exp" ;
IDENTIFIER label ;
EXP join ;
EXP next ;
},
label_stmt -> {
/* Labelled statement (including case and default statements) */
EXP parent = "NULL_exp" ;
IDENTIFIER label ;
EXP body ;
IDENTIFIER next = "NULL_id" ;
},
try_block -> {
/* Try block */
EXP parent = "NULL_exp" ;
EXP body ;
int func ;
LIST EXP handlers = "NULL_list ( EXP )" ;
LIST TYPE htypes = "NULL_list ( TYPE )" ;
EXP ellipsis = "NULL_exp" ;
LIST TYPE ttypes = "NULL_list ( TYPE )" ;
LIST LOCATION tlocs = "NULL_list ( LOCATION )" ;
ulong_type no = "LINK_NONE" ;
},
handler -> {
/* Exception handler */
EXP parent = "NULL_exp" ;
IDENTIFIER except ;
EXP body ;
ulong_type diag = "LINK_NONE" ;
},
exception -> {
/* Throw expression */
EXP arg ;
EXP size, destr ;
int expl ;
},
thrown -> {
/* Caught expression */
int done ;
},
op -> {
/* Undetermined operation */
int lex ;
EXP arg1, arg2 ;
},
opn -> {
/* Undetermined operation */
int lex ;
LIST EXP args ;
},
assembler -> {
/* asm expression */
STRING op ;
LIST EXP args ;
},
uncompiled -> {
/* Uncompiled expression */
LOCATION start ;
PPTOKEN_P defn ;
},
location -> {
/* Location expression */
LOCATION end ;
EXP arg ;
},
fail -> {
/* Install-time failure */
string msg ;
},
token -> {
/* Tokenised expression */
EXP parent = "NULL_exp" ;
IDENTIFIER tok ;
LIST TOKEN args ;
},
dummy -> {
/* Dummy expression */
EXP value ;
ulong_type no ;
OFFSET off ;
int virt = "0" ;
int cont ;
}
} ;
/*
TYPE REPRESENTING AN OFFSET
This type is used to represent an offset between two pointers. The
type construct gives the offset between successive elements of an
array of that type. The other constructs are fairly obvious.
*/
union OFFSET (off) = {
/* empty */
} + {
zero -> {
/* Zero offset */
TYPE type ;
},
type -> {
/* Type offset */
TYPE type ;
},
array -> {
/* Array offset */
TYPE type ;
unsigned arg ;
},
extra -> {
/* Extra offset for array allocator */
TYPE type ;
int scale ;
},
base -> {
/* Base class offset */
GRAPH graph ;
},
deriv -> {
/* Derived class offset */
GRAPH graph ;
OFFSET direct ;
OFFSET indirect ;
},
member -> {
/* Member offset */
IDENTIFIER id ;
},
ptr_mem -> {
/* Pointer member offset */
EXP arg ;
},
negate -> {
/* Offset negation */
OFFSET arg ;
},
plus -> {
/* Offset addition */
OFFSET arg1, arg2 ;
},
mult -> {
/* Offset multiplication */
OFFSET arg1 ;
EXP arg2 ;
},
ptr_diff -> {
/* Pointer difference */
EXP ptr1, ptr2 ;
},
token -> {
/* Tokenised offset */
IDENTIFIER tok ;
LIST TOKEN args ;
}
} ;
/*
TYPE REPRESENTING A TOKEN
This type is used to represent a TDF token. The various fields
correspond to the kinds of token - expressions, statements, types etc.
Each simple component also contains a field for holding a value of
that kind, thus a list of token arguments can themselves be expressed
as a list of tokens.
*/
union TOKEN (tok) = {
/* empty */
} + {
exp -> {
/* Expression tokens */
TYPE type ;
int constant ;
EXP value ;
},
stmt -> {
/* Statement tokens */
EXP value ;
},
nat, snat -> {
/* Integer constant tokens */
NAT value ;
},
type -> {
/* Type tokens */
BASE_TYPE kind ;
TYPE value ;
TYPE alt = "NULL_type" ;
},
func -> {
/* Function tokens */
TYPE type ;
IDENTIFIER defn = "NULL_id" ;
TOKEN proc = "NULL_tok" ;
},
member -> {
/* Class member tokens */
TYPE of ;
TYPE type ;
OFFSET value ;
},
class -> {
/* Template class tokens */
TYPE type ;
IDENTIFIER value ;
TYPE alt = "NULL_type" ;
},
proc -> {
/* Procedure tokens */
TOKEN res ;
NAMESPACE pars ;
int key ;
INSTANCE apps = "NULL_inst" ;
LIST IDENTIFIER bids = "NULL_list ( IDENTIFIER )" ;
LIST IDENTIFIER pids = "NULL_list ( IDENTIFIER )" ;
},
templ -> {
/* Template tokens */
DECL_SPEC usage ;
NAMESPACE pars ;
INSTANCE apps = "NULL_inst" ;
LIST IDENTIFIER pids = "NULL_list ( IDENTIFIER )" ;
LIST TOKEN dargs = "NULL_list ( TOKEN )" ;
}
} ;
/*
TYPE REPRESENTING A TEMPLATE INSTANCE
A template instance consists of an identifier, a type giving the
template form and a specifier giving declaration information.
*/
union INSTANCE (inst) = {
TYPE form ;
INSTANCE alias = "%0" ;
INSTANCE next ;
} + {
templ -> {
/* Template instance */
IDENTIFIER id ;
TYPE spec = "NULL_type" ;
DECL_SPEC access ;
PPTOKEN_P mode = "NULL" ;
LIST IDENTIFIER mems = "NULL_list ( IDENTIFIER )" ;
INSTANCE prev ;
},
token -> {
/* Token type instance */
ulong_type no = "LINK_NONE" ;
}
} ;
/*
TYPE REPRESENTING A VARIABLE ANALYSIS STATE
This type is used in the variable analysis routines to record the
state of a local variable.
*/
struct VARIABLE (var) = {
IDENTIFIER id ;
DECL_SPEC info ;
} ;
/*
TYPE REPRESENTING AN ERROR
An error can be either a simple message, described by an error number,
or a compound formed from two other errors. Some trickery is used to
store the arguments for a simple error after its size field.
*/
union ERROR (err) = {
int severity ;
} + {
simple -> {
/* Simple errors */
int number ;
unsigned size = "0" ;
/* arguments ... */
},
compound -> {
/* Compound errors */
ERROR head, tail ;
}
} ;
/*
TYPE REPRESENTING A FILE POSITION
A file position consists of a line number plus a pointer to a type
giving the file name.
*/
struct LOCATION (loc) = {
ulong_type line ;
ulong_type column ;
PTR POSITION posn ;
} ;
struct POSITION (posn) = {
string file ;
string input ;
string base ;
string dir ;
ulong_type offset ;
PTR LOCATION from ;
ulong_type datestamp ;
ulong_type tok = "LINK_NONE" ;
} ;