// Filename: cppBison.y
// Created by:  drose (16Jan99)
// 
////////////////////////////////////////////////////////////////////

%{

#include "cppBisonDefs.h"
#include "cppParser.h"
#include "cppExpression.h"
#include "cppSimpleType.h"
#include "cppExtensionType.h"
#include "cppStructType.h"
#include "cppEnumType.h"
#include "cppFunctionType.h"
#include "cppTBDType.h"
#include "cppMakeSeq.h"
#include "cppParameterList.h"
#include "cppInstance.h"
#include "cppClassTemplateParameter.h"
#include "cppTemplateParameterList.h"
#include "cppInstanceIdentifier.h"
#include "cppTypedef.h"
#include "cppTypeDeclaration.h"
#include "cppVisibility.h"
#include "cppIdentifier.h"
#include "cppScope.h"
#include "cppTemplateScope.h"
#include "cppNamespace.h"
#include "cppUsing.h"

////////////////////////////////////////////////////////////////////
// Defining the interface to the parser.
////////////////////////////////////////////////////////////////////

CPPScope *current_scope = NULL;
CPPScope *global_scope = NULL;
CPPPreprocessor *current_lexer = NULL;

static CPPStructType *current_struct = NULL; 
static CPPEnumType *current_enum = NULL;
static int current_storage_class = 0;
static CPPType *current_type = NULL;
static CPPExpression *current_expr = NULL;
static int publish_nest_level = 0;
static CPPVisibility publish_previous;
static YYLTYPE publish_loc;

static vector<CPPScope *> last_scopes;
static vector<int> last_storage_classes;
static vector<CPPStructType *> last_structs;

int yyparse();

#define YYERROR_VERBOSE

static void
yyerror(const string &msg) {
  current_lexer->error(msg);
}

static void
yyerror(const string &msg, YYLTYPE &loc) {
  current_lexer->error(msg, loc.first_line, loc.first_column);
}

static void
yywarning(const string &msg, YYLTYPE &loc) {
  current_lexer->warning(msg, loc.first_line, loc.first_column);
}

static int
yylex(YYSTYPE *lval, YYLTYPE *lloc) {
  CPPToken token = current_lexer->get_next_token();
  *lval = token._lval;
  *lloc = token._lloc;
  return token._token;
}

void
parse_cpp(CPPParser *cp) {
  CPPScope *old_scope = current_scope;
  CPPScope *old_global_scope = global_scope;
  CPPPreprocessor *old_lexer = current_lexer;

  current_scope = cp;
  global_scope = cp;
  current_lexer = cp;
  publish_nest_level = 0;
  yyparse();

  if (publish_nest_level != 0) {
    yyerror("Unclosed __begin_publish", publish_loc);
    publish_nest_level = 0;
  }

  current_scope = old_scope;
  global_scope = old_global_scope;
  current_lexer = old_lexer;
    
}

CPPExpression *
parse_const_expr(CPPPreprocessor *pp, CPPScope *new_current_scope,
                 CPPScope *new_global_scope) {
  CPPScope *old_scope = current_scope;
  CPPScope *old_global_scope = global_scope;
  CPPPreprocessor *old_lexer = current_lexer;
  CPPExpression *old_expr = current_expr;

  current_scope = new_current_scope;
  global_scope = new_global_scope;
  current_expr = (CPPExpression *)NULL;
  current_lexer = pp;
  yyparse();

  CPPExpression *result = current_expr;

  current_scope = old_scope;
  global_scope = old_global_scope;
  current_lexer = old_lexer;
  current_expr = old_expr;

  return result;
}

CPPType *
parse_type(CPPPreprocessor *pp, CPPScope *new_current_scope,
           CPPScope *new_global_scope) {
  CPPScope *old_scope = current_scope;
  CPPScope *old_global_scope = global_scope;
  CPPPreprocessor *old_lexer = current_lexer;
  CPPType *old_type = current_type;

  current_scope = new_current_scope;
  global_scope = new_global_scope;
  current_type = (CPPType *)NULL;
  current_lexer = pp;
  yyparse();

  CPPType *result = current_type;
  
  current_scope = old_scope;
  global_scope = old_global_scope;
  current_lexer = old_lexer;
  current_type = old_type;

  return result;
}

static void
push_scope(CPPScope *new_scope) {
  last_scopes.push_back(current_scope);
  if (new_scope != NULL) {
    current_scope = new_scope;
  }
}

static void
pop_scope() {
  assert(!last_scopes.empty());
  current_scope = last_scopes.back();
  last_scopes.pop_back();
}

static void
push_storage_class(int new_storage_class) {
  last_storage_classes.push_back(current_storage_class);
  current_storage_class = new_storage_class;
}

static void
pop_storage_class() {
  assert(!last_storage_classes.empty());
  current_storage_class = last_storage_classes.back();
  last_storage_classes.pop_back();
}

static void
push_struct(CPPStructType *new_struct) {
  last_structs.push_back(current_struct);
  current_struct = new_struct;
}

static void
pop_struct() {
  assert(!last_structs.empty());
  current_struct = last_structs.back();
  last_structs.pop_back();
}

%}

/* This is a bison-specific declaration to enable recursive calls to
   yyparse().  It changes the calling sequence to yylex(), passing
   pointers to the current yylval and yylloc. */
%pure_parser

%token <u.real> REAL
%token <u.integer> INTEGER
%token <u.integer> CHAR_TOK
%token <str> STRING SIMPLE_IDENTIFIER
%token <u.identifier> IDENTIFIER TYPENAME_IDENTIFIER SCOPING
%token <u.type> TYPEDEFNAME

%token ELLIPSIS
%token OROR
%token ANDAND
%token EQCOMPARE
%token NECOMPARE
%token LECOMPARE
%token GECOMPARE
%token LSHIFT
%token RSHIFT
%token POINTSAT_STAR
%token DOT_STAR
%token UNARY
%token UNARY_NOT
%token UNARY_NEGATE
%token UNARY_MINUS
%token UNARY_STAR
%token UNARY_REF
%token POINTSAT
%token SCOPE
%token PLUSPLUS 
%token MINUSMINUS 
%token TIMESEQUAL
%token DIVIDEEQUAL
%token MODEQUAL
%token PLUSEQUAL
%token MINUSEQUAL
%token OREQUAL
%token ANDEQUAL
%token XOREQUAL
%token LSHIFTEQUAL
%token RSHIFTEQUAL
%token TOKENPASTE

%token KW_BEGIN_PUBLISH
%token KW_BLOCKING
%token KW_BOOL
%token KW_CATCH
%token KW_CHAR
%token KW_WCHAR_T
%token KW_CLASS 
%token KW_CONST 
%token KW_DELETE
%token KW_DOUBLE
%token KW_DYNAMIC_CAST
%token KW_ELSE
%token KW_END_PUBLISH 
%token KW_ENUM 
%token KW_EXTERN
%token KW_EXPLICIT
%token KW_PUBLISHED
%token KW_FALSE
%token KW_FLOAT
%token KW_FRIEND
%token KW_FOR
%token KW_GOTO
%token KW_IF
%token KW_INLINE
%token KW_INT
%token KW_LONG
%token KW_LONGLONG
%token KW_MAKE_SEQ
%token KW_MUTABLE
%token KW_NAMESPACE
%token KW_NEW
%token KW_OPERATOR
%token KW_PRIVATE
%token KW_PROTECTED
%token KW_PUBLIC
%token KW_REGISTER
%token KW_RETURN
%token KW_SHORT
%token KW_SIGNED
%token KW_SIZEOF
%token KW_STATIC
%token KW_STATIC_CAST
%token KW_STRUCT 
%token KW_TEMPLATE
%token KW_THROW
%token KW_TRUE
%token KW_TRY
%token KW_TYPEDEF 
%token KW_TYPENAME
%token KW_UNION 
%token KW_UNSIGNED
%token KW_USING
%token KW_VIRTUAL
%token KW_VOID
%token KW_VOLATILE
%token KW_WHILE

/* These special tokens are used to set the starting state of the
   parser.  The lexer places the appropriate one of these on the head
   of the input stream. */
%token START_CPP
%token START_CONST_EXPR
%token START_TYPE

%type <u.integer> storage_class
%type <u.instance> function_prototype
%type <u.integer> function_post
%type <str> function_operator
%type <u.decl> template_formal_parameter
%type <u.type> template_formal_parameter_type
%type <u.inst_ident> instance_identifier
%type <u.param_list> formal_parameter_list
%type <u.param_list> formal_parameters
%type <u.expr> template_parameter_maybe_initialize
%type <u.expr> maybe_initialize
%type <u.expr> maybe_initialize_or_function_body
%type <u.instance> formal_parameter
%type <u.inst_ident> not_paren_formal_parameter_identifier
%type <u.inst_ident> formal_parameter_identifier
%type <u.inst_ident> empty_instance_identifier
%type <u.type> type
%type <u.decl> type_decl
%type <u.type> predefined_type
%type <u.type> full_type
%type <u.struct_type> anonymous_struct
%type <u.struct_type> named_struct
%type <u.enum_type> anonymous_enum
%type <u.enum_type> named_enum
%type <u.extension_enum> enum_keyword
%type <u.extension_enum> struct_keyword
%type <u.simple_type> simple_type
%type <u.simple_type> simple_int_type
%type <u.simple_type> simple_float_type
%type <u.simple_type> simple_void_type
%type <u.type> class_derivation_name
/*%type <u.type> typedefname*/
%type <u.identifier> name
%type <str> string

