%{

/*
 * This parser is specified based on:
 *
 * c5.y, a ANSI-C grammar written by James A. Roskind.
 * "Portions Copyright (c) 1989, 1990 James A. Roskind".
 * (http://www.idiom.com/free-compilers/,
 * ftp://ftp.infoseek.com/ftp/pub/c++grammar/,
 * ftp://ftp.sra.co.jp/.a/pub/cmd/c++grammar2.0.tar.gz)
 */

#define PARSER ansi_c_parser

#include "ansi_c_parser.h"

int yyansi_clex();
extern char *yyansi_ctext;

#include "parser_static.inc"

#include "y.tab.h"

/*** token declaration **************************************************/
%}

/*** ANSI-C keywords ***/

%token	TOK_AUTO
%token  TOK_BOOL
%token	TOK_BREAK
%token	TOK_CASE
%token	TOK_CHAR
%token	TOK_CONST
%token	TOK_CONTINUE
%token	TOK_DEFAULT
%token	TOK_DO
%token	TOK_DOUBLE
%token	TOK_ELSE
%token	TOK_ENUM
%token	TOK_EXTERN
%token	TOK_FLOAT
%token	TOK_FOR
%token	TOK_GOTO
%token	TOK_IF
%token  TOK_INLINE
%token	TOK_INT
%token	TOK_LONG
%token	TOK_REGISTER
%token	TOK_RETURN
%token	TOK_SHORT
%token	TOK_SIGNED
%token	TOK_SIZEOF
%token	TOK_STATIC
%token	TOK_STRUCT
%token	TOK_SWITCH
%token	TOK_TYPEDEF
%token	TOK_UNION
%token	TOK_UNSIGNED
%token	TOK_VOID
%token	TOK_VOLATILE
%token	TOK_WHILE

/*** multi-character operators ***/

%token	TOK_ARROW
%token	TOK_INCR
%token	TOK_DECR
%token	TOK_SHIFTLEFT
%token	TOK_SHIFTRIGHT
%token	TOK_LE
%token	TOK_GE
%token	TOK_EQ
%token	TOK_NE
%token	TOK_ANDAND
%token	TOK_OROR
%token	TOK_ELLIPSIS

/*** modifying assignment operators ***/

%token	TOK_MULTASSIGN
%token	TOK_DIVASSIGN
%token	TOK_MODASSIGN
%token	TOK_PLUSASSIGN
%token	TOK_MINUSASSIGN
%token	TOK_SLASSIGN
%token	TOK_SRASSIGN
%token	TOK_ANDASSIGN
%token	TOK_EORASSIGN
%token	TOK_ORASSIGN

/*** scanner parsed tokens (these have a value!) ***/

%token	TOK_IDENTIFIER
%token	TOK_TYPEDEFNAME
%token	TOK_INTEGER
%token	TOK_FLOATING
%token	TOK_CHARACTER
%token	TOK_STRING

/*** extensions ***/

%token	TOK_INT8
%token	TOK_INT16
%token	TOK_INT32
%token	TOK_INT64
%token	TOK_PTR32
%token	TOK_PTR64
%token  TOK_TYPEOF
%token  TOK_ASM

/*** special scanner reports ***/

%token	TOK_SCANNER_ERROR	/* used by scanner to report errors */
%token	TOK_SCANNER_EOF		/* used by scanner to report end of import */

/*** grammar selection ***/

%token	TOK_PARSE_LANGUAGE
%token	TOK_PARSE_EXPRESSION
%token	TOK_PARSE_TYPE

/*** priority, associativity, etc. definitions **************************/


%start	grammar

%expect 1	/* the famous "dangling `else'" ambiguity */
		/* results in one shift/reduce conflict   */
		/* that we don't want to be reported      */

%{
/************************************************************************/
/*** rules **************************************************************/
/************************************************************************/
%}
%%

/*** Grammar selection **************************************************/

grammar: TOK_PARSE_LANGUAGE translation_unit
	| TOK_PARSE_EXPRESSION comma_expression
	{
	  PARSER.parse_tree.declarations.push_back(ansi_c_declarationt());
	  PARSER.parse_tree.declarations.back().swap(stack($2));
	}
	| TOK_PARSE_TYPE type_name
	;

/*** Token with values **************************************************/


identifier:
	TOK_IDENTIFIER
	;

typedef_name:
	TOK_TYPEDEFNAME
	;

integer:
	TOK_INTEGER
	;

floating:
	TOK_FLOATING
	;

character:
	TOK_CHARACTER
	;

string:
	TOK_STRING
	;

/*** Constants **********************************************************/

/* note: the following has been changed from the ANSI-C grammar:	*/
/*	- constant includes string_literal_list (cleaner)		*/

constant:
	integer
	| floating
	| character
	| string_literal_list
	;

string_literal_list:
	string
	| string_literal_list string
	{ $$ = $1;
	  // do concatenation
	  stack($$).set("value", stack($$).get_string("value")+
	    stack($2).get_string("value"));
	}
	;

/*** Expressions ********************************************************/

primary_expression:
	identifier
	| constant
	| '(' comma_expression ')'
	{ $$ = $2; }
	| statement_expression
	;

statement_expression: '(' compound_statement ')'
	{ init($$, "sideeffect");
	  stack($$).set("statement", "statement_expression");
          mto($$, $2);
	}
	;

