6 C Abstract Syntax Tree (CAST) Representation
The C Abstract Syntax Tree (cast) intermediate language is a collection of data structures built to provide an
internal representation of C/C++ source code. This representation allows Flick to generate code as it goes along
and then make modifications to the code after some analysis has been done. The definitions of the cast data
structures are contained in mom/cast.x and the library code can be found in c/libcast and its header file is in
mom/c/libcast.h.
6.1 CAST Overview
The cast data structures are designed to represent the various aspects of C/C++ source code. At the root there is the
cast_scope which corresponds to a lexical scope, so it contains all declarations and definitions. These declarations and
definitions are then represented by cast_defs which contains any information needed to produce the correct source code.
Then, beneath the cast_defs there can be types, expressions, and other things representing their counterparts in C/C++ source
code. These cast trees can then be printed out using the cast_w_* functions in the cast library (described
in Section 6.3), although it is easiest to just call cast_w_scope on the root scope to get everything printed
out.
6.2 CAST Data Structures
The following descriptions of the data structures show the C type definitions followed by a simple representation of what output
they generate. This representation is pretty simple: just think of the stuff between the angle brackets as code to be executed, or
as variable names to be referenced.
6.2.1 Scoped Names
cast uses a cast_scoped_name to hold any information about a fully scoped name in C++. The structure is made up of an
array of cast_name_s structures which hold a name and an array of template arguments for that name. Each template
argument can either be another name, a type, or an expression.
typedef struct cast_name_s cast_scoped_name<>;
enum cast_template_arg_kind
{
CAST_TEMP_ARG_NAME = 1,
CAST_TEMP_ARG_TYPE = 2,
CAST_TEMP_ARG_EXPR = 3
};
union cast_template_arg_u
switch(cast_template_arg_kind kind)
{
case CAST_TEMP_ARG_NAME: cast_scoped_name name;
case CAST_TEMP_ARG_TYPE: cast_type type;
case CAST_TEMP_ARG_EXPR: cast_expr expr;
};
struct cast_name_s
{
string name<>;
cast_template_arg_array args; /* C++ Only */
};
|
The cast library functions for scoped names are built to pass the cast_scoped_name structure by value if it is not going
to be changed or it is newly created, otherwise they are passed by pointer. For example, the cast_new_scoped_name
constructs a new cast_scoped_name from a null-terminated varargs list of strings and then returns the new scoped name by
value. However, the cast_add_scope_name function takes a pointer to a scoped name since it will be changing the structure.
Here is a full list of the pertinent libcast functions:
-
void cast_add_scope_name(cast_scoped_name *scname, char *name, cast_template_arg_array
args)
-
Adds another name and set of template arguments to a scoped name.
-
void cast_del_scope_name(cast_scoped_name *name)
-
Deletes the last name in the given scoped name.
-
void cast_prepend_scope_name(cast_scoped_name *scname, char *name,
cast_template_arg_array args)
-
Adds the given scope name to the front of a scoped name.
-
int cast_cmp_scoped_names(const cast_scoped_name *scn1, const cast_scoped_name *scn2)
-
Compares two scoped names, just like a regular strcmp. Note: this does not yet compare the template
arguments so it is possible to get a false positive.
-
cast_scoped_name cast_new_scoped_name(const char *name, ...)
-
Constructs a new cast_scoped_name from a null terminated list of strings. For example, cast_new_
scoped_name("CORBA", "Boolean", NULL) would result in the name "CORBA::Boolean".
-
cast_scoped_name cast_set_scoped_name(cast_scoped_name *scname, char *name,
cast_template_arg_array args, ...)
-
This is a more flexible version of cast_new_scoped_name which allows one to construct a name with
template arguments. It works by setting the values of scname or constructing a new one if it is null. The
varargs are tuples of strings for the names, and the template arguments for that name, terminated by a null.
-
int cast_scoped_name_is_empty(cast_scoped_name *scname)
-
Returns TRUE if scname is empty.
-
cast_scoped_name cast_copy_scoped_name(cast_scoped_name *name)
-
Returns a copy of name.
The cast library also defines some handy global variables:
-
extern cast_scoped_name null_scope_name
-
This is a cast_scoped_name initialized with zero length and value. This variable is usually used to initialize
other cast_scoped_names created on the stack.
-
extern cast_scoped_name empty_scope_name
-
This is a cast_scoped_name initialized with an empty string as its only name.
6.2.2 Types
Any C/C++ types in cast are represented in the cast_type_u union and usually referenced by cast_type which is a pointer
to a cast_type_u.
Primitive Types
The cast_primitive_type structure is able to represent any primitive types like booleans, characters, integers, and floats, as
well as any modifiers for those types. The name field is a bit of a hack so that it can be treated internally as a primitive, but
when output, the string in name will be printed rather than the primitive name.
enum cast_primitive_kind
{
CAST_PRIM_CHAR = 1,
CAST_PRIM_INT = 2,
CAST_PRIM_FLOAT = 3,
CAST_PRIM_DOUBLE = 4,
CAST_PRIM_BOOL = 5 /* C++ Only */
};
typedef unsigned cast_primitive_modifier;
const CAST_MOD_SIGNED = 0x0001;
const CAST_MOD_UNSIGNED = 0x0002;
const CAST_MOD_SHORT = 0x0010;
const CAST_MOD_LONG = 0x0020;
const CAST_MOD_LONG_LONG = 0x0040;
struct cast_primitive_type
{
cast_primitive_kind kind;
cast_primitive_modifier mod;
cast_scoped_name name;
};
|
<if name is null>
<mod> <kind>
<else>
<name>
|
Enumerations
The cast_enum_type represents an enumeration with a list of names and their value(s), as well as the name of the
enumeration itself.
struct cast_enum_field
{
/* The name of the slot */
string name<>;
/* The value (should probably be a literal primitive */
cast_expr val;
};
struct cast_enum_type
{
/* Enum tag. */
cast_scoped_name name;
/* value and name of each slot */
cast_enum_field slots<>;
};
|
enum <name> {
<slots[0].name> = <slots[0].val>,
...,
<slots[n].name> = <slots[n].val>
}
|
Arrays
The cast_array_type can represent an array of a certain length and type.
struct cast_array_type
{
/* Constant expression that determines the array's length.
Can be omitted if outside of anything or at the end of a structure;
means array is variable-length. */
cast_expr length;
/* Type of each element. */
cast_type element_type;
};
|
<element_type> [<length>]
|
Pointers
The cast_pointer_type adds a level of pointer indirection and points to the real type.
struct cast_pointer_type
{
/* What the pointer points to. */
cast_type target;
};
|
References
The cast_reference_type adds a level of C++ variable reference.
struct cast_reference_type
{
/* What the reference refers to. */
cast_type target;
};
|
Functions
The cast_func_type represents a function and any related attributes, such as parameters, exceptions, initializers in
constructors, and various flags. The parameters are an array of cast_params which contain the type, name, and default value
of the parameters.
Also, there is a flag field to specify options for the parameter. Currently, there is only the CAST_PARAM_IMPLICIT flag
which means that the parameter is implicit and should not be part of the function prototype. An example usage of this flag is the
this variable in a member function of a C++ class, or a global variable, like errno, which a function might be
using.
typedef unsigned cast_param_spec;
const CAST_PARAM_IMPLICIT = 0x0001;
struct cast_param
{
cast_param_spec spec;
string name<>;
cast_type type;
cast_init default_value; /* C++ Only */
};
typedef cast_param cast_func_param;
typedef unsigned cast_func_spec;
const CAST_FUNC_VIRTUAL = 0x0001; /* Prepends 'virtual' to the prototype */
const CAST_FUNC_PURE = 0x0002; /* Appends '= 0' to make it pure */
const CAST_FUNC_INLINE = 0x0004; /* Prepends 'inline' */
const CAST_FUNC_EXPLICIT = 0x0008; /* Prepends 'explicit' */
const CAST_FUNC_CONST = 0x0010; /* Appends 'const' */
const CAST_FUNC_OPERATOR = 0x0020; /*
* Prepends 'operator' to the cast_def
* name unless it's empty, in case it
* is printed before the return type,
* so you can do type cast operators.
*/
struct cast_func_type
{
cast_func_param params<>;
cast_type return_type;
cast_func_spec spec; /* C++ Only */
cast_type_array exception_types; /* C++ Only */
cast_expr_array initializers; /* C++ Only */
};
|
<spec & CAST_FUNC_VIRTUAL|CAST_FUNC_INLINE|CAST_FUNC_EXPLICIT> <return_type>
(<if !implicit><params[0].type and params[0].name> = <params[0].default_value>,
...
<if !implicit><params[n].type and params[n].name> = <params[n].default_value>
)
<spec & CAST_FUNC_CONST>
throw(<exception_types[0], ..., <exception_types[n]>)
: <initializers[0]>, ..., <initializers[1]>
<spec & CAST_FUNC_PURE>
|
Aggregate Types
The cast_aggregate_type is used for compound types, things like unions, structures, and classes. Members are then added
to the cast_scope just like adding a global variable to the root scope. Of course, other declarations and definitions can be
added to the scope if it is C++, as well as parents in the parents.
typedef unsigned cast_parent_flags;
const CAST_PARENT_PUBLIC = 0x0001;
const CAST_PARENT_PROTECTED = 0x0002;
const CAST_PARENT_PRIVATE = 0x0004;
const CAST_PARENT_VIRTUAL = 0x0008;
struct cast_parent_spec
{
cast_parent_flags flags;
cast_scoped_name name;
};
enum cast_aggregate_kind
{
CAST_AGGREGATE_STRUCT = 1,
CAST_AGGREGATE_UNION = 2,
CAST_AGGREGATE_CLASS = 3
};
struct cast_aggregate_type
{
cast_aggregate_kind kind;
cast_scoped_name name;
cast_parent_spec parents<>; /* C++ Only */
cast_scope scope; /* Adding things besides vars is C++ Only */
};
|
<kind> <name> : <parents[0].flags> <parents[0].name>,
...,
<parents[n].flags> <parents[n].name>
{
<scope>
};
|
Qualifiers
The cast_qualified_type is used to attach any qualifiers, const and volatile, to a type.
typedef int cast_type_qualifier;
const CAST_TQ_CONST = 0x01;
const CAST_TQ_VOLATILE = 0x02;
struct cast_qualified_type
{
cast_type_qualifier qual;
cast_type actual;
};
|
Templates
The cast_template_type is used to declare a template type with a certain set of parameters.
enum cast_template_param_kind
{
CAST_TEMP_PARAM_TYPE = 1,
CAST_TEMP_PARAM_CLASS = 2,
CAST_TEMP_PARAM_TYPENAME = 3,
CAST_TEMP_PARAM_TEMPLATE = 4
};
union cast_template_param_u
switch(cast_template_param_kind kind)
{
case CAST_TEMP_PARAM_TYPE: cast_type type_param;
case CAST_TEMP_PARAM_CLASS: void;
case CAST_TEMP_PARAM_TYPENAME: void;
case CAST_TEMP_PARAM_TEMPLATE: cast_template_param_t params<>;
};
struct cast_template_param
{
string name<>;
cast_template_param_u u;
cast_template_arg default_value;
};
typedef unsigned cast_template_flags;
const CAST_TEMP_EXPORT = 0x0001;
struct cast_template_type
{
cast_template_flags flags;
cast_template_param params<>;
cast_type def;
};
|
<flags>
template < <params[0].u and params[0].name> = <params[0].default_value>,
...,
<params[n].u and params[n].name> = <params[n].default_value> >
<def>
|
Named Types
Referencing types through their name is done by using a set of different kinds which get output in slightly different
ways:
-
CAST_TYPE_NAME
-
A plain name, usually from a typedef.
-
CAST_TYPE_STRUCT_NAME
-
A name for a structure, it will print struct before the scoped name.
-
CAST_TYPE_UNION_NAME
-
A name for a union, it will print union before the scoped name.
-
CAST_TYPE_ENUM_NAME
-
A name for an enumeration, it will just print the scoped name.
-
CAST_TYPE_CLASS_NAME
-
A forward class declaration, it will print class before the scoped name, so this cannot be used like a regular
type.
Voids
Void types are handled simply by setting the kind of the cast_type to CAST_TYPE_VOID.
Nulls
Null types are used for the return type of C++ constructors and destructors. They are handled by setting the kind of the
cast_type to CAST_TYPE_NULL.
Union of All Types
All of the specific types are brought together under the umbrella of the casttypeu union which holds them all and a few simple
ones.
enum cast_type_kind
{
CAST_TYPE_NAME = 1,
...
CAST_TYPE_TEMPLATE = 17 /* C++ Only */
};
union cast_type_u
switch (cast_type_kind kind)
{
case CAST_TYPE_NAME: cast_scoped_name name;
case CAST_TYPE_PRIMITIVE: cast_primitive_type primitive_type;
case CAST_TYPE_POINTER: cast_pointer_type pointer_type;
case CAST_TYPE_ARRAY: cast_array_type array_type;
case CAST_TYPE_FUNCTION: cast_func_type func_type;
case CAST_TYPE_ENUM: cast_enum_type enum_type;
case CAST_TYPE_STRUCT_NAME: cast_scoped_name struct_name;
case CAST_TYPE_UNION_NAME: cast_scoped_name union_name;
case CAST_TYPE_ENUM_NAME: cast_scoped_name enum_name;
case CAST_TYPE_VOID: void;
case CAST_TYPE_NULL: void;
case CAST_TYPE_QUALIFIED: cast_qualified_type qualified;
case CAST_TYPE_AGGREGATE: cast_aggregate_type agg_type;
case CAST_TYPE_CLASS_NAME: cast_scoped_name class_name;
case CAST_TYPE_REFERENCE: cast_reference_type reference_type;
case CAST_TYPE_TYPENAME: cast_scoped_name typename_name;
case CAST_TYPE_TEMPLATE: cast_template_type template_type;
};
|
There are a number of functions in the cast library for constructing and initializing these structures. There are also some
functions and variables dealing with cast_type_arrays which can be used whenever an array of cast types is
needed.
-
cast_type_array cast_set_type_array(cast_type_array *array, cast_type type, ...)
-
Sets the values of array (or construct a new one if it is NULL) with the array of values passed in the
null-terminated argument list.
-
int cast_add_type_array_value(cast_type_array *array, cast_type type)
-
Adds another element to array, initializing it with type, and then return the index of the new element.
-
extern cast_type_array null_type_array
-
This is a global that can be used for initializing other cast_type_arrays to be empty.
6.2.3 Expressions
Literal Primitives
A cast_lit_prim represents a primitive literal value of a primitive type.
union cast_lit_prim_u
switch (cast_primitive_kind kind)
{
case CAST_PRIM_CHAR: char c;
case CAST_PRIM_INT: long i; /* XXX long long */
case CAST_PRIM_FLOAT: float f;
case CAST_PRIM_DOUBLE: double d; /* XXX long double */
case CAST_PRIM_BOOL: char b; /* C++ Only */
};
struct cast_lit_prim
{
cast_primitive_modifier mod;
cast_lit_prim_u u;
};
|
Function Calls
A cast_expr_call represents a function call with a set of arguments.
struct cast_expr_call
{
cast_expr func;
cast_expr_array params;
};
|
<func>(<params[0]>, ..., <params[n]>)
|
Member Selection
A cast_expr_sel represents the selection of a member of an aggregate type.
struct cast_expr_sel
{
cast_expr var;
cast_scoped_name member;
};
|
Unary Operations
A cast_unary_expr represents a unary operation on another expression.
enum cast_unary_op
{
CAST_UNARY_DEREF = 1, /* * (unary) */
CAST_UNARY_ADDR = 2, /* & (unary) */
CAST_UNARY_NEG = 3, /* - (unary) */
CAST_UNARY_LNOT = 4, /* ! (unary) */
CAST_UNARY_BNOT = 5, /* ~ (unary) */
CAST_UNARY_PRE_INC = 6, /* ++foo */
CAST_UNARY_PRE_DEC = 7, /* --foo */
CAST_UNARY_POST_INC = 8, /* foo++ */
CAST_UNARY_POST_DEC = 9 /* foo-- */
};
struct cast_unary_expr
{
cast_unary_op op;
cast_expr expr;
};
|
Binary Operations
A cast_binary_expr represents a binary operation over two expressions.
enum cast_binary_op
{
CAST_BINARY_MUL = 1, /* * (binary) */
CAST_BINARY_DIV = 2, /* / */
CAST_BINARY_MOD = 3, /* % */
CAST_BINARY_ADD = 4, /* + (binary) */
CAST_BINARY_SUB = 5, /* - (binary) */
CAST_BINARY_SHL = 6, /* << */
CAST_BINARY_SHR = 7, /* >> */
CAST_BINARY_LT = 8, /* < */
CAST_BINARY_GT = 9, /* > */
CAST_BINARY_LE = 10, /* <= */
CAST_BINARY_GE = 11, /* >= */
CAST_BINARY_EQ = 12, /* == */
CAST_BINARY_NE = 13, /* != */
CAST_BINARY_BAND = 14, /* & */
CAST_BINARY_BXOR = 15, /* ^ */
CAST_BINARY_BOR = 16, /* | */
CAST_BINARY_LAND = 17, /* && */
CAST_BINARY_LOR = 18, /* || */
CAST_BINARY_ASSIGN = 19, /* = */
CAST_BINARY_COMMA = 20 /* , */
};
struct cast_binary_expr
{
cast_binary_op op;
cast_expr expr[2];
};
|
Type Casting
A cast_expr_cast represents a type cast on another expression.
struct cast_expr_cast
{
/* Expression to cast. */
cast_expr expr;
/* Type to cast it to. */
cast_type type;
};
|
Conditional Expressions
A cast_cond_expr represents a conditional expression with a test expression and its positive and negative
results.
struct cast_cond_expr
{
cast_expr test;
cast_expr true_expr;
cast_expr false_expr;
};
|
<test> ? <true_expr> : <false_expr>
|
The new Operator
A cast_op_new_expr represents a usage of the new operator.
struct cast_op_new_expr
{
cast_expr placement;
cast_type type;
cast_init init;
};
|
new (<placement>) <type><init>
|
The delete Operator
A cast_op_delete_expr represents a usage of the delete operator.
struct cast_op_delete_expr
{
long array;
cast_expr expr;
};
|
delete <if array>[]</if> <expr>
|
Variable Names
Variable names and other name expressions are a cast_expr set to CAST_EXPR_NAME with a scoped name.
String Literals
A string literal is simply a cast_expr set to CAST_EXPR_LIT_STRING and a C string set in the cast_expr
structure.
The sizeof Operator
A sizeof is done by setting the kind of the cast_expr to CAST_EXPR_SIZEOF_EXPR or CAST_EXPR_SIZEOF_TYPE and then
setting the value of what is to be evaluated.
The typeid Operator
A typeid is done by setting the kind of the cast_expr to CAST_EXPR_TYPEID_EXPR or CAST_EXPR_TYPEID_TYPE and then
setting the value of what is to be evaluated.
Types as Expressions
Sometimes it is necessary to use a type as an expression, such as in a macro call. This can be done by using the kind
CAST_EXPR_TYPE.
Union of All Expressions
enum cast_expr_kind
{
CAST_EXPR_NAME = 1,
...
CAST_EXPR_TYPE = 22
};
union cast_expr_u
switch (cast_expr_kind kind)
{
case CAST_EXPR_NAME: cast_scoped_name name;
case CAST_EXPR_LIT_PRIM: cast_lit_prim lit_prim;
case CAST_EXPR_LIT_STRING: string lit_string<>;
case CAST_EXPR_CALL: cast_expr_call call;
case CAST_EXPR_SEL: cast_expr_sel sel;
case CAST_EXPR_UNARY: cast_unary_expr unary;
case CAST_EXPR_CAST: cast_expr_cast cast;
case CAST_EXPR_SIZEOF_EXPR: cast_expr sizeof_expr;
case CAST_EXPR_SIZEOF_TYPE: cast_type sizeof_type;
case CAST_EXPR_BINARY: cast_binary_expr binary;
case CAST_EXPR_OP_ASSIGN: cast_binary_expr op_assign;
case CAST_EXPR_COND: cast_cond_expr cond;
case CAST_EXPR_CONST_NAME: cast_scoped_name const_name;
case CAST_EXPR_CONST_CAST: cast_expr_cast c_cast;
case CAST_EXPR_DYNAMIC_CAST: cast_expr_cast d_cast;
case CAST_EXPR_REINTERPRET_CAST: cast_expr_cast r_cast;
case CAST_EXPR_STATIC_CAST: cast_expr_cast s_cast;
case CAST_EXPR_OP_NEW: cast_op_new_expr op_new;
case CAST_EXPR_OP_DELETE: cast_op_delete_expr op_delete;
case CAST_EXPR_TYPEID_EXPR: cast_expr typeid_expr;
case CAST_EXPR_TYPEID_TYPE: cast_type typeid_type;
case CAST_EXPR_TYPE: cast_type type_expr;
};
|
6.2.4 Statements
Statement Blocks
A cast_block represents a block of executable statements.
typedef unsigned cast_block_flags;
/* The statements in the block are in reverse order */
const CAST_BLOCK_REVERSE = 0x00000001;
/* The statements in the block should be treated as if they were
inlined with the rest of the statements in the parent block.
(e.g. there is no '{' when printed) */
const CAST_BLOCK_INLINE = 0x00000002;
struct cast_block
{
/* Variable declarations and such at beginning of block. */
cast_scope scope;
/* Statements following decls, but preceeding "regular" code.
(Mainly useful for initializing temporary vars, etc.) */
cast_stmt initials<>;
/* Statements following them ("regular" code). */
cast_stmt stmts<>;
cast_block_flags flags;
};
|
{
<scope>
<initials[0]>
...
<initials[n]>
<if !reverse>
<stmts[0]>
...
<stmts[n]>
<else>
<stmts[n]>
...
<stmts[0]>
}
|
If Statements
A cast_if represents an if statement and its true and false branches.
struct cast_if
{
cast_expr test;
cast_stmt true_stmt;
cast_stmt false_stmt; /* optional */
};
|
if( <test> )
<true_stmt>
else
<false_stmt>
|
While and Do/While Loops
A cast_while can be used to represent both while and do/while loops, depending on the cast_stmt_kind.
struct cast_while
{
cast_expr test;
cast_stmt stmt;
};
|
while( <test> )
<stmt>
do
<stmt>
while( <test> )
|
For Loops
A cast_for represents a for loop.
struct cast_for
{
cast_expr init; /* optional */
cast_expr test; /* optional */
cast_expr iter; /* optional */
cast_stmt stmt;
};
|
for( <init>; <test>; <iter> )
<stmt>
|
Switch Statements
A cast_switch represents a switch statement. The stmt slot is used as the body so it should be a cast_block with a number
of cast_cases for each branch.
struct cast_switch
{
cast_expr test;
cast_stmt stmt;
};
|
Statement Labels
A cast_label attaches a label to a statement. The users slot is used internally to track the number of times
this label has been used, if the count is zero then this label will not be printed, but the statement will still be
output.
struct cast_label
{
string label<>;
cast_stmt stmt;
int users;
};
|
<if users><label>:</if>
<stmt>
|
Case Statements
A cast_case represents a case branch in a switch statement.
struct cast_case
{
cast_expr label;
cast_stmt stmt;
};
|
Catch Blocks
A cast_catch represents a catch block in C++.
/* C++ Only */
struct cast_catch
{
cast_type type;
string name<>;
cast_stmt block;
};
|
catch( <type and name> )
<block>
|
Try Blocks
A cast_try represents a try block in C++.
/* C++ Only */
struct cast_try
{
cast_stmt block;
cast_catch handlers<>;
};
|
try
<block>
<handlers[0]>
...
<handlers[n]>
|
Break Statements
Setting the cast_stmt kind to CAST_STMT_BREAK will make a break statement. Since there is nothing else associated with a
break statement, it has no structure
Continue Statements
Setting the cast_stmt kind to CAST_STMT_CONTINUE will make a continue statement. Since there is nothing else associated
with a continue statement, it has no structure
Null Statements
Setting the cast_stmt kind to CAST_STMT_NULL will make a null statement which just prints out a `;'
Empty Statements
Setting the cast_stmt kind to CAST_STMT_EMPTY will make a completely empty statement, nothing will be printed. This is
useful for deleting statements in the middle of blocks without having to move all of the following statements back
one.
Union of All Statements
enum cast_stmt_kind
{
CAST_STMT_EXPR = 1,
...
CAST_STMT_DECL = 20 /* C++ Only */
};
union cast_stmt_u
switch (cast_stmt_kind kind)
{
case CAST_STMT_EXPR: cast_expr expr;
case CAST_STMT_BLOCK: cast_block block;
case CAST_STMT_IF: cast_if s_if;
case CAST_STMT_WHILE: cast_while s_while;
case CAST_STMT_DO_WHILE: cast_while s_do_while;
case CAST_STMT_FOR: cast_for s_for;
case CAST_STMT_SWITCH: cast_switch s_switch;
case CAST_STMT_BREAK: void;
case CAST_STMT_CONTINUE: void;
case CAST_STMT_GOTO: string goto_label<>;
case CAST_STMT_LABEL: cast_label s_label;
case CAST_STMT_CASE: cast_case s_case;
case CAST_STMT_DEFAULT: cast_stmt default_stmt;
/* optional: */
case CAST_STMT_RETURN: cast_expr return_expr;
case CAST_STMT_TEXT: string text<>;
case CAST_STMT_NULL: void;
case CAST_STMT_EMPTY: void;
case CAST_STMT_TRY: cast_try try_block;
case CAST_STMT_THROW: cast_expr throw_expr;
case CAST_STMT_DECL: cast_scope decl;
};
|
6.2.5 Definitions and Declarations
Function Definitions
A cast_func_def represents a function definition. The type slot should be a function type that is usable in a definition, and
the block slot is the function body.
struct cast_func_def
{
cast_func_type type;
cast_block block;
};
|
Variable Definitions
A cast_var_def represents a variable definition for a function.
enum cast_init_kind
{
CAST_INIT_EXPR = 1,
CAST_INIT_AGGREGATE = 2,
CAST_INIT_CONSTRUCT = 3 /* C++ Only */
};
union cast_init_u
switch (cast_init_kind kind)
{
case CAST_INIT_EXPR: cast_expr expr;
case CAST_INIT_AGGREGATE: cast_init_array subs;
case CAST_INIT_CONSTRUCT: cast_expr_array exprs;
};
struct cast_var_def
{
cast_type type;
/* Optional - if present, specifies the variable's initializer. */
cast_init init;
};
|
Include Directives
A cast_include represents an include directive for the C preprocessor.
struct cast_include
{
string filename<>;
bool system_only;
};
|
<if system_only>
#include <<filename>>
<else>
#include "<filename>"
|
Using Directives
A cast_using_kind indicates the type of scope that is to be `used'.
/* C++ Only */
enum cast_using_kind
{
CAST_USING_NAME = 1,
CAST_USING_TYPENAME = 2,
CAST_USING_NAMESPACE = 3
};
|
Direct Code
A cast_direct is used to inject any string into the current scope. For example, one can insert comments or preprocessor
directives using this structure.
struct cast_direct
{
string code_string<>;
};
|
Union of All Definitions
The cast_def structure brings everything together.
enum cast_storage_class
{
CAST_SC_NONE = 0,
CAST_SC_AUTO = 1,
CAST_SC_STATIC = 2,
CAST_SC_EXTERN = 3,
CAST_SC_REGISTER = 4,
CAST_SC_MUTABLE = 5 /* C++ Only */
};
enum cast_def_kind
{
CAST_TYPEDEF = 0x00000001,
...
CAST_FRIEND = 0x00001000 /* C++ Only */
};
union cast_def_u
switch (cast_def_kind kind)
{
case CAST_TYPEDEF: cast_type typedef_type;
case CAST_TYPE: cast_type type;
case CAST_FUNC_DECL: cast_func_type func_type;
case CAST_FUNC_DEF: cast_func_def func_def;
case CAST_VAR_DECL: cast_type var_type;
case CAST_VAR_DEF: cast_var_def var_def;
case CAST_DEFINE: cast_expr define_as;
case CAST_INCLUDE: cast_include include;
case CAST_DIRECT_CODE: cast_direct direct;
case CAST_NAMESPACE: cast_scope *new_namespace;
case CAST_USING: cast_using_kind using_scope;
case CAST_LINKAGE: cast_scope *linkage;
case CAST_FRIEND: cast_type friend_decl;
};
/* C++ Only */
enum cast_def_protection {
CAST_PROT_NONE = 0,
CAST_PROT_PUBLIC = 1,
CAST_PROT_PROTECTED = 2,
CAST_PROT_PRIVATE = 3
};
struct cast_def
{
/* C identifier of type (or whatever) to be declared/defined.
Length 0 if none. */
cast_scoped_name name;
/* Storage class for the declaration/definition. */
cast_storage_class sc;
/* Description of definition. */
cast_def_u u;
/* The data_channel this definition is a part of */
data_channel_index channel;
cast_def_protection protection; /* C++ Only */
};
|
6.3 The CAST Library
-
cast_new_*
-
Many constructor functions are available so one does not have to build the structures manually.
-
int cast_find_def(cast_scope **scope, const cast_scoped_name name, int kind)
-
Locates a definition with name in scope. The return value is either -1, if it was not found, or the index of
the definition in scope. If the name is scoped then it will try to descend down into child scopes, and then set
scope to the child scope where the definition was located. The kind parameter is a bitmask that causes the
function to only match on definition kinds that are set in the mask.
-
int cast_find_def_pos(cast_scope **scope, int scope_pos, const cast_scoped_name name, int
kind)
-
Similar to cast_find_def except it allows one to start searching at a specific position. This is useful in case
a definition match, but it was not the correct one. For example, a forward class declaration would match, but
if one were searching for the actual class definition, one would have to do another cast_find_def.
-
cast_type cast_find_typedef_type(cast_scope *scope, cast_type name_ctype)
-
Returns the real definition of a typedef.
-
cast_enum_type *cast_find_enum_type(cast_scope *scope, cast_type ctype)
-
Returns the cast_enum_type that was specified with the passed in enumeration named type.
-
cast_aggregate_type *cast_find_struct_type(cast_scope *scope, cast_type ctype)
-
Returns the cast_aggregate_type that was specified with the passed in struct named type.
-
cast_aggregate_type *cast_find_union_type(cast_scope *scope, cast_type ctype)
-
Returns the cast_aggregate_type that was specified with the passed in union named type.
-
cast_aggregate_type *cast_find_class_type(cast_scope *scope, cast_type ctype)
-
Returns the cast_aggregate_type that was specified with the passed in class named type.
-
void cast_block_absorb_stmt(cast_block *b, cast_stmt st)
-
Adds the statement to the cast_block. If the statement is also a cast_block then it will transfer any
statements and declarations to the parent block.
-
int cast_find_label(cast_block *block, const char *label)
-
Returns the index of the cast_label in the block that matches given label
-
extern cast_expr_array null_expr_array
-
A global expression array available for initializing to an empty expression array.
-
cast_expr_array cast_set_expr_array(cast_expr_array *array, cast_expr expr, ...)
-
Set, or create if array is null, the values in an expression array.
-
extern cast_init_array null_init_array
-
Similar to nullexprarray, except for cast_inits.
-
cast_init_array cast_set_init_array(cast_init_array *array, cast_init, ...)
-
Similar to the cast_expr version.
-
extern cast_template_arg_array null_template_arg_array
-
Similar to nullexprarray, except for template arguments. This is handy for passing to a cast_add_scope_
name since it requires a template argument array.
-
cast_template_arg_array cast_set_template_arg_array(cast_template_arg_array *array,
cast_template_arg template_arg, ...)
-
Similar to the cast_expr version.
-
cast_check(cast_scope *scope)
-
Check the data structures to ensure they have valid data. This is done before cast is written to a file or after
it has been read from a file, usually as part of pres_c_check.
-
int cast_expr_const(cast_expr expr)
-
Returns true if expr is a constant expression, very helpful when trying to do optimizations.
-
cast_expr aoi_const_to_cast_expr(aoi_const c)
-
Converts an aoi_const to a cast_expr.
-
int cast_cmp_type(cast_type a, cast_type b)
-
Returns zero if the two types are the same, less than zero if type a is "less than" b, or greater than zero if type
a is "greater than" b. The exact notion of lesser and greater are not exactly clear in all cases (e.g., if the types
are completely different, the scalar difference between the two kind enumerations is returned). For similar
(read "same") types, a more intelligent "difference" can be taken (e.g., two CAST_TYPE_TYPENAMEs return
the "string difference", similar to strcmp, of the two typenames).
-
int cast_cmp_expr(cast_expr a, cast_expr b)
-
Returns zero if the two expressions are the same, or nonzero based on a similar lesser/greater relationship as
cast_cmp_type, above.
-
int cast_cmp_init(cast_init a, cast_init b)
-
Returns zero if the two initializers are the same, or nonzero based on a similar lesser/greater relationship as
cast_cmp_type, above.
-
cast_w_*()
-
Uses the w_printf functions to output the various cast structures.
6.4 Summary and Comments
cast was originally restricted to exactly the syntax of C, with minor accommodations for comments and preprocessor
directives. Over time, cast was extended to describe C++, and was also enhanced with certain pseudo-syntactic features such
as implicit function arguments (CAST_PARAM_IMPLICIT), types used as expressions (CAST_EXPR_TYPE, so that
types can be passed as arguments to macros), and label reference counts (so that unused labels can be silently
dropped during output). These extra-syntactic features make it significantly easier to create pres_c descriptions of
stubs.
cast allows Flick to create and modify C/C++ code at a fine-grain level, before it is output. This is an important feature
that allows Flick to create code in ways that would be difficult to accomplish through other means such as template
instantiation. Unfortunately, cast's fine-grained nature also makes cast an inconvenient data structure for some tasks, such as
instantiating big blocks of mostly predetermined code. For code-building tasks that are not suited to cast, Flick uses a separate
intermediate representation called scml, described in Chapter 8.
A second problem inherent to cast it that -- by design -- it is tied to C and C++. cast originally supported only C and
was later extended for C++, but it would likely be infeasible to extend cast to languages that are very unlike C. If Flick were
extended to such languages, it would be useful to replace cast with a more abstract code description language, designed
around the logical organization of code (e.g., flow control) rather than around any specific language syntax. Flick
would then map this abstract language onto concrete programming languages. This mapping could even be
user-customizable.