/* We need to treat KW_OPERATOR as a scopable keyword. */
%type <u.identifier> KW_OPERATOR

%type <u.expr> optional_const_expr
%type <u.expr> optional_const_expr_comma
%type <u.expr> const_expr_comma
%type <u.expr> no_angle_bracket_const_expr
%type <u.expr> const_expr
%type <u.expr> const_operand
%type <u.expr> formal_const_expr
%type <u.expr> formal_const_operand


/* Precedence rules. */

%left IDENTIFIER TYPENAME_IDENTIFIER TYPEDEFNAME KW_ENUM ELLIPSIS KW_OPERATOR KW_TYPENAME KW_INT KW_SHORT KW_UNSIGNED KW_SIGNED KW_LONG KW_FLOAT KW_DOUBLE KW_CHAR KW_WCHAR_T KW_BOOL

%left '{' ',' ';'

%nonassoc KW_THROW
%right ':'
%right '='
%right '?'
%left OROR
%left ANDAND
%left '|'
%left '^'
%left '&'
%left EQCOMPARE NECOMPARE
%left LECOMPARE GECOMPARE '<' '>'
%left LSHIFT RSHIFT
%left '+' '-'
%left '*' '/' '%'
%left POINTSAT_STAR DOT_STAR
%right UNARY PLUSPLUS MINUSMINUS '~'
%left POINTSAT '.' '(' '['

%right SCOPE
%nonassoc KW_NEW KW_DELETE KW_TRY KW_CATCH

%%

grammar:
        START_CPP cpp
        | START_CONST_EXPR const_expr
{
  current_expr = $2;
}
        | START_TYPE full_type
{
  current_type = $2;
}
        ;

cpp:
        empty
        | cpp ';'
        | cpp declaration
        ;

constructor_inits:
        constructor_init
        | constructor_inits ',' constructor_init
        ;

constructor_init:
        name '(' optional_const_expr_comma ')'
{
  delete $3;
}
        ;

/* This is principally for the syntax: extern "C" { ... }.

   We use storage_class instead of simply KW_EXTERN to avoid
   shift/reduce conflicts with yacc's limited differentiation
   ability. */
extern_c:
        storage_class '{'
{
  push_storage_class((current_storage_class & ~CPPInstance::SC_c_binding) | 
                     ($1 & CPPInstance::SC_c_binding));
}
        cpp '}'
{
  pop_storage_class();
}
        ;

declaration:
        type_like_declaration
        | template_declaration
        | extern_c
        | namespace_declaration
        | using_declaration
        | friend_declaration
        | KW_TYPEDEF typedef_declaration
        | KW_BEGIN_PUBLISH 
{
  if (publish_nest_level != 0) {
    yyerror("Unclosed __begin_publish", publish_loc);
    publish_nest_level = 0;
    current_scope->set_current_vis(V_public);
  }

  publish_previous = current_scope->get_current_vis();
  publish_loc = @1;
  publish_nest_level++;
  current_scope->set_current_vis(V_published);
}
        | KW_END_PUBLISH 
{
  if (publish_nest_level != 1) {
    yyerror("Unmatched __end_publish", @1);
  } else {
    current_scope->set_current_vis(publish_previous);
  }
  publish_nest_level = 0;
}
        | KW_PUBLISHED ':'
{
  current_scope->set_current_vis(V_published);
}
        | KW_PUBLIC ':'
{
  if (publish_nest_level > 0) {
    current_scope->set_current_vis(V_published);
  } else {
    current_scope->set_current_vis(V_public);
  }
}
        | KW_PROTECTED ':'
{
  current_scope->set_current_vis(V_protected);
}
        | KW_PRIVATE ':'
{
  current_scope->set_current_vis(V_private);
}
	| KW_MAKE_SEQ '(' name ',' name ',' name ')' ';'
{
  CPPMakeSeq *make_seq = new CPPMakeSeq($3->get_simple_name(), $5->get_simple_name(), $7->get_simple_name(), @1.file);
  current_scope->add_declaration(make_seq, global_scope, current_lexer, @1);
}
        ;

friend_declaration:
        KW_FRIEND
{
  CPPScope *new_scope = new CPPScope(current_scope, CPPNameComponent("temp"),
                                     V_public);
  push_scope(new_scope);
}
        declaration
{
  delete current_scope;
  pop_scope();
}
        ;


storage_class:
        empty
{
  $$ = 0;
}
        | storage_class KW_EXTERN
{
  $$ = $1 | (int)CPPInstance::SC_extern;
}
        | storage_class KW_EXTERN string
{
  $$ = $1 | (int)CPPInstance::SC_extern;
  if ($3 == "C") {
    $$ |= (int)CPPInstance::SC_c_binding;
  } else if ($3 == "C++") {
    $$ &= ~(int)CPPInstance::SC_c_binding;
  } else {
    yywarning("Ignoring unknown linkage type \"" + $3 + "\"", @3);
  }
}
        | storage_class KW_STATIC
{
  $$ = $1 | (int)CPPInstance::SC_static;
}
        | storage_class KW_INLINE
{
  $$ = $1 | (int)CPPInstance::SC_inline;
}
        | storage_class KW_VIRTUAL
{
  $$ = $1 | (int)CPPInstance::SC_virtual;
}
        | storage_class KW_EXPLICIT
{
  $$ = $1 | (int)CPPInstance::SC_explicit;
}
        | storage_class KW_VOLATILE
{
  $$ = $1 | (int)CPPInstance::SC_volatile;
}
        | storage_class KW_MUTABLE
{
  $$ = $1 | (int)CPPInstance::SC_mutable;
}
        | storage_class KW_REGISTER
{
  $$ = $1 | (int)CPPInstance::SC_register;
}
        | storage_class KW_BLOCKING
{
  $$ = $1 | (int)CPPInstance::SC_blocking;
}
        ;

type_like_declaration:
        multiple_var_declaration
{
  /* multiple_var_declaration adds itself to the scope. */
}
        | storage_class type_decl ';'
{
  // We don't really care about the storage class here.  In fact, it's
  // not actually legal to define a class or struct using a particular
  // storage class, but we require it just to help yacc out in its
  // parsing.

  current_scope->add_declaration($2, global_scope, current_lexer, @2);
}
        | storage_class function_prototype maybe_initialize_or_function_body
{
  if ($2 != (CPPInstance *)NULL) {
    $2->_storage_class |= (current_storage_class | $1);
    current_scope->add_declaration($2, global_scope, current_lexer, @2);
    $2->set_initializer($3);
  }
}
        ;

multiple_var_declaration:
        storage_class type_decl
{
  // We don't need to push/pop type, because we can't nest
  // multiple_var_declarations.
  if ($2->as_type_declaration()) {
    current_type = $2->as_type_declaration()->_type;
  } else {
    current_type = $2->as_type();
  }
  push_storage_class($1);
}
        multiple_instance_identifiers
{
  pop_storage_class();
}
        | storage_class KW_CONST type
{
  // We don't need to push/pop type, because we can't nest
  // multiple_var_declarations.
  current_type = $3;
  push_storage_class($1);
}
        multiple_const_instance_identifiers
{
  pop_storage_class();
}

        /* We don't need to include a rule for variables that point to
           functions, because we get those from the function_prototype
           definition. */
        ;

multiple_instance_identifiers:
        instance_identifier maybe_initialize_or_function_body
{
  CPPInstance *inst = new CPPInstance(current_type, $1,
                                      current_storage_class,
                                      @1.file);
  inst->set_initializer($2);
  current_scope->add_declaration(inst, global_scope, current_lexer, @1);
}
        | instance_identifier maybe_initialize ',' multiple_instance_identifiers
{
  CPPInstance *inst = new CPPInstance(current_type, $1,
                                      current_storage_class,
                                      @1.file);
  inst->set_initializer($2);
  current_scope->add_declaration(inst, global_scope, current_lexer, @1);
}
        ;

multiple_const_instance_identifiers:
        instance_identifier maybe_initialize_or_function_body
{
  $1->add_modifier(IIT_const);
  CPPInstance *inst = new CPPInstance(current_type, $1,
                                      current_storage_class,
                                      @1.file);
  inst->set_initializer($2);
  current_scope->add_declaration(inst, global_scope, current_lexer, @1);
}
        | instance_identifier maybe_initialize ',' multiple_const_instance_identifiers
{
  $1->add_modifier(IIT_const);
  CPPInstance *inst = new CPPInstance(current_type, $1,
                                      current_storage_class,
                                      @1.file);
  inst->set_initializer($2);
  current_scope->add_declaration(inst, global_scope, current_lexer, @1);
}
        ;


typedef_declaration:
        storage_class type_decl
{
  // We don't need to push/pop type, because we can't nest
  // multiple_var_declarations.
  if ($2->as_type_declaration()) {
    current_type = $2->as_type_declaration()->_type;
  } else {
    current_type = $2->as_type();
  }
  push_storage_class($1);
}
        typedef_instance_identifiers
{
  pop_storage_class();
}
        | storage_class KW_CONST type
{
  // We don't need to push/pop type, because we can't nest
  // multiple_var_declarations.
  current_type = $3;
  push_storage_class($1);
}
        typedef_const_instance_identifiers
{
  pop_storage_class();
}
        | storage_class function_prototype maybe_initialize_or_function_body
{
  if ($2 != (CPPDeclaration *)NULL) {
    CPPInstance *inst = $2->as_instance();
    if (inst != (CPPInstance *)NULL) {
      inst->_storage_class |= (current_storage_class | $1);
      current_scope->add_declaration(inst, global_scope, current_lexer, @2);
      current_scope->add_declaration(new CPPTypedef(inst, current_scope == global_scope), global_scope, current_lexer, @2);
    }
  }
}
        ;