postfix_expression:
	primary_expression
	| postfix_expression '[' comma_expression ']'
	{ binary($$, $1, "index", $3); }
	| postfix_expression '(' ')'
	{ $$=$2;
	  set($$, "sideeffect"); mto($$, $1);
	  stack($$).set("statement", "functioncall");
	}
	| postfix_expression '(' argument_expression_list ')'
	{ $$=$2;
	  init($$, "sideeffect");
	  mto($$, $1);
	  stack($$).set("statement", "functioncall");
	  // move the operands
	  stack($$).reserve_operands(stack($3).operands().size()+1);

	  Forall_operands(it, stack($3))
	    stack($$).move_to_operands(*it);

	  stack($3).clear();
	}
	| postfix_expression '.' member_name
	{ $$=$2;
	  set($$, "member");
	  mto($$, $1);
	  stack($$).set("component_name", stack($3).get("#base_name"));
	}
	| postfix_expression TOK_ARROW member_name
	{ $$=$2;
	  set($$, "ptrmember");
	  mto($$, $1);
	  stack($$).set("component_name", stack($3).get("#base_name"));
	}
	| postfix_expression TOK_INCR
	{ $$=$2;
	  init($$, "sideeffect");
	  mto($$, $1);
	  stack($$).set("statement", "postincrement");
	}
	| postfix_expression TOK_DECR
	{ $$=$2;
	  init($$, "sideeffect");
	  mto($$, $1);
	  stack($$).set("statement", "postdecrement");
	}
	;

member_name:
	identifier
	| typedef_name
	;

argument_expression_list:
	assignment_expression
	{
	  init($$, "expression_list");
	  mto($$, $1);
	}
	| argument_expression_list ',' assignment_expression
	{
	  $$=$1;
	  mto($$, $3);
	}
	;

unary_expression:
	postfix_expression
	| TOK_INCR unary_expression
	{ $$=$1;
	  set($$, "sideeffect");
	  stack($$).set("statement", "preincrement");
	  mto($$, $2);
	}
	| TOK_DECR unary_expression
	{ $$=$1;
	  set($$, "sideeffect");
	  stack($$).set("statement", "predecrement");
	  mto($$, $2);
	}
	| '&' cast_expression
	{ $$=$1;
	  set($$, "address_of");
	  mto($$, $2);
	}
	| '*' cast_expression
	{ $$=$1;
	  set($$, "dereference");
	  mto($$, $2);
	}
	| '+' cast_expression
	{ $$=$1;
	  set($$, "unary+");
	  mto($$, $2);
	}
	| '-' cast_expression
	{ $$=$1;
	  set($$, "unary-");
	  mto($$, $2);
	}
	| '~' cast_expression
	{ $$=$1;
	  set($$, "bitnot");
	  mto($$, $2);
	}
	| '!' cast_expression
	{ $$=$1;
	  set($$, "not");
	  mto($$, $2);
	}
	| TOK_SIZEOF unary_expression
	{ $$=$1;
	  set($$, "sizeof");
	  mto($$, $2);
	}
	| TOK_SIZEOF '(' type_name ')'
	{ $$=$1;
	  set($$, "sizeof");
	  stack($$).add("sizeof-type").swap(stack($3));
	}
	;

cast_expression:
	unary_expression
	| '(' type_name ')' cast_expression
	{
	  $$=$1;
	  set($$, "typecast");
	  mto($$, $4);
	  stack($$).type().swap(stack($2));
	}
	;

multiplicative_expression:
	cast_expression
	| multiplicative_expression '*' cast_expression
	{ binary($$, $1, "*", $3); }
	| multiplicative_expression '/' cast_expression
	{ binary($$, $1, "/", $3); }
	| multiplicative_expression '%' cast_expression
	{ binary($$, $1, "mod", $3); }
	;

additive_expression:
	multiplicative_expression
	| additive_expression '+' multiplicative_expression
	{ binary($$, $1, "+", $3); }
	| additive_expression '-' multiplicative_expression
	{ binary($$, $1, "-", $3); }
	;

shift_expression:
	additive_expression
	| shift_expression TOK_SHIFTLEFT additive_expression
	{ binary($$, $1, "shl", $3); }
	| shift_expression TOK_SHIFTRIGHT additive_expression
	{ binary($$, $1, "shr", $3); }
	;

relational_expression:
	shift_expression
	| relational_expression '<' shift_expression
	{ binary($$, $1, "<", $3); }
	| relational_expression '>' shift_expression
	{ binary($$, $1, ">", $3); }
	| relational_expression TOK_LE shift_expression
	{ binary($$, $1, "<=", $3); }
	| relational_expression TOK_GE shift_expression
	{ binary($$, $1, ">=", $3); }
	;

equality_expression:
	relational_expression
	| equality_expression TOK_EQ relational_expression
	{ binary($$, $1, "=", $3); }
	| equality_expression TOK_NE relational_expression
	{ binary($$, $1, "notequal", $3); }
	;

and_expression:
	equality_expression
	| and_expression '&' equality_expression
	{ binary($$, $1, "bitand", $3); }
	;

exclusive_or_expression:
	and_expression
	| exclusive_or_expression '^' and_expression
	{ binary($$, $1, "bitxor", $3); }
	;

inclusive_or_expression:
	exclusive_or_expression
	| inclusive_or_expression '|' exclusive_or_expression
	{ binary($$, $1, "bitor", $3); }
	;

logical_and_expression:
	inclusive_or_expression
	| logical_and_expression TOK_ANDAND inclusive_or_expression
	{ binary($$, $1, "and", $3); }
	;

logical_or_expression:
	logical_and_expression
	| logical_or_expression TOK_OROR logical_and_expression
	{ binary($$, $1, "or", $3); }
	;

conditional_expression:
	logical_or_expression
	| logical_or_expression '?' comma_expression ':' conditional_expression
	{ $$=$2;
	  init($$, "if");
	  mto($$, $1);
	  mto($$, $3);
	  mto($$, $5);
	}
	;

