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;
 };
 <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;
 };
 <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;
 };
 <qual> <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;
 };
 <mod> <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;
 };
 <var>.<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;
 };
 <op><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];
 };
 <expr[0]> <op> <expr[1]>

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;
 };
 (<type>)<expr>

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;
 };
 switch( <test> )
   <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;
 };
 case <label>:
   <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;
 };
 <type>
 <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
 };
 using <cast_using_kind>

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.