typedef_instance_identifiers:
        instance_identifier maybe_initialize_or_function_body
{
  CPPInstance *inst = new CPPInstance(current_type, $1,
                                      current_storage_class,
                                      @1.file);
  inst->set_initializer($2);
  current_scope->add_declaration(new CPPTypedef(inst, current_scope == global_scope), global_scope, current_lexer, @1);
}
        | instance_identifier maybe_initialize ',' typedef_instance_identifiers
{
  CPPInstance *inst = new CPPInstance(current_type, $1,
                                      current_storage_class,
                                      @1.file);
  inst->set_initializer($2);
  current_scope->add_declaration(new CPPTypedef(inst, current_scope == global_scope), global_scope, current_lexer, @1);
}
        ;

typedef_const_instance_identifiers:
        instance_identifier maybe_initialize_or_function_body
{
  $1->add_modifier(IIT_const);
  CPPInstance *inst = new CPPInstance(current_type, $1,
                                      current_storage_class,
                                      @1.file);
  inst->set_initializer($2);
  current_scope->add_declaration(new CPPTypedef(inst, current_scope == global_scope), global_scope, current_lexer, @1);
}
        | instance_identifier maybe_initialize ',' typedef_const_instance_identifiers
{
  $1->add_modifier(IIT_const);
  CPPInstance *inst = new CPPInstance(current_type, $1,
                                      current_storage_class,
                                      @1.file);
  inst->set_initializer($2);
  current_scope->add_declaration(new CPPTypedef(inst, current_scope == global_scope), global_scope, current_lexer, @1);
}
        ;

function_prototype:

/* Functions with implicit return types, and constructors */
        IDENTIFIER '(' 
{
  push_scope($1->get_scope(current_scope, global_scope));
}
        formal_parameter_list ')' function_post
{
  pop_scope();
  CPPType *type;
  if ($1->get_simple_name() == current_scope->get_simple_name()) {
    // This is a constructor, and has no return.
    type = new CPPSimpleType(CPPSimpleType::T_void);
  } else {
    // This isn't a constructor, so it has an implicit return type of
    // int.
    type = new CPPSimpleType(CPPSimpleType::T_int);
  }

  CPPInstanceIdentifier *ii = new CPPInstanceIdentifier($1);
  ii->add_func_modifier($4, $6);

  $$ = new CPPInstance(type, ii, 0, @1.file);
}
        | TYPENAME_IDENTIFIER '('
{
  push_scope($1->get_scope(current_scope, global_scope));
}
        formal_parameter_list ')' function_post
{
  pop_scope();
  CPPType *type;
  if ($1->get_simple_name() == current_scope->get_simple_name()) {
    // This is a constructor, and has no return.
    type = new CPPSimpleType(CPPSimpleType::T_void);
  } else {
    // This isn't a constructor, so it has an implicit return type of
    // int.
    type = new CPPSimpleType(CPPSimpleType::T_int);
  }

  CPPInstanceIdentifier *ii = new CPPInstanceIdentifier($1);
  ii->add_func_modifier($4, $6);

  $$ = new CPPInstance(type, ii, 0, @1.file);
}

/* Destructors */
        | '~' name '('
{
  push_scope($2->get_scope(current_scope, global_scope));
}
        formal_parameter_list ')' function_post
{
  pop_scope();
  if ($2->is_scoped()) {
    yyerror("Invalid destructor name: ~" + $2->get_fully_scoped_name(), @2);
  } else {
    CPPIdentifier *ident = 
      new CPPIdentifier("~" + $2->get_simple_name(), @2.file);
    delete $2;

    CPPType *type;
    type = new CPPSimpleType(CPPSimpleType::T_void);
    
    CPPInstanceIdentifier *ii = new CPPInstanceIdentifier(ident);
    ii->add_func_modifier($5, $7);

    $$ = new CPPInstance(type, ii, 0, @2.file);
  }
}

/* This is a special case: a function pointer declaration that looks
   at first a lot like a constructor declaration.  This is provided to
   help yacc sort out the differences.  It isn't an ideal solution,
   because it doesn't catch a lot of subtle variants on this form--but
   this will get at least the 99% most common uses. */

        | TYPENAME_IDENTIFIER '(' '*' instance_identifier ')' '('
{
  push_scope($4->get_scope(current_scope, global_scope));
}
        formal_parameter_list ')' function_post
{
  pop_scope();
  CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer);
  assert(type != NULL);

  CPPInstanceIdentifier *ii = $4;
  ii->add_modifier(IIT_pointer);
  ii->add_func_modifier($8, $10);
  $$ = new CPPInstance(type, ii, 0, @1.file);
}
        | TYPENAME_IDENTIFIER '(' SCOPING '*' instance_identifier ')' '('
{
  push_scope($5->get_scope(current_scope, global_scope));
}
        formal_parameter_list ')' function_post
{
  pop_scope();
  CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer);
  assert(type != NULL);

  CPPInstanceIdentifier *ii = $5;
  ii->add_scoped_pointer_modifier($3);
  ii->add_func_modifier($9, $11);
  $$ = new CPPInstance(type, ii, 0, @1.file);
}

/* Typecast operators */
        | KW_OPERATOR type not_paren_formal_parameter_identifier '('
{
  if ($1 != NULL) {
    push_scope($1->get_scope(current_scope, global_scope));
  }
}
        formal_parameter_list ')' function_post
{
  if ($1 != NULL) {
    pop_scope();
  }

  // We use formal_parameter_identifier, because that can match a type
  // name with or without an identifier, but especially without, which
  // is what follows the keyword "operator" in a typecast function.
  // As an added bonus, the type of the formal_parameter will be the
  // typecast type, i.e. the return type of the typecast function.

  // We give typecast operators the name "operator typecast <name>",
  // where <name> is a simple name of the type to be typecast.  Use
  // the method's return type to determine the full type description.
  string name = "operator typecast " + $2->get_simple_name();
  CPPIdentifier *ident = $1;
  if (ident == NULL) {
    ident = new CPPIdentifier(name, @1.file);
  } else {
    ident->add_name(name);
  }
  $$ = CPPInstance::make_typecast_function
    (new CPPInstance($2, $3, 0, @3.file), ident, $6, $8);
}
        | KW_OPERATOR KW_CONST type not_paren_formal_parameter_identifier '('
{
  if ($1 != NULL) {
    push_scope($1->get_scope(current_scope, global_scope));
  }
}
        formal_parameter_list ')' function_post
{
  if ($1 != NULL) {
    pop_scope();
  }

  CPPIdentifier *ident = $1;
  if (ident == NULL) {
    ident = new CPPIdentifier("operator typecast", @1.file);
  } else {
    ident->add_name("operator typecast");
  }
  $4->add_modifier(IIT_const);
  $$ = CPPInstance::make_typecast_function
    (new CPPInstance($3, $4, 0, @4.file), ident, $7, $9);
}

/* Not actually a function prototype, but maybe a template
   instantiation.  Just included here (instead of somewhere else) to
   avoid shift/reduce conflicts. */
        | IDENTIFIER
{
  CPPDeclaration *decl = 
    $1->find_symbol(current_scope, global_scope, current_lexer);
  if (decl != (CPPDeclaration *)NULL) {
    $$ = decl->as_instance();
  } else {
    $$ = (CPPInstance *)NULL;
  }
}
        ;

function_post:
        empty
{
  $$ = 0;
}
        | KW_CONST
{
  $$ = (int)CPPFunctionType::F_const_method;
}
        | function_post KW_THROW '(' ')'
{
  $$ = $1;
}
        | function_post KW_THROW '(' name ')'
{
  $$ = $1;
}
        ;

function_operator:
        '!'
{
  $$ = "!";
}
        | '~'
{
  $$ = "~";
}
        | '*'
{
  $$ = "*";
}
        | '/'
{
  $$ = "/";
}
        | '%'
{
  $$ = "%";
}
        | '+'
{
  $$ = "+";
}
        | '-'
{
  $$ = "-";
}
        | '|'
{
  $$ = "|";
}
        | '&'
{
  $$ = "&";
}
        | '^'
{
  $$ = "^";
}
        | OROR
{
  $$ = "||";
}
        | ANDAND
{
  $$ = "&&";
}
        | EQCOMPARE
{
  $$ = "==";
}
        | NECOMPARE
{
  $$ = "!=";
}
        | LECOMPARE
{
  $$ = "<=";
}
        | GECOMPARE
{
  $$ = ">=";
}
        | '<'
{
  $$ = "<";
}
        | '>'
{
  $$ = ">";
}
        | LSHIFT
{
  $$ = "<<";
}
        | RSHIFT
{
  $$ = ">>";
}
        | '='
{
  $$ = "=";
}
        | ','
{
  $$ = ",";
}
        | PLUSPLUS
{
  $$ = "++";
}
        | MINUSMINUS
{
  $$ = "--";
}
        | TIMESEQUAL
{
  $$ = "*=";
}
        | DIVIDEEQUAL
{
  $$ = "/=";
}
        | MODEQUAL
{
  $$ = "%=";
}
        | PLUSEQUAL
{
  $$ = "+=";
}
        | MINUSEQUAL
{
  $$ = "-=";
}
        | OREQUAL
{
  $$ = "|=";
}
        | ANDEQUAL
{
  $$ = "&=";
}
        | XOREQUAL
{
  $$ = "^=";
}
        | LSHIFTEQUAL
{
  $$ = "<<=";
}
        | RSHIFTEQUAL
{
  $$ = ">>=";
}
        | POINTSAT
{
  $$ = "->";
}
        | '[' ']'
{
  $$ = "[]";
}
        | '(' ')'
{
  $$ = "()";
}
        | KW_NEW
{
  $$ = "new";
}
        | KW_DELETE
{
  $$ = "delete";
}
        ;