assignment_expression:
	conditional_expression
	| cast_expression '=' assignment_expression
	{ binary($$, $1, "sideeffect", $3); stack($$).set("statement", "assign"); }
	| cast_expression TOK_MULTASSIGN assignment_expression
	{ binary($$, $1, "sideeffect", $3); stack($$).set("statement", "assign*"); }
	| cast_expression TOK_DIVASSIGN assignment_expression
	{ binary($$, $1, "sideeffect", $3); stack($$).set("statement", "assign_div"); }
	| cast_expression TOK_MODASSIGN assignment_expression
	{ binary($$, $1, "sideeffect", $3); stack($$).set("statement", "assign_mod"); }
	| cast_expression TOK_PLUSASSIGN assignment_expression
	{ binary($$, $1, "sideeffect", $3); stack($$).set("statement", "assign+"); }
	| cast_expression TOK_MINUSASSIGN assignment_expression
	{ binary($$, $1, "sideeffect", $3); stack($$).set("statement", "assign-"); }
	| cast_expression TOK_SLASSIGN assignment_expression
	{ binary($$, $1, "sideeffect", $3); stack($$).set("statement", "assign_shl"); }
	| cast_expression TOK_SRASSIGN assignment_expression
	{ binary($$, $1, "sideeffect", $3); stack($$).set("statement", "assign_shr"); }
	| cast_expression TOK_ANDASSIGN assignment_expression
	{ binary($$, $1, "sideeffect", $3); stack($$).set("statement", "assign_bitand"); }
	| cast_expression TOK_EORASSIGN assignment_expression
	{ binary($$, $1, "sideeffect", $3); stack($$).set("statement", "assign_bitxor"); }
	| cast_expression TOK_ORASSIGN assignment_expression
	{ binary($$, $1, "sideeffect", $3); stack($$).set("statement", "assign_bitor"); }
	;

comma_expression:
	assignment_expression
	| comma_expression ',' assignment_expression
	{ binary($$, $1, "comma", $3); }
	;

constant_expression:
	conditional_expression
	;

comma_expression_opt:
	/* nothing */
	{ init($$); stack($$).make_nil(); }
	| comma_expression
	;

/*** Declarations *******************************************************/


declaration:
	declaration_specifier ';'
	{
	  init($$);
	}
	| type_specifier ';'
	{
	  init($$);
	}
	| declaring_list ';'
	| default_declaring_list ';'
	;

default_declaring_list:
	declaration_qualifier_list identifier_declarator
		{
		  init($$);
		  PARSER.new_declaration(stack($1), stack($2), stack($$));
		}
	initializer_opt
		{
		  init($$);
		  stack($$).add("type")=stack($1);
		  decl_statement($$, $3, $4);
		}
	| type_qualifier_list identifier_declarator
	{
	  init($$);
	  PARSER.new_declaration(stack($1), stack($2), stack($$));
	}
	initializer_opt
	{
	  init($$);
	  stack($$).add("type")=stack($1);
	  decl_statement($$, $3, $4);
	}
	| default_declaring_list ',' identifier_declarator
		{
		  init($$);
		  const irept &t=stack($1).find("type");
		  PARSER.new_declaration(t, stack($3), stack($$));
		}
		initializer_opt
	{
	  $$=$1;
	  decl_statement($$, $4, $5);
	}
	;

declaring_list:			/* DeclarationSpec */
	declaration_specifier declarator
		{
		  // the symbol has to be visible during initialization
		  init($$);
		  PARSER.new_declaration(stack($1), stack($2), stack($$));
		}
		initializer_opt
	{
	  init($$);
	  stack($$).add("type")=stack($1);
	  decl_statement($$, $3, $4);
	}
	| type_specifier declarator
		{
		  // the symbol has to be visible during initialization
		  init($$);
		  PARSER.new_declaration(stack($1), stack($2), stack($$));
		}
		initializer_opt
	{
	  init($$);
	  stack($$).add("type")=stack($1);
	  decl_statement($$, $3, $4);
	}
	| declaring_list ',' declarator
		{
		  init($$);
		  const irept &t=stack($1).find("type");
		  PARSER.new_declaration(t, stack($3), stack($$));
		}
		initializer_opt
	{
	  $$=$1;
	  decl_statement($$, $4, $5);
	}
	;

declaration_specifier:
	basic_declaration_specifier
	| sue_declaration_specifier
	| typedef_declaration_specifier
	;

type_specifier:
	basic_type_specifier
	| sue_type_specifier
	| typedef_type_specifier
	| typeof_type_specifier
	;

declaration_qualifier_list:
	storage_class
	| type_qualifier_list storage_class
	{
	  $$=$1;
	  merge_types($$, $2);
	}
	| declaration_qualifier_list declaration_qualifier
	{
	  $$=$1;
	  merge_types($$, $2);
	}
	;

type_qualifier_list:
	type_qualifier
	| type_qualifier_list type_qualifier
	{
	  $$=$1;
	  merge_types($$, $2);
	}
	;

declaration_qualifier:
	storage_class
	| type_qualifier
	;

type_qualifier:
	TOK_CONST      { $$=$1; set($$, "const"); }
	| TOK_VOLATILE { $$=$1; set($$, "volatile"); }
	;

basic_declaration_specifier:
	declaration_qualifier_list basic_type_name
	{
	  $$=$1;
	  merge_types($$, $2);
	}
	| basic_type_specifier storage_class
	{
	  $$=$1;
	  merge_types($$, $2);
	}
	| basic_declaration_specifier declaration_qualifier
	{
	  $$=$1;
	  merge_types($$, $2);
	}
	| basic_declaration_specifier basic_type_name
	{
	  $$=$1;
	  merge_types($$, $2);
	};

basic_type_specifier:
	basic_type_name
	| type_qualifier_list basic_type_name
	{
	  $$=$1;
	  merge_types($$, $2);
	}
	| basic_type_specifier type_qualifier
	{
	  $$=$1;
	  merge_types($$, $2);
	}
	| basic_type_specifier basic_type_name
	{
	  $$=$1;
	  merge_types($$, $2);
	};

sue_declaration_specifier:
	declaration_qualifier_list elaborated_type_name
	{
	  $$=$1;
	  merge_types($$, $2);
	}
	| sue_type_specifier storage_class
	{
	  $$=$1;
	  merge_types($$, $2);
	}
	| sue_declaration_specifier declaration_qualifier
	{
	  $$=$1;
	  merge_types($$, $2);
	}
	;