more_template_declaration:
        type_like_declaration
        | template_declaration
        ;

template_declaration:
        KW_TEMPLATE
{
  push_scope(new CPPTemplateScope(current_scope));
}
        '<' template_formal_parameters '>' more_template_declaration
{
  pop_scope();
}
        ;

template_formal_parameters:
        empty
        | template_nonempty_formal_parameters
        ;

template_nonempty_formal_parameters:
        template_formal_parameter
{
  CPPTemplateScope *ts = current_scope->as_template_scope();
  assert(ts != NULL);
  ts->add_template_parameter($1);
}
        | template_nonempty_formal_parameters ',' template_formal_parameter
{
  CPPTemplateScope *ts = current_scope->as_template_scope();
  assert(ts != NULL);
  ts->add_template_parameter($3);
}
        ;

template_formal_parameter:
        KW_CLASS name
{
  $$ = CPPType::new_type(new CPPClassTemplateParameter($2));
}
        | KW_CLASS name '=' full_type
{
  $$ = CPPType::new_type(new CPPClassTemplateParameter($2, $4));
}
        | template_formal_parameter_type formal_parameter_identifier template_parameter_maybe_initialize
{
  CPPInstance *inst = new CPPInstance($1, $2, 0, @2.file);
  inst->set_initializer($3);
  $$ = inst;
}
        | KW_CONST template_formal_parameter_type formal_parameter_identifier template_parameter_maybe_initialize
{
  $3->add_modifier(IIT_const);
  CPPInstance *inst = new CPPInstance($2, $3, 0, @3.file);
  inst->set_initializer($4);
  $$ = inst;
}
        ;

template_formal_parameter_type:
        simple_type
{
  $$ = CPPType::new_type($1);
}
        | IDENTIFIER
{
  yywarning("Not a type: " + $1->get_fully_scoped_name(), @1);
  $$ = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_unknown));
}
        | TYPENAME_IDENTIFIER
{
  $$ = $1->find_type(current_scope, global_scope, false, current_lexer);
  assert($$ != NULL);
}
        | KW_TYPENAME name
{
  $$ = CPPType::new_type(new CPPTBDType($2));
}
        ;


instance_identifier:
        name
{
  $$ = new CPPInstanceIdentifier($1);
}
        | KW_OPERATOR function_operator
{
  // For an operator function.  We implement this simply by building a
  // ficticious name for the function; in other respects it's just
  // like a regular function.
  CPPIdentifier *ident = $1;
  if (ident == NULL) {
    ident = new CPPIdentifier("operator "+$2, @2.file);
  } else {
    ident->_names.push_back("operator "+$2);
  }

  $$ = new CPPInstanceIdentifier(ident);
}
        | KW_CONST instance_identifier  %prec UNARY
{
  $$ = $2;
  $$->add_modifier(IIT_const);
}
        | '*' instance_identifier  %prec UNARY
{
  $$ = $2;
  $$->add_modifier(IIT_pointer);
}
        | '&' instance_identifier  %prec UNARY
{
  $$ = $2;
  $$->add_modifier(IIT_reference);
}
        | SCOPING '*' instance_identifier  %prec UNARY
{
  $$ = $3;
  $$->add_scoped_pointer_modifier($1);
}
        | instance_identifier '[' optional_const_expr ']'
{
  $$ = $1;
  $$->add_array_modifier($3);
}
        | instance_identifier ':' INTEGER
{
  // bitfield definition.  We ignore the bitfield for now.
  $$ = $1;
}
        | '(' instance_identifier ')'
{
  $$ = $2;
  $$->add_modifier(IIT_paren);
}
        | instance_identifier '('
{
  push_scope($1->get_scope(current_scope, global_scope));
}
        formal_parameter_list ')' function_post
{
  pop_scope();
  $$ = $1;
  if ($4->is_parameter_expr() && $6 == 0) {
    // Oops, this must have been an instance declaration with a
    // parameter list, not a function prototype.
    $$->add_initializer_modifier($4);

  } else {
    // This was (probably) a function prototype.
    $$->add_func_modifier($4, $6);
  }
}
        ;


formal_parameter_list:
        empty
{
  $$ = new CPPParameterList;
}
        | ELLIPSIS
{
  $$ = new CPPParameterList;
  $$->_includes_ellipsis = true;
}
        | formal_parameters
{
  $$ = $1;
}
        | formal_parameters ',' ELLIPSIS
{
  $$ = $1;
  $$->_includes_ellipsis = true;
}
        | formal_parameters ELLIPSIS
{
  $$ = $1;
  $$->_includes_ellipsis = true;
}
        ;

formal_parameters:
        formal_parameter
{
  $$ = new CPPParameterList;
  $$->_parameters.push_back($1);
}
        | formal_parameters ',' formal_parameter
{
  $$ = $1;
  $$->_parameters.push_back($3);
}
        ;

template_parameter_maybe_initialize:
        empty
{
  $$ = (CPPExpression *)NULL;
}
        | '=' no_angle_bracket_const_expr
{
  $$ = $2;
}
        ;

maybe_initialize:
        empty
{
  $$ = (CPPExpression *)NULL;
}
        | '=' const_expr
{
  $$ = $2;
}
        ;

maybe_initialize_or_function_body:
        ';'
{
  $$ = (CPPExpression *)NULL;
}
        | '{' code '}'
{
  $$ = (CPPExpression *)NULL;
}
        | ':' constructor_inits '{' code '}'
{
  $$ = (CPPExpression *)NULL;
}
        | '=' const_expr ';'
{
  $$ = $2;
}
        | '=' '{' structure_init '}'
{
  $$ = (CPPExpression *)NULL;
}
        ;

structure_init:
        empty
        | structure_init_body
        | structure_init_body ','
        ;

structure_init_body:
        const_expr
{
}
        | '{' structure_init '}'
        | structure_init_body ',' const_expr
        | structure_init_body ',' '{' structure_init '}'
        ;

formal_parameter:
        type formal_parameter_identifier maybe_initialize
{
  $$ = new CPPInstance($1, $2, 0, @2.file);
  $$->set_initializer($3);
}
        | IDENTIFIER formal_parameter_identifier maybe_initialize
{
  yywarning("Not a type: " + $1->get_fully_scoped_name(), @1);
  CPPType *type =
    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_unknown));
  $$ = new CPPInstance(type, $2, 0, @2.file);
  $$->set_initializer($3);
}
        | KW_CONST type formal_parameter_identifier maybe_initialize
{
  $3->add_modifier(IIT_const);
  $$ = new CPPInstance($2, $3, 0, @3.file);
  $$->set_initializer($4);
}
        | formal_const_expr
{
  CPPType *type =
    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_parameter));
  $$ = new CPPInstance(type, "expr");
  $$->set_initializer($1);
}
        ;

not_paren_formal_parameter_identifier:
        empty
{
  $$ = new CPPInstanceIdentifier((CPPIdentifier *)NULL);
}
        | IDENTIFIER
{
  $$ = new CPPInstanceIdentifier($1);
}
        | TYPENAME_IDENTIFIER
{
  $$ = new CPPInstanceIdentifier($1);
}
        | KW_CONST not_paren_formal_parameter_identifier  %prec UNARY
{
  $$ = $2;
  $$->add_modifier(IIT_const);
}
        | '*' not_paren_formal_parameter_identifier  %prec UNARY
{
  $$ = $2;
  $$->add_modifier(IIT_pointer);
}
        | '&' not_paren_formal_parameter_identifier  %prec UNARY
{
  $$ = $2;
  $$->add_modifier(IIT_reference);
}
        | SCOPING '*' not_paren_formal_parameter_identifier  %prec UNARY
{
  $$ = $3;
  $$->add_scoped_pointer_modifier($1);
}
        | not_paren_formal_parameter_identifier '[' optional_const_expr ']'
{
  $$ = $1;
  $$->add_array_modifier($3);
}
        ;

formal_parameter_identifier:
        empty
{
  $$ = new CPPInstanceIdentifier((CPPIdentifier *)NULL);
}
        | IDENTIFIER
{
  $$ = new CPPInstanceIdentifier($1);
}
        | TYPENAME_IDENTIFIER
{
  $$ = new CPPInstanceIdentifier($1);
}
        | KW_CONST formal_parameter_identifier  %prec UNARY
{
  $$ = $2;
  $$->add_modifier(IIT_const);
}
        | '*' formal_parameter_identifier  %prec UNARY
{
  $$ = $2;
  $$->add_modifier(IIT_pointer);
}
        | '&' formal_parameter_identifier  %prec UNARY
{
  $$ = $2;
  $$->add_modifier(IIT_reference);
}
        | SCOPING '*' formal_parameter_identifier  %prec UNARY
{
  $$ = $3;
  $$->add_scoped_pointer_modifier($1);
}
        | formal_parameter_identifier '[' optional_const_expr ']'
{
  $$ = $1;
  $$->add_array_modifier($3);
}
        | '(' formal_parameter_identifier ')' '(' formal_parameter_list ')' function_post
{
  $$ = $2;
  $$->add_modifier(IIT_paren);
  $$->add_func_modifier($5, $7);
}
        | '(' formal_parameter_identifier ')'
{
  $$ = $2;
  $$->add_modifier(IIT_paren);
}
        ;

empty_instance_identifier:
        empty
{
  $$ = new CPPInstanceIdentifier((CPPIdentifier *)NULL);
}
        | KW_CONST empty_instance_identifier  %prec UNARY
{
  $$ = $2;
  $$->add_modifier(IIT_const);
}
        | '*' empty_instance_identifier  %prec UNARY
{
  $$ = $2;
  $$->add_modifier(IIT_pointer);
}
        | '&' empty_instance_identifier  %prec UNARY
{
  $$ = $2;
  $$->add_modifier(IIT_reference);
}
        | SCOPING '*' empty_instance_identifier  %prec UNARY
{
  $$ = $3;
  $$->add_scoped_pointer_modifier($1);
}
        | empty_instance_identifier '[' optional_const_expr ']'
{
  $$ = $1;
  $$->add_array_modifier($3);
}
        | '(' empty_instance_identifier ')' '(' formal_parameter_list ')' function_post
{
  $$ = $2;
  $$->add_modifier(IIT_paren);
  $$->add_func_modifier($5, $7);
}
        ;

type:
        simple_type
{
  $$ = CPPType::new_type($1);
}
        | TYPENAME_IDENTIFIER
{
  $$ = $1->find_type(current_scope, global_scope, false, current_lexer);
  assert($$ != NULL);
}
        | KW_TYPENAME name
{
  $$ = CPPType::new_type(new CPPTBDType($2));
}
        | anonymous_struct
{
  $$ = CPPType::new_type($1);
}
        | named_struct
{
  $$ = CPPType::new_type($1);
}
        | anonymous_enum
{
  $$ = CPPType::new_type($1);
}
        | named_enum
{
  $$ = CPPType::new_type($1);
}
        | struct_keyword name
{
  CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer);
  if (type != NULL) {
    $$ = type;
  } else {
    CPPExtensionType *et = 
      CPPType::new_type(new CPPExtensionType($1, $2, current_scope, @1.file))
      ->as_extension_type();
    CPPScope *scope = $2->get_scope(current_scope, global_scope);
    if (scope != NULL) {
      scope->define_extension_type(et);
    }
    $$ = et;
  }
}
        | enum_keyword name
{
  CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer);
  if (type != NULL) {
    $$ = type;
  } else {
    CPPExtensionType *et = 
      CPPType::new_type(new CPPExtensionType($1, $2, current_scope, @1.file))
      ->as_extension_type();
    CPPScope *scope = $2->get_scope(current_scope, global_scope);
    if (scope != NULL) {
      scope->define_extension_type(et);
    }
    $$ = et;
  }
}
        ;

type_decl:
        simple_type
{
  $$ = CPPType::new_type($1);
}
        | TYPENAME_IDENTIFIER
{
  $$ = $1->find_type(current_scope, global_scope, false, current_lexer);
  assert($$ != NULL);
}
        | KW_TYPENAME name
{
  $$ = CPPType::new_type(new CPPTBDType($2));
}
        | anonymous_struct
{
  $$ = CPPType::new_type($1);
}
        | named_struct
{
  $$ = new CPPTypeDeclaration(CPPType::new_type($1));
}
        | anonymous_enum
{
  $$ = CPPType::new_type($1);
}
        | named_enum
{
  $$ = new CPPTypeDeclaration(CPPType::new_type($1));
}
        | struct_keyword name
{
  CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer);
  if (type != NULL) {
    $$ = type;
  } else {
    CPPExtensionType *et = 
      CPPType::new_type(new CPPExtensionType($1, $2, current_scope, @1.file))
      ->as_extension_type();
    CPPScope *scope = $2->get_scope(current_scope, global_scope);
    if (scope != NULL) {
      scope->define_extension_type(et);
    }
    $$ = et;
  }
}
        | enum_keyword name
{
  CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer);
  if (type != NULL) {
    $$ = type;
  } else {
    CPPExtensionType *et = 
      CPPType::new_type(new CPPExtensionType($1, $2, current_scope, @1.file))
      ->as_extension_type();
    CPPScope *scope = $2->get_scope(current_scope, global_scope);
    if (scope != NULL) {
      scope->define_extension_type(et);
    }
    $$ = et;
  }
}
        ;

predefined_type:
        simple_type
{
  $$ = CPPType::new_type($1);
}
        | TYPENAME_IDENTIFIER
{
  $$ = $1->find_type(current_scope, global_scope, false, current_lexer);
  assert($$ != NULL);
}
        | KW_TYPENAME name
{
  $$ = CPPType::new_type(new CPPTBDType($2));
}
        | struct_keyword name
{
  CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer);
  if (type != NULL) {
    $$ = type;
  } else {
    CPPExtensionType *et = 
      CPPType::new_type(new CPPExtensionType($1, $2, current_scope, @1.file))
      ->as_extension_type();
    CPPScope *scope = $2->get_scope(current_scope, global_scope);
    if (scope != NULL) {
      scope->define_extension_type(et);
    }
    $$ = et;
  }
}
        | enum_keyword name
{
  CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer);
  if (type != NULL) {
    $$ = type;
  } else {
    CPPExtensionType *et = 
      CPPType::new_type(new CPPExtensionType($1, $2, current_scope, @1.file))
      ->as_extension_type();
    CPPScope *scope = $2->get_scope(current_scope, global_scope);
    if (scope != NULL) {
      scope->define_extension_type(et);
    }
    $$ = et;
  }
}
        ;

full_type:
        type empty_instance_identifier
{
  CPPInstance *inst = new CPPInstance($1, $2, 0, @1.file);
  $$ = inst->_type;
  delete inst;
}
        | KW_CONST type empty_instance_identifier
{
  $3->add_modifier(IIT_const);
  CPPInstance *inst = new CPPInstance($2, $3, 0, @1.file);
  $$ = inst->_type;
  delete inst;
}
        ;

anonymous_struct:
        struct_keyword '{'
{
  CPPVisibility starting_vis = 
  ($1 == CPPExtensionType::T_class) ? V_private : V_public;
  
  CPPScope *new_scope = new CPPScope(current_scope, CPPNameComponent("anon"),
                                     starting_vis);
  CPPStructType *st = new CPPStructType($1, NULL, current_scope,
                                        new_scope, @1.file);
  new_scope->set_struct_type(st);

  push_scope(new_scope);
  push_struct(st);
}
        cpp '}'
{
  $$ = current_struct;
  current_struct->_incomplete = false;
  pop_struct();
  pop_scope();
}
        ;

named_struct:
        struct_keyword name
{
  CPPVisibility starting_vis = 
  ($1 == CPPExtensionType::T_class) ? V_private : V_public;

  CPPScope *scope = $2->get_scope(current_scope, global_scope, current_lexer);
  if (scope == NULL) {
    scope = current_scope;
  }
  CPPScope *new_scope = new CPPScope(scope, $2->_names.back(),
                                     starting_vis);

  CPPStructType *st = new CPPStructType($1, $2, current_scope,
                                        new_scope, @1.file);
  new_scope->set_struct_type(st);
  current_scope->define_extension_type(st);
  
  push_scope(new_scope);
  push_struct(st);
}
        maybe_class_derivation '{' cpp '}'
{
  $$ = current_struct;
  current_struct->_incomplete = false;
  pop_struct();
  pop_scope();
}
        ;

maybe_class_derivation:
        empty
        | class_derivation
        ;

class_derivation:
        ':' base_specification
        | class_derivation ',' base_specification
        ;

base_specification:
        KW_PUBLIC class_derivation_name
{
  current_struct->append_derivation($2, V_public, false);
}
        | KW_PROTECTED class_derivation_name
{
  current_struct->append_derivation($2, V_protected, false);
}
        | KW_PRIVATE class_derivation_name
{
  current_struct->append_derivation($2, V_private, false);
}
        | KW_VIRTUAL KW_PUBLIC class_derivation_name
{
  current_struct->append_derivation($3, V_public, true);
}
        | KW_VIRTUAL KW_PROTECTED class_derivation_name
{
  current_struct->append_derivation($3, V_protected, true);
}
        | KW_VIRTUAL KW_PRIVATE class_derivation_name
{
  current_struct->append_derivation($3, V_private, true);
}
        | KW_PUBLIC KW_VIRTUAL class_derivation_name
{
  current_struct->append_derivation($3, V_public, true);
}
        | KW_PROTECTED KW_VIRTUAL class_derivation_name
{
  current_struct->append_derivation($3, V_protected, true);
}
        | KW_PRIVATE KW_VIRTUAL class_derivation_name
{
  current_struct->append_derivation($3, V_private, true);
}
        ;