sue_type_specifier:
	elaborated_type_name
	| type_qualifier_list elaborated_type_name
	{
	  $$=$1;
	  merge_types($$, $2);
	}
	| sue_type_specifier type_qualifier
	{
	  $$=$1;
	  merge_types($$, $2);
	}
	;

typedef_declaration_specifier:	/* DeclarationSpec */
	typedef_type_specifier storage_class
	{
	  $$=$1;
	  merge_types($$, $2);
	}
	| declaration_qualifier_list typedef_name
	{
	  $$=$1;
	  merge_types($$, $2);
	}
	| typedef_declaration_specifier declaration_qualifier
	{
	  $$=$1;
	  merge_types($$, $2);
	}
	;

typedef_type_specifier:		/* Type */
	typedef_name
	| type_qualifier_list typedef_name
	{
	  $$=$1;
	  merge_types($$, $2);
	}
	| typedef_type_specifier type_qualifier
	{
	  $$=$1;
	  merge_types($$, $2);
	}
	;

typeof_type_specifier:
	TOK_TYPEOF '(' comma_expression ')'
	{ $$ = $3;
	  locationt location=stack($$).location();
	  typet new_type("type_of");
	  new_type.subtype() = (typet &)(stack($$));
	  stack($$).swap(new_type);
	  stack($$).location()=location;
	  stack($$).set("#is_expression", true);
	}
	| TOK_TYPEOF '(' ptr_type_specifier  ')'
	{ $$ = $3;
	  locationt location=stack($$).location();
	  typet new_type("type_of");
	  new_type.subtype() = (typet &)(stack($$));
	  stack($$).swap(new_type);
	  stack($$).location()=location;
	  stack($$).set("#is_expression", false);
	}
	;

ptr_type_specifier:
	type_specifier
	|
	ptr_type_specifier '*'
	{ $$ = $1;
	  locationt location=stack($$).location();
	  typet new_type("pointer");
	  new_type.subtype() = (typet&) stack($$);
	  stack($$).swap(new_type);
	  stack($$).location()=location;
	}
	;

storage_class:
	TOK_TYPEDEF    { $$=$1; set($$, "typedef"); }
	| TOK_EXTERN   { $$=$1; set($$, "extern"); }
	| TOK_STATIC   { $$=$1; set($$, "static"); }
	| TOK_AUTO     { $$=$1; set($$, "auto"); }
	| TOK_REGISTER { $$=$1; set($$, "register"); }
	| TOK_INLINE   { $$=$1; set($$, "inline"); }
	;

basic_type_name:
	TOK_INT        { $$=$1; set($$, "int"); }
	| TOK_INT8     { $$=$1; set($$, "int8"); }
	| TOK_INT16    { $$=$1; set($$, "int16"); }
	| TOK_INT32    { $$=$1; set($$, "int32"); }
	| TOK_INT64    { $$=$1; set($$, "int64"); }
	| TOK_PTR32    { $$=$1; set($$, "ptr32"); }
	| TOK_PTR64    { $$=$1; set($$, "ptr64"); }
	| TOK_CHAR     { $$=$1; set($$, "char"); }
	| TOK_SHORT    { $$=$1; set($$, "short"); }
	| TOK_LONG     { $$=$1; set($$, "long"); }
	| TOK_FLOAT    { $$=$1; set($$, "float"); }
	| TOK_DOUBLE   { $$=$1; set($$, "double"); }
	| TOK_SIGNED   { $$=$1; set($$, "signed"); }
	| TOK_UNSIGNED { $$=$1; set($$, "unsigned"); }
	| TOK_VOID     { $$=$1; set($$, "empty"); }
	| TOK_BOOL     { $$=$1; set($$, "bool"); }
	;

elaborated_type_name:
	aggregate_name
	| enum_name
	;

aggregate_name:
	aggregate_key
		{
		  // an anon struct
		  exprt symbol("symbol");

		  symbol.set("#base_name", PARSER.get_anon_name());

		  init($$);
		  PARSER.new_declaration(stack($1), symbol, stack($$), true);
		}
		'{' member_declaration_list_opt '}'
	{
	  typet &type=stack($2).type();
	  type.add("components").get_sub().swap(stack($4).add("operands").get_sub());

	  // grab symbol
	  init($$, "symbol");
	  stack($$).set("identifier", stack($2).get("name"));
	  stack($$).location()=stack($2).location();

	  PARSER.move_declaration(stack($2));
	}
	| aggregate_key identifier_or_typedef_name
		{
		  PARSER.new_declaration(stack($1), stack($2), stack($$), true);

		  exprt tmp(stack($$));
		  tmp.type().id("incomplete_"+tmp.type().id_string());
		  PARSER.move_declaration(tmp);
		}
		'{' member_declaration_list_opt '}'
	{
	  typet &type=stack($3).type();
	  type.add("components").get_sub().swap(stack($5).add("operands").get_sub());

	  // grab symbol
	  init($$, "symbol");
	  stack($$).set("identifier", stack($3).get("name"));
	  stack($$).location()=stack($3).location();

	  PARSER.move_declaration(stack($3));
	}
	| aggregate_key identifier_or_typedef_name
	{
	  do_tag($1, $2);
	  $$=$2;
	}
	;

aggregate_key:
	TOK_STRUCT
	{ $$=$1; set($$, "struct"); }
	| TOK_UNION
	{ $$=$1; set($$, "union"); }
	;

member_declaration_list_opt:
		  /* Nothing */
	{
	  init($$, "declaration_list");
	}
	| member_declaration_list
	;

member_declaration_list:
	  member_declaration
	| member_declaration_list member_declaration
	{
	  assert(stack($1).id()=="declaration_list");
	  assert(stack($2).id()=="declaration_list");
	  $$=$1;
	  Forall_operands(it, stack($2))
	    stack($$).move_to_operands(*it);
	  stack($2).clear();
	}
	;

member_declaration:
	member_declaring_list ';'
	| member_default_declaring_list ';'
	| ';' /* empty declaration */
	{
	  init($$, "declaration_list");
	}
	;

member_default_declaring_list:
	type_qualifier_list member_identifier_declarator
	{
	  init($$, "declaration_list");

	  exprt declaration;

	  PARSER.new_declaration(stack($1), stack($2), declaration, false, false);

	  stack($$).move_to_operands(declaration);
	}
	| member_default_declaring_list ',' member_identifier_declarator
	{
	  exprt declaration;

	  typet type;
	  PARSER.new_declaration(stack($1), stack($3), declaration, false, false);

	  $$=$1;
	  stack($$).move_to_operands(declaration);
	}
	;

member_declaring_list:
	type_specifier member_declarator
	{
	  init($$, "declaration_list");

	  // save the type_specifier
	  stack($$).add("declaration_type")=stack($1);

	  exprt declaration;
	  PARSER.new_declaration(stack($1), stack($2), declaration, false, false);

	  stack($$).move_to_operands(declaration);
	}
	| member_declaring_list ',' member_declarator
	{
	  exprt declaration;

	  irept declaration_type(stack($1).find("declaration_type"));
	  PARSER.new_declaration(declaration_type, stack($3), declaration, false, false);

	  $$=$1;
	  stack($$).move_to_operands(declaration);
	}
	;

member_declarator:
	declarator bit_field_size_opt
	{
	  if(!stack($2).is_nil())
	  {
		$$=$2;
		stack($$).add("subtype").swap(stack($1));
	  }
	  else
		$$=$1;
	}
	| /* empty */
	{
	  init($$);
	  stack($$).make_nil();
	}
	| bit_field_size
	{
	  $$=$1;
	  stack($$).add("subtype").make_nil();
	}
	;

member_identifier_declarator:
	identifier_declarator
		{ /* note: this mid-rule action (suggested by the grammar) */
		  /*       is not used because we don't have direct access */
		  /*       to the declaration specifier; therefore the     */
		  /*       symbol table is not updated ASAP (which is only */
		  /*       a minor problem; bit_field_size_opt expression  */
		  /*       cannot use the identifier_declarator)           */
		 }
		bit_field_size_opt
	{
	  $$=$1;
	  if(!stack($3).is_nil())
		merge_types($$, $3);
	}
	| bit_field_size
	{
	  // TODO
	  assert(0);
	}
	;

bit_field_size_opt:
	/* nothing */
	{
	  init($$);
	  stack($$).make_nil();
	}
	| bit_field_size
	;

bit_field_size:			/* Expression */
	':' constant_expression
	{
	  $$=$1; set($$, "c_bitfield");
	  stack($$).set("size", stack($2));
	}
	;

/* note: although the grammar didn't suggest mid-rule actions here	*/
/*       we handle enum exactly like struct/union			*/
enum_name:			/* Type */
	enum_key
		{
		  // an anon enum
		  exprt symbol("symbol");
		  symbol.set("#base_name", PARSER.get_anon_name());

		  PARSER.new_declaration(stack($1), symbol, stack($$), true);

		  exprt tmp(stack($$));
		  tmp.type().id("incomplete_"+tmp.type().id_string());
		  PARSER.move_declaration(tmp);
		}
		'{' enumerator_list '}'
	{
	  // grab symbol
	  init($$, "symbol");
	  stack($$).set("identifier", stack($2).get("name"));
	  stack($$).location()=stack($2).location();

	  do_enum_members((const typet &)stack($$), stack($4));

	  PARSER.move_declaration(stack($2));
	}
	| enum_key identifier_or_typedef_name
		{ /* !!! mid-rule action !!! */
		  PARSER.new_declaration(stack($1), stack($2), stack($$), true);

		  exprt tmp(stack($$));
		  tmp.type().id("incomplete_"+tmp.type().id_string());
		  PARSER.move_declaration(tmp);
		}
		'{' enumerator_list '}'
	{
	  // grab symbol
	  init($$, "symbol");
	  stack($$).set("identifier", stack($3).get("name"));
	  stack($$).location()=stack($3).location();

	  do_enum_members((const typet &)stack($$), stack($5));

	  PARSER.move_declaration(stack($3));
	}
	| enum_key identifier_or_typedef_name
	{
	  do_tag($1, $2);
	  $$=$2;
	}
	;

enum_key: TOK_ENUM
	{
	  $$=$1;
	  set($$, "c_enum");
	}
	;

enumerator_list:		/* MemberList */
	enumerator_declaration
	{
	  init($$);
	  mto($$, $1);
	}
	| enumerator_list ',' enumerator_declaration
	{
	  $$=$1;
	  mto($$, $3);
	}
	| enumerator_list ','
	{
	  $$=$1;
	}
	;

enumerator_declaration:
	  identifier_or_typedef_name enumerator_value_opt
	{
	  init($$);
	  irept type("enum");
	  PARSER.new_declaration(type, stack($1), stack($$));
	  stack($$).set("is_macro", true);
	  stack($$).add("value").swap(stack($2));
	}
	;

enumerator_value_opt:		/* Expression */
	/* nothing */
	{
	  init($$);
	  stack($$).make_nil();
	}
	| '=' constant_expression
	{
	  $$=$2;
	}
	;

parameter_type_list:		/* ParameterList */
	parameter_list
	| parameter_list ',' TOK_ELLIPSIS
	{
	  typet tmp("ansi_c_ellipsis");
	  $$=$1;
	  ((typet &)stack($$)).move_to_subtypes(tmp);
	}
	| KnR_parameter_list
	;

KnR_parameter_list:
	KnR_parameter
	{
          init($$, "arguments");
          mts($$, $1);
	}
	| KnR_parameter_list ',' KnR_parameter
	{
          $$=$1;
          mts($$, $3);
	}
	;