anonymous_enum:
        enum_keyword '{'
{
  current_enum = new CPPEnumType(NULL, current_scope, @1.file);
}
        enum_body '}'
{
  $$ = current_enum;
  current_enum = NULL;
}
        ;

named_enum:
        enum_keyword name '{'
{
  current_enum = new CPPEnumType($2, current_scope, @1.file);
}
        enum_body '}'
{
  $$ = current_enum;
  current_enum = NULL;
}
        ;

enum_body:
        empty
        | enum_body_no_trailing_comma
        | enum_body_no_trailing_comma ','
        ;

enum_body_no_trailing_comma:
        name
{
  assert(current_enum != NULL);
  current_enum->add_element($1->get_simple_name(), current_scope);
}
        | name '=' const_expr
{
  assert(current_enum != NULL);
  current_enum->add_element($1->get_simple_name(), current_scope, $3);
}
        | enum_body_no_trailing_comma ',' name
{
  assert(current_enum != NULL);
  current_enum->add_element($3->get_simple_name(), current_scope);
}
        | enum_body_no_trailing_comma ',' name '=' const_expr
{
  assert(current_enum != NULL);
  current_enum->add_element($3->get_simple_name(), current_scope, $5);
}
        ;

enum_keyword:
        KW_ENUM
{
  $$ = CPPExtensionType::T_enum;
}
        ;

struct_keyword:
        KW_CLASS
{
  $$ = CPPExtensionType::T_class;
}
        | KW_STRUCT
{
  $$ = CPPExtensionType::T_struct;
}
        | KW_UNION
{
  $$ = CPPExtensionType::T_union;
}
        ;

namespace_declaration:
        KW_NAMESPACE name '{'
{
  CPPScope *scope = $2->find_scope(current_scope, global_scope, current_lexer);
  if (scope == NULL) {
    // This must be a new namespace declaration.
    CPPScope *parent_scope = 
      $2->get_scope(current_scope, global_scope, current_lexer);
    if (parent_scope == NULL) {
      parent_scope = current_scope;
    }
    scope = new CPPScope(parent_scope, $2->_names.back(), V_public);
  }

  CPPNamespace *nspace = new CPPNamespace($2, scope, @1.file);
  current_scope->add_declaration(nspace, global_scope, current_lexer, @1);
  current_scope->define_namespace(nspace);
  push_scope(scope);
}
        cpp '}'
{
  pop_scope();
}
        | KW_NAMESPACE '{' cpp '}'
        ;

using_declaration:
        KW_USING name
{
  CPPUsing *using_decl = new CPPUsing($2, false, @1.file);
  current_scope->add_declaration(using_decl, global_scope, current_lexer, @1);
  current_scope->add_using(using_decl, global_scope, current_lexer);
}
        | KW_USING KW_NAMESPACE name
{
  CPPUsing *using_decl = new CPPUsing($3, true, @1.file);
  current_scope->add_declaration(using_decl, global_scope, current_lexer, @1);
  current_scope->add_using(using_decl, global_scope, current_lexer);
}
        ;

simple_type:
        simple_int_type
        | simple_float_type
        | simple_void_type
        ;

simple_int_type:
        KW_BOOL
{
  $$ = new CPPSimpleType(CPPSimpleType::T_bool);
}
        | KW_CHAR
{
  $$ = new CPPSimpleType(CPPSimpleType::T_char);
}
        | KW_WCHAR_T
{
  $$ = new CPPSimpleType(CPPSimpleType::T_wchar_t);
}
        | KW_SHORT
{
  $$ = new CPPSimpleType(CPPSimpleType::T_int,
                         CPPSimpleType::F_short);
}
        | KW_LONG
{
  $$ = new CPPSimpleType(CPPSimpleType::T_int,
                         CPPSimpleType::F_long);
}
        | KW_LONGLONG
{
  $$ = new CPPSimpleType(CPPSimpleType::T_int,
                         CPPSimpleType::F_longlong);
}
        | KW_UNSIGNED
{
  $$ = new CPPSimpleType(CPPSimpleType::T_int,
                         CPPSimpleType::F_unsigned);
}
        | KW_SIGNED
{
  $$ = new CPPSimpleType(CPPSimpleType::T_int,
                         CPPSimpleType::F_signed);
}
        | KW_INT
{
  $$ = new CPPSimpleType(CPPSimpleType::T_int);
}
        | KW_SHORT simple_int_type
{
  $$ = $2;
  $$->_flags |= CPPSimpleType::F_short;
}
        | KW_LONG simple_int_type
{
  $$ = $2;
  if ($$->_flags & CPPSimpleType::F_long) {
    $$->_flags |= CPPSimpleType::F_longlong;
  } else {
    $$->_flags |= CPPSimpleType::F_long;
  }
}
        | KW_UNSIGNED simple_int_type
{
  $$ = $2;
  $$->_flags |= CPPSimpleType::F_unsigned;
}
        | KW_SIGNED simple_int_type
{
  $$ = $2;
  $$->_flags |= CPPSimpleType::F_signed;
}
        ;

simple_float_type:
        KW_FLOAT
{
  $$ = new CPPSimpleType(CPPSimpleType::T_float);
}
        | KW_LONG KW_FLOAT
{
  $$ = new CPPSimpleType(CPPSimpleType::T_float,
                         CPPSimpleType::F_long);
}
        | KW_DOUBLE
{
  $$ = new CPPSimpleType(CPPSimpleType::T_double);
}
        | KW_LONG KW_LONG KW_FLOAT
{
  $$ = new CPPSimpleType(CPPSimpleType::T_float,
                         CPPSimpleType::F_longlong);
}
        | KW_LONG KW_DOUBLE
{
  $$ = new CPPSimpleType(CPPSimpleType::T_double,
                         CPPSimpleType::F_long);
}
        ;

simple_void_type:
        KW_VOID
{
  $$ = new CPPSimpleType(CPPSimpleType::T_void);
}
        ;

/* We don't care what the code is.  We just want to be sure we match
   up opening and closing braces properly.  For anything else, we'll
   accept just token salad. */
code:
{
  current_lexer->_resolve_identifiers = false;
}
        code_block
{
  current_lexer->_resolve_identifiers = true;
}
        ;

code_block:
        empty
        | code_block element
        ;

element:
        REAL
{
}
        | INTEGER
{
}
        | STRING
{
}
        | CHAR_TOK
{
}
        | IDENTIFIER
{
}
        | TYPENAME_IDENTIFIER
{
}
        | SCOPING
{
}
        | SIMPLE_IDENTIFIER
{
}
        | ELLIPSIS | OROR | ANDAND
        | EQCOMPARE | NECOMPARE | LECOMPARE | GECOMPARE
        | LSHIFT | RSHIFT | POINTSAT_STAR | DOT_STAR | POINTSAT
        | SCOPE | PLUSPLUS | MINUSMINUS 
        | TIMESEQUAL | DIVIDEEQUAL | MODEQUAL | PLUSEQUAL | MINUSEQUAL
        | OREQUAL | ANDEQUAL | XOREQUAL | LSHIFTEQUAL | RSHIFTEQUAL
        | KW_BOOL | KW_CATCH | KW_CHAR | KW_WCHAR_T | KW_CLASS | KW_CONST
        | KW_DELETE | KW_DOUBLE | KW_DYNAMIC_CAST | KW_ELSE | KW_ENUM
        | KW_EXTERN | KW_EXPLICIT | KW_FALSE
        | KW_FLOAT | KW_FRIEND | KW_FOR | KW_GOTO 
        | KW_IF | KW_INLINE | KW_INT
        | KW_LONG | KW_MUTABLE | KW_NEW | KW_PRIVATE | KW_PROTECTED
        | KW_PUBLIC | KW_PUBLISHED | KW_REGISTER | KW_RETURN
        | KW_SHORT | KW_SIGNED | KW_SIZEOF | KW_STATIC | KW_STATIC_CAST
        | KW_STRUCT | KW_THROW | KW_TRUE | KW_TRY | KW_TYPEDEF | KW_TYPENAME
        | KW_UNION | KW_UNSIGNED | KW_VIRTUAL | KW_VOID | KW_VOLATILE
        | KW_WHILE | TOKENPASTE
        | KW_OPERATOR
{
}
        | '+' | '-' | '*' | '/' | '&' | '|' | '^' | '!' | '~' | '=' | '%'
        | '<' | '>' | '(' | ')' | '.' | ',' | ';' | ':' | '[' | ']'
        | '?' | '{' code_block '}'
        ;

optional_const_expr:
        empty
{
  $$ = (CPPExpression *)NULL;
}
        | const_expr
{
  $$ = $1;
}
        ;

optional_const_expr_comma:
        empty
{
  $$ = (CPPExpression *)NULL;
}
        | const_expr_comma
{
  $$ = $1;
}
        ;

const_expr_comma:
        const_expr
{
  $$ = $1;
}
        | const_expr_comma ',' const_expr
{
  $$ = new CPPExpression(',', $1, $3);
}
        ;

no_angle_bracket_const_expr:
        const_operand
{
  $$ = $1;
}
        | '(' full_type ')' no_angle_bracket_const_expr %prec UNARY
{
  $$ = new CPPExpression(CPPExpression::typecast_op($2, $4));
}
        | KW_STATIC_CAST '<' full_type '>' '(' const_expr_comma ')'
{
  $$ = new CPPExpression(CPPExpression::typecast_op($3, $6));
}
        | KW_DYNAMIC_CAST '<' full_type '>' '(' const_expr_comma ')'
{
  $$ = new CPPExpression(CPPExpression::typecast_op($3, $6));
}
        | KW_SIZEOF '(' full_type ')' %prec UNARY
{
  $$ = new CPPExpression(CPPExpression::sizeof_func($3));
}
        | '!' no_angle_bracket_const_expr    %prec UNARY
{
  $$ = new CPPExpression(UNARY_NOT, $2);
}
        | '~' no_angle_bracket_const_expr    %prec UNARY
{
  $$ = new CPPExpression(UNARY_NEGATE, $2);
}
        | '-' no_angle_bracket_const_expr    %prec UNARY
{
  if ($2->_type == CPPExpression::T_integer) {
    $$ = $2;
    $$->_u._integer = -$$->_u._integer;
  } else if ($2->_type == CPPExpression::T_real) {
    $$ = $2;
    $$->_u._real = -$$->_u._real;
  } else {
    $$ = new CPPExpression(UNARY_MINUS, $2);
  }
}
        | '*' no_angle_bracket_const_expr    %prec UNARY
{
  $$ = new CPPExpression(UNARY_STAR, $2);
}
        | '&' no_angle_bracket_const_expr    %prec UNARY
{
  $$ = new CPPExpression(UNARY_REF, $2);
}
        | no_angle_bracket_const_expr '*' no_angle_bracket_const_expr
{
  $$ = new CPPExpression('*', $1, $3);
}
        | no_angle_bracket_const_expr '/' no_angle_bracket_const_expr
{
  $$ = new CPPExpression('/', $1, $3);
}
        | no_angle_bracket_const_expr '%' no_angle_bracket_const_expr
{
  $$ = new CPPExpression('%', $1, $3);
}
        | no_angle_bracket_const_expr '+' no_angle_bracket_const_expr
{
  $$ = new CPPExpression('+', $1, $3);
}
        | no_angle_bracket_const_expr '-' no_angle_bracket_const_expr
{
  $$ = new CPPExpression('-', $1, $3);
}
        | no_angle_bracket_const_expr '|' no_angle_bracket_const_expr
{
  $$ = new CPPExpression('|', $1, $3);
}
        | no_angle_bracket_const_expr '^' no_angle_bracket_const_expr
{
  $$ = new CPPExpression('^', $1, $3);
}
        | no_angle_bracket_const_expr '&' no_angle_bracket_const_expr
{
  $$ = new CPPExpression('&', $1, $3);
}
        | no_angle_bracket_const_expr OROR no_angle_bracket_const_expr
{
  $$ = new CPPExpression(OROR, $1, $3);
}
        | no_angle_bracket_const_expr ANDAND no_angle_bracket_const_expr
{
  $$ = new CPPExpression(ANDAND, $1, $3);
}
        | no_angle_bracket_const_expr EQCOMPARE no_angle_bracket_const_expr
{
  $$ = new CPPExpression(EQCOMPARE, $1, $3);
}
        | no_angle_bracket_const_expr NECOMPARE no_angle_bracket_const_expr
{
  $$ = new CPPExpression(NECOMPARE, $1, $3);
}
        | no_angle_bracket_const_expr LECOMPARE no_angle_bracket_const_expr
{
  $$ = new CPPExpression(LECOMPARE, $1, $3);
}
        | no_angle_bracket_const_expr GECOMPARE no_angle_bracket_const_expr
{
  $$ = new CPPExpression(GECOMPARE, $1, $3);
}
        | no_angle_bracket_const_expr LSHIFT no_angle_bracket_const_expr
{
  $$ = new CPPExpression(LSHIFT, $1, $3);
}
        | no_angle_bracket_const_expr RSHIFT no_angle_bracket_const_expr
{
  $$ = new CPPExpression(RSHIFT, $1, $3);
}
        | no_angle_bracket_const_expr '?' no_angle_bracket_const_expr ':' no_angle_bracket_const_expr
{
  $$ = new CPPExpression('?', $1, $3, $5);
}
        | no_angle_bracket_const_expr '[' const_expr ']'
{
  $$ = new CPPExpression('[', $1, $3);
}
        | no_angle_bracket_const_expr '(' const_expr_comma ')'
{
  $$ = new CPPExpression('f', $1, $3);
}
        | no_angle_bracket_const_expr '(' ')'
{
  $$ = new CPPExpression('f', $1);
}
        | no_angle_bracket_const_expr '.' no_angle_bracket_const_expr
{
  $$ = new CPPExpression('.', $1, $3);
}
        | no_angle_bracket_const_expr POINTSAT no_angle_bracket_const_expr
{
  $$ = new CPPExpression(POINTSAT, $1, $3);
}
        | '(' const_expr_comma ')'
{
  $$ = $2;
}
        ;


const_expr:
        const_operand
{
  $$ = $1;
}
        | '(' full_type ')' const_expr %prec UNARY
{
  $$ = new CPPExpression(CPPExpression::typecast_op($2, $4));
}
        | KW_STATIC_CAST '<' full_type '>' '(' const_expr_comma ')'
{
  $$ = new CPPExpression(CPPExpression::typecast_op($3, $6));
}
        | KW_DYNAMIC_CAST '<' full_type '>' '(' const_expr_comma ')'
{
  $$ = new CPPExpression(CPPExpression::typecast_op($3, $6));
}
        | TYPENAME_IDENTIFIER '(' optional_const_expr_comma ')'
{
  // A constructor call.
  CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer);
  assert(type != NULL);
  $$ = new CPPExpression(CPPExpression::construct_op(type, $3));
}
        | KW_INT '(' optional_const_expr_comma ')'
{
  CPPType *type = 
    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int));
  $$ = new CPPExpression(CPPExpression::construct_op(type, $3));
}
        | KW_CHAR '(' optional_const_expr_comma ')'
{
  CPPType *type = 
    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char));
  $$ = new CPPExpression(CPPExpression::construct_op(type, $3));
}
        | KW_WCHAR_T '(' optional_const_expr_comma ')'
{
  CPPType *type = 
    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_wchar_t));
  $$ = new CPPExpression(CPPExpression::construct_op(type, $3));
}
        | KW_BOOL '(' optional_const_expr_comma ')'
{
  CPPType *type = 
    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_bool));
  $$ = new CPPExpression(CPPExpression::construct_op(type, $3));
}
        | KW_SHORT '(' optional_const_expr_comma ')'
{
  CPPType *type = 
    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int,
                                        CPPSimpleType::F_short));
  $$ = new CPPExpression(CPPExpression::construct_op(type, $3));
}
        | KW_LONG '(' optional_const_expr_comma ')'
{
  CPPType *type = 
    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int,
                                        CPPSimpleType::F_long));
  $$ = new CPPExpression(CPPExpression::construct_op(type, $3));
}
        | KW_UNSIGNED '(' optional_const_expr_comma ')'
{
  CPPType *type = 
    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int,
                                        CPPSimpleType::F_unsigned));
  $$ = new CPPExpression(CPPExpression::construct_op(type, $3));
}
        | KW_SIGNED '(' optional_const_expr_comma ')'
{
  CPPType *type = 
    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int,
                                        CPPSimpleType::F_signed));
  $$ = new CPPExpression(CPPExpression::construct_op(type, $3));
}
        | KW_FLOAT '(' optional_const_expr_comma ')'
{
  CPPType *type = 
    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_float));
  $$ = new CPPExpression(CPPExpression::construct_op(type, $3));
}
        | KW_DOUBLE '(' optional_const_expr_comma ')'
{
  CPPType *type = 
    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_double));
  $$ = new CPPExpression(CPPExpression::construct_op(type, $3));
}
        | KW_SIZEOF '(' full_type ')' %prec UNARY
{
  $$ = new CPPExpression(CPPExpression::sizeof_func($3));
}
        | KW_NEW predefined_type %prec UNARY
{
  $$ = new CPPExpression(CPPExpression::new_op($2));
}
        | KW_NEW predefined_type '(' optional_const_expr_comma ')' %prec UNARY
{
  $$ = new CPPExpression(CPPExpression::new_op($2, $4));
}
        | '!' const_expr    %prec UNARY
{
  $$ = new CPPExpression(UNARY_NOT, $2);
}
        | '~' const_expr    %prec UNARY
{
  $$ = new CPPExpression(UNARY_NEGATE, $2);
}
        | '-' const_expr    %prec UNARY
{
  if ($2->_type == CPPExpression::T_integer) {
    $$ = $2;
    $$->_u._integer = -$$->_u._integer;
  } else if ($2->_type == CPPExpression::T_real) {
    $$ = $2;
    $$->_u._real = -$$->_u._real;
  } else {
    $$ = new CPPExpression(UNARY_MINUS, $2);
  }
}
        | '*' const_expr    %prec UNARY
{
  $$ = new CPPExpression(UNARY_STAR, $2);
}
        | '&' const_expr    %prec UNARY
{
  $$ = new CPPExpression(UNARY_REF, $2);
}
        | const_expr '*' const_expr
{
  $$ = new CPPExpression('*', $1, $3);
}
        | const_expr '/' const_expr
{
  $$ = new CPPExpression('/', $1, $3);
}
        | const_expr '%' const_expr
{
  $$ = new CPPExpression('%', $1, $3);
}
        | const_expr '+' const_expr
{
  $$ = new CPPExpression('+', $1, $3);
}
        | const_expr '-' const_expr
{
  $$ = new CPPExpression('-', $1, $3);
}
        | const_expr '|' const_expr
{
  $$ = new CPPExpression('|', $1, $3);
}
        | const_expr '^' const_expr
{
  $$ = new CPPExpression('^', $1, $3);
}
        | const_expr '&' const_expr
{
  $$ = new CPPExpression('&', $1, $3);
}
        | const_expr OROR const_expr
{
  $$ = new CPPExpression(OROR, $1, $3);
}
        | const_expr ANDAND const_expr
{
  $$ = new CPPExpression(ANDAND, $1, $3);
}
        | const_expr EQCOMPARE const_expr
{
  $$ = new CPPExpression(EQCOMPARE, $1, $3);
}
        | const_expr NECOMPARE const_expr
{
  $$ = new CPPExpression(NECOMPARE, $1, $3);
}
        | const_expr LECOMPARE const_expr
{
  $$ = new CPPExpression(LECOMPARE, $1, $3);
}
        | const_expr GECOMPARE const_expr
{
  $$ = new CPPExpression(GECOMPARE, $1, $3);
}
        | const_expr '<' const_expr
{
  $$ = new CPPExpression('<', $1, $3);
}
        | const_expr '>' const_expr
{
  $$ = new CPPExpression('>', $1, $3);
}
        | const_expr LSHIFT const_expr
{
  $$ = new CPPExpression(LSHIFT, $1, $3);
}
        | const_expr RSHIFT const_expr
{
  $$ = new CPPExpression(RSHIFT, $1, $3);
}
        | const_expr '?' const_expr ':' const_expr
{
  $$ = new CPPExpression('?', $1, $3, $5);
}
        | const_expr '[' const_expr ']'
{
  $$ = new CPPExpression('[', $1, $3);
}
        | const_expr '(' const_expr_comma ')'
{
  $$ = new CPPExpression('f', $1, $3);
}
        | const_expr '(' ')'
{
  $$ = new CPPExpression('f', $1);
}
        | const_expr '.' const_expr
{
  $$ = new CPPExpression('.', $1, $3);
}
        | const_expr POINTSAT const_expr
{
  $$ = new CPPExpression(POINTSAT, $1, $3);
}
        | '(' const_expr_comma ')'
{
  $$ = $2;
}
        ;