KnR_parameter: identifier
	{
          init($$);
	  irept type("KnR");
	  PARSER.new_declaration(type, stack($1), stack($$));
	}
	;

parameter_list:
	parameter_declaration
	{
		  init($$, "arguments");
		  mts($$, $1);
		}
	| parameter_list ',' parameter_declaration
	{
		  $$=$1;
		  mts($$, $3);
	}
		;

parameter_declaration:
	declaration_specifier
	{
	  init($$);
	  exprt nil;
	  nil.make_nil();
	  PARSER.new_declaration(stack($1), nil, stack($$));
	}
	| declaration_specifier parameter_abstract_declarator
	{
	  init($$);
	  PARSER.new_declaration(stack($1), stack($2), stack($$));
	}
	| declaration_specifier identifier_declarator
	{
	  init($$);
	  PARSER.new_declaration(stack($1), stack($2), stack($$));
	}
	| declaration_specifier parameter_typedef_declarator
	{
	  init($$);
	  merge_types($1, $2);
	  exprt nil;
	  nil.make_nil();
	  PARSER.new_declaration(stack($1), nil, stack($$));
	}
	| declaration_qualifier_list
	{
	  init($$);
	  exprt nil;
	  nil.make_nil();
	  PARSER.new_declaration(stack($1), nil, stack($$));
	}
	| declaration_qualifier_list parameter_abstract_declarator
	{
	  init($$);
	  PARSER.new_declaration(stack($1), stack($2), stack($$));
	}
	| declaration_qualifier_list identifier_declarator
	{
	  init($$);
	  PARSER.new_declaration(stack($1), stack($2), stack($$));
	}
	| type_specifier
	{
	  init($$);
	  exprt nil;
	  nil.make_nil();
	  PARSER.new_declaration(stack($1), nil, stack($$));
	}
	| type_specifier parameter_abstract_declarator
	{
	  init($$);
	  PARSER.new_declaration(stack($1), stack($2), stack($$));
	}
	| type_specifier identifier_declarator
	{
	  init($$);
	  PARSER.new_declaration(stack($1), stack($2), stack($$));
	}
	| type_specifier parameter_typedef_declarator
	{
	  init($$);
	  merge_types($1, $2);
	  exprt nil;
	  nil.make_nil();
	  PARSER.new_declaration(stack($1), nil, stack($$));
	}
	| type_qualifier_list
	{
	  init($$);
	  exprt nil;
	  nil.make_nil();
	  PARSER.new_declaration(stack($1), nil, stack($$));
	}
	| type_qualifier_list parameter_abstract_declarator
	{
	  init($$);
	  PARSER.new_declaration(stack($1), stack($2), stack($$));
	}
	| type_qualifier_list identifier_declarator
	{
	  init($$);
	  PARSER.new_declaration(stack($1), stack($2), stack($$));
	}
	;

identifier_or_typedef_name:
	identifier
	| typedef_name
	;

type_name:
	type_specifier
	| type_specifier abstract_declarator
	{
	  $$=$1;
	  make_subtype($$, $2);
	}
	| type_qualifier_list
	| type_qualifier_list abstract_declarator
	{
	  $$=$1;
	  make_subtype($$, $2);
	}
	;

initializer_opt:
	/* nothing */
	{
	  newstack($$);
	  stack($$).make_nil();
	}
	| '=' initializer
	{ $$ = $2; }
	;

/* note: the following has been changed from the ANSI-C grammar:	*/
/*	- an initializer is not an assignment_expression,		*/
/*	  but a constant_expression					*/
/*	  (which probably is the case anyway for 99.9% of C programs)	*/

initializer:
	'{' initializer_list '}'
	{
	  $$=$1;
	  set($$, "constant");
	  stack($$).type().id("incomplete_array");
	  stack($$).operands().swap(stack($2).operands());
	}
	| '{' initializer_list ',' '}'
	{
	  $$=$1;
	  set($$, "constant");
	  stack($$).type().id("incomplete_array");
	  stack($$).operands().swap(stack($2).operands());
	}
	| constant_expression	/* was: assignment_expression */
	;

initializer_list:
	initializer
	{
	  $$=$1;
	  exprt tmp;
	  tmp.swap(stack($$));
	  stack($$).clear();
	  stack($$).move_to_operands(tmp);
	}
	| initializer_list ',' initializer
	{
	  $$=$1;
	  mto($$, $3);
	}
	;

/*** Statements *********************************************************/

statement:
	labeled_statement
	| compound_statement
	| declaration_statement
	| expression_statement
	| selection_statement
	| iteration_statement
	| jump_statement
	;

declaration_statement:
	declaration
	{
	  init($$);
	  statement($$, "decl-block");
	  stack($$).operands().swap(stack($1).operands());
	}
	;

labeled_statement:
	identifier_or_typedef_name ':' statement
	{
	  $$=$2;
	  statement($$, "label");
	  stack($$).set("label", stack($1).get("#base_name"));
	  mto($$, $3);
	}
	| TOK_CASE constant_expression ':' statement
	{
	  $$=$1;
	  statement($$, "label");
	  mto($$, $4);
	  static_cast<exprt &>(stack($$).add("case")).
		move_to_operands(stack($2));
	}
	| TOK_DEFAULT ':' statement
	{
	  $$=$1;
	  statement($$, "label");
	  mto($$, $3);
	  stack($$).set("default", true);
	}
	;

/* note: the following has been changed from the ANSI-C grammar:	*/
/*	- rule compound_scope is used to prepare an inner scope for	*/
/*	  each compound_statement (and to obtain the line infos)	*/

compound_statement:
	compound_scope '{' '}'
	{
	  $$=$2;
	  statement($$, "block");
	  PARSER.pop_scope();
	}
	| compound_scope '{' statement_list '}'
	{
	  $$=$3;
	  PARSER.pop_scope();
	}
	;

compound_scope:
	/* nothing */
	{
	  unsigned prefix=++PARSER.current_scope().compound_counter;
	  PARSER.new_scope(i2string(prefix)+"::");
	}
	;

statement_list:
	statement
	{
	  $$=$1;
	  to_code(stack($$)).make_block();
	}
	| statement_list statement
	{
	  mto($$, $2);
	}
	;

expression_statement:
	comma_expression_opt ';'
	{
	  $$=$2;

	  if(stack($1).is_nil())
	    statement($$, "skip");
	  else
	  {
	    statement($$, "expression");
	    mto($$, $1);
	  }
	}
	;

selection_statement:
	  TOK_IF '(' comma_expression ')' statement
	{
	  $$=$1;
	  statement($$, "ifthenelse");
	  mto($$, $3);
	  mto($$, $5);
	}
	| TOK_IF '(' comma_expression ')' statement TOK_ELSE statement
	{
	  $$=$1;
	  statement($$, "ifthenelse");
	  mto($$, $3);
	  mto($$, $5);
	  mto($$, $7);
	}
	| TOK_SWITCH '(' comma_expression ')' statement
	{
	  $$=$1;
	  statement($$, "switch");
	  mto($$, $3);
	  mto($$, $5);
	}
	;

declaration_or_expression_statement:
	  declaration_statement
	| expression_statement
	;

iteration_statement:
	TOK_WHILE '(' comma_expression_opt ')' statement
	{
	  $$=$1;
	  statement($$, "while");
	  mto($$, $3);
	  mto($$, $5);
	}
	| TOK_DO statement TOK_WHILE '(' comma_expression ')' ';'
	{
	  $$=$1;
	  statement($$, "dowhile");
	  mto($$, $5);
	  mto($$, $2);
	}
	| TOK_FOR '(' declaration_or_expression_statement
		comma_expression_opt ';' comma_expression_opt ')' statement
	{
	  $$=$1;
	  statement($$, "for");
	  mto($$, $3);
	  mto($$, $4);
	  mto($$, $6);
	  mto($$, $8);
	}
	;

jump_statement:
	TOK_GOTO identifier_or_typedef_name ';'
	{
	  $$=$1;
	  statement($$, "goto");
	  stack($$).set("destination", stack($2).get("#base_name"));
	}
	| TOK_CONTINUE ';'
	{ $$=$1; statement($$, "continue"); }
	| TOK_BREAK ';'
	{ $$=$1; statement($$, "break"); }
	| TOK_RETURN ';'
	{ $$=$1; statement($$, "return"); }
	| TOK_RETURN comma_expression ';'
	{ $$=$1; statement($$, "return"); mto($$, $2); }
	;

/*** External Definitions ***********************************************/


/* note: the following has been changed from the ANSI-C grammar:	*/
/*	- translation unit is allowed to be empty!			*/

translation_unit:
	/* nothing */
	| external_definition_list
	;

external_definition_list:
	external_definition
	| external_definition_list external_definition
	;

external_definition:
	function_definition
	| declaration
	;

function_definition:
	function_head KnR_parameter_header_opt compound_statement
	{ 
          stack($1).add("value").swap(stack($3));
          PARSER.pop_scope();
          PARSER.move_declaration(stack($1));
	}
	;

KnR_parameter_header_opt:
          /* empty */
	| KnR_parameter_header
	;

KnR_parameter_header:
	  KnR_parameter_declaration
	| KnR_parameter_header KnR_parameter_declaration
	;

KnR_parameter_declaration: declaring_list ';'
	;

function_head:
	identifier_declarator /* void */
	{
	  init($$);
	  irept type("int");
	  PARSER.new_declaration(type, stack($1), stack($$));
	  create_function_scope(stack($$));
	}
	| declaration_specifier declarator
	{
	  init($$);
	  PARSER.new_declaration(stack($1), stack($2), stack($$));
	  create_function_scope(stack($$));
	}
	| type_specifier declarator
	{
	  init($$);
	  PARSER.new_declaration(stack($1), stack($2), stack($$));
	  create_function_scope(stack($$));
	}
	| declaration_qualifier_list identifier_declarator
	{
	  init($$);
	  PARSER.new_declaration(stack($1), stack($2), stack($$));
	  create_function_scope(stack($$));
	}
	| type_qualifier_list identifier_declarator
	{
	  init($$);
	  PARSER.new_declaration(stack($1), stack($2), stack($$));
	  create_function_scope(stack($$));
	}
	;

declarator:
	identifier_declarator
	| typedef_declarator
	;

typedef_declarator:
	paren_typedef_declarator
	| parameter_typedef_declarator
	;

parameter_typedef_declarator:
	typedef_name
	| typedef_name postfixing_abstract_declarator
	{
	  $$=$1;
	  make_subtype($$, $2);
	}
	| clean_typedef_declarator
	;

clean_typedef_declarator:	/* Declarator */
	clean_postfix_typedef_declarator
	| '*' parameter_typedef_declarator
	{
	  $$=$2;
	  do_pointer($1, $2);
	}
	| '*' type_qualifier_list parameter_typedef_declarator
	{
	  merge_types($2, $3);
	  $$=$2;
	  do_pointer($1, $2);
	}
	;

clean_postfix_typedef_declarator:	/* Declarator */
	'(' clean_typedef_declarator ')'
	{ $$ = $2; }
	| '(' clean_typedef_declarator ')' postfixing_abstract_declarator
	{
	  /* note: this is a pointer ($2) to a function ($4) */
	  /* or an array ($4)! */
	  $$=$2;
	  make_subtype($$, $4);
	}
	;