const_operand:
        INTEGER
{
  $$ = new CPPExpression($1);
}
        | KW_TRUE
{
  $$ = new CPPExpression(true);
}
        | KW_FALSE
{
  $$ = new CPPExpression(false);
}
        | CHAR_TOK
{
  $$ = new CPPExpression($1);
}
        | REAL
{
  $$ = new CPPExpression($1);
}
        | string
{
  $$ = new CPPExpression($1);
}
        | IDENTIFIER
{
  $$ = new CPPExpression($1, current_scope, global_scope, current_lexer);
}
        ;

/* This is used for a const_expr as a "formal parameter", which really
   means an instance declaration using a parameter list (which looks a
   lot like a function prototype).  It differs from const_expr mainly
   in that it forbids some expressions unless they are parenthesized,
   to avoid shift/reduce conflicts with the actual formal parameter
   definition. */

formal_const_expr:
        formal_const_operand
{
  $$ = $1;
}
        | '(' full_type ')' const_expr %prec UNARY
{
  $$ = new CPPExpression(CPPExpression::typecast_op($2, $4));
}
        | KW_STATIC_CAST '<' full_type '>' '(' const_expr_comma ')'
{
  $$ = new CPPExpression(CPPExpression::typecast_op($3, $6));
}
        | KW_DYNAMIC_CAST '<' full_type '>' '(' const_expr_comma ')'
{
  $$ = new CPPExpression(CPPExpression::typecast_op($3, $6));
}
        | KW_SIZEOF '(' full_type ')' %prec UNARY
{
  $$ = new CPPExpression(CPPExpression::sizeof_func($3));
}
        | KW_NEW predefined_type %prec UNARY
{
  $$ = new CPPExpression(CPPExpression::new_op($2));
}
        | KW_NEW predefined_type '(' optional_const_expr_comma ')' %prec UNARY
{
  $$ = new CPPExpression(CPPExpression::new_op($2, $4));
}
        | '!' const_expr    %prec UNARY
{
  $$ = new CPPExpression(UNARY_NOT, $2);
}
        | '~' const_expr    %prec UNARY
{
  $$ = new CPPExpression(UNARY_NEGATE, $2);
}
        | '-' const_expr    %prec UNARY
{
  if ($2->_type == CPPExpression::T_integer) {
    $$ = $2;
    $$->_u._integer = -$$->_u._integer;
  } else if ($2->_type == CPPExpression::T_real) {
    $$ = $2;
    $$->_u._real = -$$->_u._real;
  } else {
    $$ = new CPPExpression(UNARY_MINUS, $2);
  }
}
        | '&' const_expr    %prec UNARY
{
  $$ = new CPPExpression(UNARY_REF, $2);
}
        | formal_const_expr '*' const_expr
{
  $$ = new CPPExpression('*', $1, $3);
}
        | formal_const_expr '/' const_expr
{
  $$ = new CPPExpression('/', $1, $3);
}
        | formal_const_expr '%' const_expr
{
  $$ = new CPPExpression('%', $1, $3);
}
        | formal_const_expr '+' const_expr
{
  $$ = new CPPExpression('+', $1, $3);
}
        | formal_const_expr '-' const_expr
{
  $$ = new CPPExpression('-', $1, $3);
}
        | formal_const_expr '|' const_expr
{
  $$ = new CPPExpression('|', $1, $3);
}
        | formal_const_expr '^' const_expr
{
  $$ = new CPPExpression('^', $1, $3);
}
        | formal_const_expr '&' const_expr
{
  $$ = new CPPExpression('&', $1, $3);
}
        | formal_const_expr OROR const_expr
{
  $$ = new CPPExpression(OROR, $1, $3);
}
        | formal_const_expr ANDAND const_expr
{
  $$ = new CPPExpression(ANDAND, $1, $3);
}
        | formal_const_expr EQCOMPARE const_expr
{
  $$ = new CPPExpression(EQCOMPARE, $1, $3);
}
        | formal_const_expr NECOMPARE const_expr
{
  $$ = new CPPExpression(NECOMPARE, $1, $3);
}
        | formal_const_expr LECOMPARE const_expr
{
  $$ = new CPPExpression(LECOMPARE, $1, $3);
}
        | formal_const_expr GECOMPARE const_expr
{
  $$ = new CPPExpression(GECOMPARE, $1, $3);
}
        | formal_const_expr '<' const_expr
{
  $$ = new CPPExpression('<', $1, $3);
}
        | formal_const_expr '>' const_expr
{
  $$ = new CPPExpression('>', $1, $3);
}
        | formal_const_expr LSHIFT const_expr
{
  $$ = new CPPExpression(LSHIFT, $1, $3);
}
        | formal_const_expr RSHIFT const_expr
{
  $$ = new CPPExpression(RSHIFT, $1, $3);
}
        | formal_const_expr '?' const_expr ':' const_expr
{
  $$ = new CPPExpression('?', $1, $3, $5);
}
        | formal_const_expr '[' const_expr ']'
{
  $$ = new CPPExpression('[', $1, $3);
}
        | formal_const_expr '(' const_expr_comma ')'
{
  $$ = new CPPExpression('f', $1, $3);
}
        | formal_const_expr '(' ')'
{
  $$ = new CPPExpression('f', $1);
}
        | formal_const_expr '.' const_expr
{
  $$ = new CPPExpression('.', $1, $3);
}
        | formal_const_expr POINTSAT const_expr
{
  $$ = new CPPExpression(POINTSAT, $1, $3);
}
        | '(' const_expr_comma ')'
{
  $$ = $2;
}
        ;

formal_const_operand:
        INTEGER
{
  $$ = new CPPExpression($1);
}
        | KW_TRUE
{
  $$ = new CPPExpression(true);
}
        | KW_FALSE
{
  $$ = new CPPExpression(false);
}
        | CHAR_TOK
{
  $$ = new CPPExpression($1);
}
        | REAL
{
  $$ = new CPPExpression($1);
}
        | string
{
  $$ = new CPPExpression($1);
}
        ;

class_derivation_name:
        name
{
  CPPType *type = $1->find_type(current_scope, global_scope, true);
  if (type == NULL) {
    type = CPPType::new_type(new CPPTBDType($1));
  }
  $$ = type;
}
        | struct_keyword name
{
  CPPType *type = $2->find_type(current_scope, global_scope, true, current_lexer);
  if (type == NULL) {
    type = CPPType::new_type(new CPPTBDType($2));
  }
  $$ = type;
}
        | KW_TYPENAME name
{
  $$ = CPPType::new_type(new CPPTBDType($2));
}
        ;

/*
typedefname:
        TYPENAME_IDENTIFIER
{
  CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer);
  assert(type != NULL);
  $$ = type;
}
        | KW_TYPENAME name
{
  $$ = CPPType::new_type(new CPPTBDType($2));
}
        ;
*/


name:
        IDENTIFIER
{
  $$ = $1;
}
        | TYPENAME_IDENTIFIER
{
  $$ = $1;
}
        ;

string:
        STRING
{
  $$ = $1;
}
        | string STRING
{
  $$ = $1 + $2;
}
        ;

empty:
        ;