paren_typedef_declarator:	/* Declarator */
	paren_postfix_typedef_declarator
	| '*' '(' simple_paren_typedef_declarator ')'
	{
	  $$=$3;
	  do_pointer($1, $3);
	}
	| '*' type_qualifier_list '(' simple_paren_typedef_declarator ')'
	{
	  // not sure where the type qualifiers belong
	  merge_types($2, $4);
	  $$=$2;
	  do_pointer($1, $2);
	}
	| '*' paren_typedef_declarator
	{
	  $$=$2;
	  do_pointer($1, $2);
	}
	| '*' type_qualifier_list paren_typedef_declarator
	{
	  merge_types($2, $3);
	  $$=$2;
	  do_pointer($1, $2);
	}
	;

paren_postfix_typedef_declarator:	/* Declarator */
	'(' paren_typedef_declarator ')'
	{ $$ = $2; }
	| '(' simple_paren_typedef_declarator postfixing_abstract_declarator ')'
	{	/* note: this is a function ($3) with a typedef name ($2) */
	  $$=$2;
	  make_subtype($$, $3);
	}
	| '(' paren_typedef_declarator ')' postfixing_abstract_declarator
	{
	  /* note: this is a pointer ($2) to a function ($4) */
	  /* or an array ($4)! */
	  $$=$2;
	  make_subtype($$, $4);
	}
	;

simple_paren_typedef_declarator:
	typedef_name
	{
	  assert(0);
	}
	| '(' simple_paren_typedef_declarator ')'
	{ $$ = $2; }
	;

identifier_declarator:
	  unary_identifier_declarator
	| paren_identifier_declarator
	;

unary_identifier_declarator:
	postfix_identifier_declarator
	| '*' identifier_declarator
	{
	  $$=$2;
	  do_pointer($1, $2);
	}
	| '*' type_qualifier_list identifier_declarator
	{
	  merge_types($2, $3);
	  $$=$2;
	  do_pointer($1, $2);
	}
	;

postfix_identifier_declarator:
        paren_identifier_declarator postfixing_abstract_declarator
	{
	  /* note: this is a function or array ($2) with name ($1) */
	  $$=$1;
	  make_subtype($$, $2);
	}
	| '(' unary_identifier_declarator ')'
	{ $$ = $2; }
	| '(' unary_identifier_declarator ')' postfixing_abstract_declarator
	{
	  /* note: this is a pointer ($2) to a function ($4) */
	  /* or an array ($4)! */
	  $$=$2;
	  make_subtype($$, $4);
	}
	;

paren_identifier_declarator:
	identifier
	| '(' paren_identifier_declarator ')'
	{ $$=$2; };

abstract_declarator:
	unary_abstract_declarator
	| postfix_abstract_declarator
	| postfixing_abstract_declarator
	;

parameter_abstract_declarator:
	parameter_unary_abstract_declarator
	;

postfixing_abstract_declarator:	/* AbstrDeclarator */
	array_abstract_declarator
	| '(' ')'
	{
	  $$=$1;
	  set($$, "code");
	  stack($$).add("arguments");
	  stack($$).add("subtype").make_nil();
	}
	| '('
	  {
		unsigned prefix=++PARSER.current_scope().compound_counter;
		PARSER.new_scope(i2string(prefix)+"::");
	  }
	  parameter_type_list ')'
	{
	  $$=$1;
	  set($$, "code");
	  stack($$).add("subtype").make_nil();
	  stack($$).add("arguments").get_sub().
	    swap(stack($3).add("subtypes").get_sub());
	  PARSER.pop_scope();
	}
	;

array_abstract_declarator:	/* AbstrDeclarator */
	'[' ']'
	{
	  $$=$1;
	  set($$, "incomplete_array");
	  stack($$).add("subtype").make_nil();
	}
	| '[' constant_expression ']'
	{
	  $$=$1;
	  set($$, "array");
	  stack($$).add("size").swap(stack($2));
	  stack($$).add("subtype").make_nil();
	}
	| array_abstract_declarator '[' constant_expression ']'
	{
	  // we need to push this down
	  $$=$1;
	  set($2, "array");
	  stack($2).add("size").swap(stack($3));
	  stack($2).add("subtype").make_nil();
	  make_subtype($1, $2);
	}
	;

unary_abstract_declarator:
	'*'
	{
	  $$=$1;
	  set($$, "pointer");
	  stack($$).add("subtype").make_nil();
	}
	| '*' type_qualifier_list
	{
	  $$=$2;
	  exprt nil_declarator(static_cast<const exprt &>(nil_rep));
	  merge_types(stack($2), nil_declarator);
	  do_pointer($1, $2);
	}
	| '*' abstract_declarator
	{
	  $$=$2;
	  do_pointer($1, $2);
	}
	| '*' type_qualifier_list abstract_declarator
	{
	  $$=$2;
	  merge_types($2, $3);
	  do_pointer($1, $2);
	}
	;

parameter_unary_abstract_declarator:
	'*'
	{
          $$=$1;
          set($$, "pointer");
          stack($$).add("subtype").make_nil();
	}
	| '*' type_qualifier_list
	{
          $$=$2;
          exprt nil_declarator(static_cast<const exprt &>(nil_rep));
          merge_types(stack($2), nil_declarator);
          do_pointer($1, $2);
	}
	| '*' parameter_abstract_declarator
	{
          $$=$2;
          do_pointer($1, $2);
	}
	| '*' type_qualifier_list parameter_abstract_declarator
	{
          $$=$2;
          merge_types($2, $3);
          do_pointer($1, $2);
	}
	;

postfix_abstract_declarator:
	'(' unary_abstract_declarator ')'
	{ $$ = $2; }
	| '(' postfix_abstract_declarator ')'
	{ $$ = $2; }
	| '(' postfixing_abstract_declarator ')'
	{ $$ = $2; }
	| '(' unary_abstract_declarator ')' postfixing_abstract_declarator
	{
	  /* note: this is a pointer ($2) to a function ($4) */
	  /* or an array ($4) of pointers with name ($2)! */
	  $$=$2;
	  make_subtype($$, $4);
	}
	;

%%
