%{
/*
 *  FILE: parser.y
 *  AUTH: Michael John Radwin <mjr@acm.org>
 *
 *  DESC: stubgen grammar description.  Portions borrowed from
 *  Newcastle University's Arjuna project (http://arjuna.ncl.ac.uk/),
 *  and Jeff Lee's ANSI Grammar
 *  (ftp://ftp.uu.net/usenet/net.sources/ansi.c.grammar.Z)
 *  This grammar is only a subset of the real C++ language.
 *
 *  DATE: Thu Aug 15 13:10:06 EDT 1996
 *   $Id: parser.y,v 1.1 2002-06-06 22:09:16 ocoursiere Exp $
 *
 *  Copyright (c) 1996-1998  Michael John Radwin
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *  --------------------------------------------------------------------
 * 
 *  $Log: not supported by cvs2svn $
 *  Revision 1.1  2001/11/07 10:06:07  ithamar
 *  Added stubgen to CVS
 *
 *  Revision 1.72  1998/07/07 00:14:06  mradwin
 *  removed extra space from throw_decl, cleaned up memory leak
 *  in ctor skeleton
 *
 *  Revision 1.71  1998/06/11 14:52:09  mradwin
 *  allow for empty class declarations, such as
 *  class Element {};
 *  also, differentiate structs from classes with
 *  new STRUCT_KIND tag.
 *  New version: 2.04
 *
 *  Revision 1.70  1998/05/11 19:49:11  mradwin
 *  Version 2.03 (updated copyright information).
 *
 *  Revision 1.69  1998/04/07 23:43:34  mradwin
 *  changed error-handling code significantly.
 *  several functions now return a value, and instead of
 *  calling fatal(), we do a YYABORT or YYERROR to get out
 *  of the parsing state.
 *  New version: 2.02.
 *
 *  Revision 1.68  1998/03/28 02:59:41  mradwin
 *  working on slightly better error recovery; not done yet.
 *
 *  Revision 1.67  1998/03/28 02:34:56  mradwin
 *  added multi-line function parameters
 *  also changed pointer and reference (* and &) types so there
 *  is no trailing space before the parameter name.
 *
 *  Revision 1.66  1998/01/12 19:39:11  mradwin
 *  modified rcsid
 *
 *  Revision 1.65  1997/11/13 22:50:55  mradwin
 *  moved copyright from parser.y to main.c
 *
 *  Revision 1.64  1997/11/13 22:40:15  mradwin
 *  fixed a silly comment bug
 *
 *  Revision 1.63  1997/11/13 22:37:31  mradwin
 *  changed char[] to char * to make non-gcc compilers
 *  a little happier.  We need to #define const to nothing
 *  for other compilers as well.
 *
 *  Revision 1.62  97/11/13  21:29:30  21:29:30  mradwin (Michael Radwin)
 *  moved code from parser.y to main.c
 * 
 *  Revision 1.61  1997/11/13 21:10:17  mradwin
 *  renamed stubgen.[ly] to parser.y lexer.l
 *
 *  Revision 1.60  1997/11/11 04:11:29  mradwin
 *  fixed command-line flags: invalid options now force usgage.
 *
 *  Revision 1.59  1997/11/11 04:03:56  mradwin
 *  changed version info
 *
 *  Revision 1.58  1997/11/11 03:54:05  mradwin
 *  fixed a long-standing bug with -b option.  a typo was causing
 *  the -b flag to be ignored.
 *
 *  Revision 1.57  1997/11/11 03:52:06  mradwin
 *  changed fatal()
 *
 *  Revision 1.56  1997/11/05 03:02:02  mradwin
 *  Modified logging routines.
 *
 *  Revision 1.55  1997/11/05 02:14:38  mradwin
 *  Made some compiler warnings disappear.
 *
 *  Revision 1.54  1997/11/01 23:26:13  mradwin
 *  new Revision string and usage info
 *
 *  Revision 1.53  1997/11/01 23:12:43  mradwin
 *  greatly improved error-recovery.  errors no longer spill over
 *  into other files because the yyerror state is properly reset.
 *
 *  Revision 1.52  1997/10/27 01:14:23  mradwin
 *  fixed constant_value so it supports simple arithmetic.  it's
 *  not as robust as full expressions, but this will handle the
 *  char buffer[BUFSIZE + 1] problem.
 *
 *  Also removed expansion rules that simply did { $$ = $1; } because
 *  that action is implicit anyway.
 *
 *  Revision 1.51  1997/10/26 23:16:32  mradwin
 *  changed inform_user and fatal functions to use varargs
 *
 *  Revision 1.50  1997/10/26 22:27:07  mradwin
 *  Fixed this bug:
 *  stubgen dies on the following because the protected section is empty:
 *
 *  class WidgetCsg : public WidgetLens {
 *   protected:
 *
 *   public:
 *       virtual ~WidgetCsg() {}
 *                WidgetCsg();
 *  };
 *
 *  Error:
 *  stubgen version 2.0-beta $Revision: 1.1 $.
 *  parse error at line 4, file test.H:
 *   public:
 *        ^
 *
 *  Revision 1.49  1997/10/16 19:42:48  mradwin
 *  added support for elipses, static member/array initializers,
 *  and bitfields.
 *
 *  Revision 1.48  1997/10/16 17:35:39  mradwin
 *  cleaned up usage info
 *
 *  Revision 1.47  1997/10/16 17:12:59  mradwin
 *  handle extern "C" blocks better now, and support multi-line
 *  macros.  still need error-checking.
 *
 *  Revision 1.46  1997/10/15 22:09:06  mradwin
 *  changed tons of names.  stubelem -> sytaxelem,
 *  stubin -> infile, stubout -> outfile, stublog -> logfile.
 *
 *  Revision 1.45  1997/10/15 21:33:36  mradwin
 *  fixed up function_hdr
 *
 *  Revision 1.44  1997/10/15 21:33:02  mradwin
 *  *** empty log message ***
 *
 *  Revision 1.43  1997/10/15 17:42:37  mradwin
 *  added support for 'extern "C" { ... }' blocks.
 *
 *  Revision 1.42  1997/09/26 20:59:18  mradwin
 *  now allow "struct foobar *f" to appear in a parameter
 *  list or as a variable decl.  Had to remove the
 *  class_or_struct rule and blow up the class_specifier
 *  description.
 *
 *  Revision 1.41  1997/09/26 19:02:18  mradwin
 *  fixed memory leak involving template decls in skeleton code.
 *  Leads me to believe that skel_elemcmp() is flawed, because
 *  it may rely in parent->templ info.
 *
 *  Revision 1.40  1997/09/26 18:44:22  mradwin
 *  changed parameter handing from char *'s to an argument type
 *  to facilitate comparisons between skeleton code
 *  and header code.  Now we can correctly recognize different
 *  parameter names while still maintaining the same signature.
 *
 *  Revision 1.39  1997/09/26 00:47:29  mradwin
 *  added better base type support -- recognize things like
 *  "long long" and "short int" now.
 *
 *  Revision 1.38  1997/09/19 18:16:37  mradwin
 *  allowed an instance name to come after a class, struct,
 *  union, or enum.  This improves parseability of typedefs
 *  commonly found in c header files, although true typedefs are
 *  not understood.
 *
 *  Revision 1.37  1997/09/15 22:38:28  mradwin
 *  did more revision on the SGDEBUG stuff
 *
 *  Revision 1.36  1997/09/15 19:05:26  mradwin
 *  allow logging to be compiled out by turning off SGDEBUG
 *
 *  Revision 1.35  1997/09/12 00:58:43  mradwin
 *  duh, silly me.  messed up compilation.
 *
 *  Revision 1.34  1997/09/12 00:57:49  mradwin
 *  Revision string inserted in usage
 *
 *  Revision 1.33  1997/09/12 00:51:19  mradwin
 *  string copyright added to code for binary copyright.
 *
 *  Revision 1.32  1997/09/12 00:47:21  mradwin
 *  some more compactness of grammar with parameter_list_opt
 *  and also ampersand_opt
 *
 *  Revision 1.31  1997/09/12 00:26:19  mradwin
 *  better template support, but still poor
 *
 *  Revision 1.30  1997/09/08 23:24:51  mradwin
 *  changes to error-handling code.
 *  also got rid of the %type <flag> for the top-level rules
 *
 *  Revision 1.30  1997/09/08 23:20:02  mradwin
 *  some error reporting changes and default values for top-level
 *  grammar stuff.
 *
 *  Revision 1.29  1997/09/08 17:54:24  mradwin
 *  cleaned up options and usage info.
 *
 *  Revision 1.28  1997/09/05 19:38:04  mradwin
 *  changed options for .ext instead of -l or -x
 *
 *  Revision 1.27  1997/09/05 19:17:06  mradwin
 *  works for scanning old versions, except for parameter
 *  names that differ between .H and .C files.
 *
 *  Revision 1.26  1997/09/05 16:34:36  mradwin
 *  GPL-ized code.
 *
 *  Revision 1.25  1997/09/05 16:11:44  mradwin
 *  some simple cleanup before GPL-izing the code
 *
 *  Revision 1.24  1997/09/04 19:50:34  mradwin
 *  whoo-hoo!  by blowing up the description
 *  exponentially, it works!
 *
 *  Revision 1.23  1997/03/20 16:05:41  mjr
 *  renamed syntaxelem to syntaxelem_t, cleaned up throw_decl
 *
 *  Revision 1.22  1996/10/02 15:16:57  mjr
 *  using pathname.h instead of libgen.h
 *
 *  Revision 1.21  1996/09/12 14:44:49  mjr
 *  Added throw decl recognition (great, another 4 bytes in syntaxelem)
 *  and cleaned up the grammar so that const_opt appears in far fewer
 *  places.  const_opt is by default 0 as well, so we don't need to
 *  pass it as an arg to new_elem().
 *
 *  I also added a fix to a potential bug with the MINIT and INLIN
 *  exclusive start states.  I think they could have been confused
 *  by braces within comments, so now I'm grabbing comments in those
 *  states as well.
 *
 *  Revision 1.20  1996/09/12 04:55:22  mjr
 *  changed expand strategy.  Don't expand while parsing now;
 *  enqueue as we go along and expand at the end.  Eventually
 *  we'll need to provide similar behavior for when we parse
 *  .C files
 *
 *  Revision 1.19  1996/09/12 03:46:10  mjr
 *  No concrete changes in code.  Just added some sanity by
 *  factoring out code into util.[ch] and putting some prototypes
 *  that were in table.h into stubgen.y where they belong.
 *
 *  Revision 1.18  1996/09/06 14:32:48  mjr
 *  defined the some_KIND constants for clarity, and made
 *  expandClass return immediately if it was give something other
 *  than a CLASS_KIND element.
 *
 *  Revision 1.17  1996/09/06 14:05:44  mjr
 *  Almost there with expanded operator goodies.  Still need
 *  to get OPERATOR type_name to work.
 *
 *  Revision 1.16  1996/09/04 22:28:09  mjr
 *  nested classes work and default arguments are now removed
 *  from the parameter lists.
 *
 *  Revision 1.15  1996/09/04 20:01:57  mjr
 *  non-functional expanded code.  needs work.
 *
 *  Revision 1.14  1996/09/01 21:29:34  mjr
 *  put the expanded_operator code back in as a useless rule.
 *  oughta think about fixing it up if possible
 *
 *  Revision 1.13  1996/09/01 20:59:48  mjr
 *  Added collectMemberInitList() function, which is similar
 *  to collectInlineDef() and also the exclusive state MINIT
 *
 *  Revision 1.12  1996/08/23 05:09:19  mjr
 *  fixed up some more portability things
 *
 *  Revision 1.11  1996/08/22 02:43:47  mjr
 *  added parse error message (using O'Reilly p. 274)
 *
 *  Revision 1.10  1996/08/21 18:33:50  mjr
 *  added support for template instantiation in the type_name
 *  rule.  surprisingly it didn't cause any shift/reduce conflicts.
 *
 *  Revision 1.9  1996/08/21 17:40:56  mjr
 *  added some cpp directives for porting to WIN32
 *
 *  Revision 1.8  1996/08/21 00:00:19  mjr
 *  approaching stability and usability.  command line arguments
 *  are handled now and the fopens and fcloses appear to work.
 *
 *  Revision 1.7  1996/08/20 20:44:23  mjr
 *  added initial support for optind but it is incomplete.
 *
 *  Revision 1.6  1996/08/19 17:14:59  mjr
 *  misordered args, fixed bug
 *
 *  Revision 1.5  1996/08/19 17:11:41  mjr
 *  RCS got confused with the RCS-style header goodies.
 *  got it cleaned up now.
 *
 *  Revision 1.4  1996/08/19 17:01:33  mjr
 *  Removed the expanded code checking and added
 *  lots of code that duplicates what stubgen.pl did.
 *  still need options pretty badly
 *
 *  Revision 1.3  1996/08/17 23:21:10  mjr
 *  added the expanded operator code, cleaned up tabs.
 *  consider putting all of the expanded code in another
 *  grammar - this one is getting cluttered.
 *
 */
%}

%{
#include "table.h"
#include "util.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#ifdef WIN32
#include <malloc.h> /* defintion of alloca */
#include "getopt.h" /* use GNU getopt      */
#endif /* WIN32 */

#ifndef WIN32
#include <pwd.h>
#endif /* WIN32 */

/* defined in lexer.l */
extern int collectInlineDef();
extern int collectMemberInitList();

/* defined here in parser.y */
static int error_recovery();
static int yyerror(char *);
static const char rcsid[] = "$Id: parser.y,v 1.1 2002-06-06 22:09:16 ocoursiere Exp $";

/* defined in main.c */
extern FILE *outfile;
extern char *currentFile;
extern int lineno;

%}

%union {
  char *string;
  syntaxelem_t *elt;
  arg_t *arg;
  int flag;
}

%token <string> IDENTIFIER CONSTANT STRING_LITERAL
%token <string> CHAR SHORT INT LONG SIGNED UNSIGNED FLOAT DOUBLE VOID
%token NEW DELETE TEMPLATE THROW

%token PTR_OP INC_OP DEC_OP LEFT_OP RIGHT_OP LE_OP GE_OP EQ_OP NE_OP
%token AND_OP OR_OP MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN
%token SUB_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN
%token XOR_ASSIGN OR_ASSIGN CLCL MEM_PTR_OP

%token FRIEND OPERATOR CONST CLASS STRUCT UNION ENUM 
%token PROTECTED PRIVATE PUBLIC EXTERN ELIPSIS

%type <string> simple_type_name simple_signed_type non_reference_type
%type <string> scoped_identifier pointer asterisk
%type <string> type_name binary_operator
%type <string> assignment_operator unary_operator any_operator
%type <string> variable_specifier_list union_specifier
%type <string> constant_value multiple_variable_specifier
%type <string> enum_specifier enumerator_list enumerator 
%type <string> template_specifier template_arg template_arg_list
%type <string> template_instance_arg template_instance_arg_list
%type <string> throw_decl throw_list variable_name bitfield_savvy_identifier
%type <string> primary_expression expression
%type <string> multiplicative_expression additive_expression
%type <flag> const_opt ampersand_opt
%type <elt> class_specifier
%type <elt> member_func_specifier function_specifier constructor
%type <elt> destructor member_specifier member member_with_access
%type <elt> mem_type_specifier member_or_error
%type <elt> member_list member_list_opt
%type <elt> member_func_inlined type_specifier overloaded_op_specifier
%type <elt> member_func_skel member_func_skel_spec
%type <elt> constructor_skeleton destructor_skeleton
%type <elt> overloaded_op_skeleton function_skeleton
%type <arg> variable_or_parameter variable_specifier
%type <arg> parameter_list parameter_list_opt unnamed_parameter


%start translation_unit
%%

translation_unit
	: declaration
	| translation_unit declaration
	;

declaration
	: declaration_specifiers ';'
	| EXTERN STRING_LITERAL compound_statement
{ 
  log_printf("IGNORING extern \"C\" { ... } block.\n");
  free($2);
}
	| member_func_inlined
{ 
    $1->kind = INLINED_KIND; 
    log_printf("\nBEGIN matched dec : m_f_i rule --");
    print_se($1); 
    log_printf("END   matched dec : m_f_i rule\n");
}
	| function_skeleton
{ 
    $1->kind = SKEL_KIND; 
    log_printf("\nBEGIN matched dec : function_skeleton rule --");
    print_se($1); 
    enqueue_skeleton($1);
    log_printf("END   matched dec : function_skeleton rule\n");
}
	| ';'
	| error
{
  log_printf("declaration : error.  Attempting to recover...\n");
  yyerrok;
  yyclearin;
  if (error_recovery() != 0) {
      log_printf("ERROR recovery could not complete -- YYABORT.\n");
      YYABORT;
  }
  log_printf("ERROR recovery complete.\n");
}
	;

declaration_specifiers
	: member_specifier
{
    /* the name of the rule "member_specifier" might be misleading, but
     * this is either a class, struct, union, enum, global var, global
     * prototype, etc..  */
    if ($1->kind == CLASS_KIND || $1->kind == STRUCT_KIND) {
	enqueue_class($1);
    } else {
	log_printf("\nIGNORING      dec_spec : mem_spec (%s) --",
		  string_kind($1->kind));
	print_se($1);
	log_printf("END IGNORING  dec_spec : mem_spec (%s)\n",
		  string_kind($1->kind));
    }
}
	| forward_decl
	;

type_specifier
	: union_specifier
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem(strdup(""), $1, NULL, IGNORE_KIND);
/*   print_se(elem); */
  $$ = elem;
}
	| enum_specifier
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem(strdup(""), $1, NULL, IGNORE_KIND);
/*   print_se(elem); */
  $$ = elem;
}
	| class_specifier
	;

ampersand_opt
	: /* nothing */				{ $$ = 0; }
	| '&'					{ $$ = 1; }
	;

type_name
	: non_reference_type ampersand_opt
{ 
  char *tmp_str = (char *) malloc(strlen($1) + ($2 ? 2 : 0) + 1);
  strcpy(tmp_str, $1);
  if ($2)
    strcat(tmp_str, " &");
  free($1);
  $$ = tmp_str;
}
	| CONST non_reference_type ampersand_opt
{ 
  char *tmp_str = (char *) malloc(strlen($2) + ($3 ? 2 : 0) + 7);
  sprintf(tmp_str, "const %s%s", $2, ($3 ? " &" : ""));
  free($2);
  $$ = tmp_str;
}
	;

forward_decl
	: CLASS IDENTIFIER			{ free($2); }
	| CLASS scoped_identifier		{ free($2); }
	| STRUCT IDENTIFIER			{ free($2); }
	| STRUCT scoped_identifier		{ free($2); }
	;

non_reference_type
	: scoped_identifier
	| IDENTIFIER
	| STRUCT IDENTIFIER	
{
  char *tmp_str = (char *) malloc(strlen($2) + 8);
  strcpy(tmp_str, "struct ");
  strcat(tmp_str, $2);
  free($2);
  $$ = tmp_str; 
}
	| scoped_identifier '<' template_instance_arg_list '>'
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($3) + 3);
  sprintf(tmp_str, "%s<%s>", $1, $3);
  free($1);
  free($3);
  $$ = tmp_str;
}
	| IDENTIFIER '<' template_instance_arg_list '>'
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($3) + 3);
  sprintf(tmp_str, "%s<%s>", $1, $3);
  free($1);
  free($3);
  $$ = tmp_str;
}
	| scoped_identifier '<' template_instance_arg_list '>' pointer
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($3) + strlen($5) + 4);
  sprintf(tmp_str, "%s<%s> %s", $1, $3, $5);
  free($1);
  free($3);
  free($5);
  $$ = tmp_str;
}
	| IDENTIFIER '<' template_instance_arg_list '>' pointer
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($3) + strlen($5) + 4);
  sprintf(tmp_str, "%s<%s> %s", $1, $3, $5);
  free($1);
  free($3);
  free($5);
  $$ = tmp_str;
}
	| scoped_identifier pointer	
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($2) + 2);
  sprintf(tmp_str, "%s %s", $1, $2);
  free($1);
  free($2);
  $$ = tmp_str;
}
	| IDENTIFIER pointer	
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($2) + 2);
  sprintf(tmp_str, "%s %s", $1, $2);
  free($1);
  free($2);
  $$ = tmp_str;
}
	| STRUCT IDENTIFIER pointer	
{ 
  char *tmp_str = (char *) malloc(strlen($2) + strlen($3) + 9);
  sprintf(tmp_str, "struct %s %s", $2, $3);
  free($2);
  free($3);
  $$ = tmp_str;
}
	| simple_signed_type
	| simple_signed_type pointer
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($2) + 2);
  sprintf(tmp_str, "%s %s", $1, $2);
  free($1);
  free($2);
  $$ = tmp_str;
}
	;

simple_signed_type
	: simple_type_name
	| SIGNED simple_type_name	
{ 
  char *tmp_str = (char *) malloc(strlen($2) + 8);
  strcpy(tmp_str,"signed ");
  strcat(tmp_str, $2);
  free($1);
  free($2);
  $$ = tmp_str;
}
	| UNSIGNED simple_type_name
{ 
  char *tmp_str = (char *) malloc(strlen($2) + 10);
  strcpy(tmp_str,"unsigned ");
  strcat(tmp_str, $2);
  free($1);
  free($2);
  $$ = tmp_str;
}
	;

simple_type_name
	: CHAR
	| SHORT
	| SHORT INT
{ 
  $$ = strdup("short int");
  free($1);
  free($2);
}
	| INT
	| LONG
	| LONG INT
{ 
  $$ = strdup("long int");
  free($1);
  free($2);
}
	| LONG LONG
{ 
  $$ = strdup("long long");
  free($1);
  free($2);
}
	| LONG LONG INT
{ 
  $$ = strdup("long long int");
  free($1);
  free($2);
  free($3);
}
	| UNSIGNED
	| FLOAT
	| DOUBLE
	| VOID
	;

scoped_identifier
	: IDENTIFIER CLCL IDENTIFIER
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($3) + 3);
  sprintf(tmp_str, "%s::%s", $1, $3);
  free($1);
  free($3);
  $$ = tmp_str;
}
	| scoped_identifier CLCL IDENTIFIER
{ 
  /* control-Y programming! */
  char *tmp_str = (char *) malloc(strlen($1) + strlen($3) + 3);
  sprintf(tmp_str, "%s::%s", $1, $3);
  free($1);
  free($3);
  $$ = tmp_str;
}
	;

pointer
	: asterisk
	| pointer asterisk
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($2) + 1);
  strcpy(tmp_str,$1);
  strcat(tmp_str,$2);
  free($1);
  free($2);
  $$ = tmp_str;
}
	;

asterisk
	: '*'					{ $$ = strdup("*"); }
	| '*' CONST				{ $$ = strdup("*const "); }
	;

variable_or_parameter
	: type_name bitfield_savvy_identifier
{ 
  arg_t *new_arg = (arg_t *) malloc(sizeof(arg_t));
  new_arg->type = $1;
  new_arg->name = $2;
  new_arg->array = NULL;
  new_arg->next = NULL;
  $$ = new_arg;
}
	| type_name scoped_identifier
{ 
  arg_t *new_arg = (arg_t *) malloc(sizeof(arg_t));
  new_arg->type = $1;
  new_arg->name = $2;
  new_arg->array = NULL;
  new_arg->next = NULL;
  $$ = new_arg;
}
	| variable_or_parameter '[' constant_value ']'
{
  char *old_array = $1->array;
  int old_len = old_array ? strlen(old_array) : 0;
  $1->array = (char *) malloc(strlen($3) + old_len + 3);
  sprintf($1->array, "%s[%s]", old_array ? old_array : "", $3);
  free($3);
  if (old_array)
    free(old_array);
  $$ = $1;
}
	| variable_or_parameter '[' ']'
{ 
  char *old_array = $1->array;
  int old_len = old_array ? strlen(old_array) : 0;
  $1->array = (char *) malloc(old_len + 3);
  sprintf($1->array, "%s[]", old_array ? old_array : "");
  if (old_array)
    free(old_array);
  $$ = $1;
}
	;

bitfield_savvy_identifier
	: IDENTIFIER
	| IDENTIFIER ':' CONSTANT		{ free($3); $$ = $1;}
	;

variable_name
	: bitfield_savvy_identifier
	| pointer IDENTIFIER
{
  char *tmp_str = (char *) malloc(strlen($1) + strlen($2) + 2);
  sprintf(tmp_str, "%s %s", $1, $2);
  free($1);
  free($2);
  $$ = tmp_str;
}
	| variable_name '[' constant_value ']'
{
  char *tmp_str = (char *) malloc(strlen($1) + strlen($3) + 3);
  sprintf(tmp_str, "%s[%s]", $1, $3);
  free($1);
  free($3);
  $$ = tmp_str;
}
	| variable_name '[' ']'
{ 
  char *tmp_str = (char *) malloc(strlen($1) + 3);
  strcpy(tmp_str, $1);
  strcat(tmp_str, "[]");
  free($1);
  $$ = tmp_str;
}
	;

variable_specifier
	: variable_or_parameter
	| EXTERN variable_or_parameter		{ $$ = $2; }
	| variable_or_parameter '=' constant_value
{
  free($3);
  $$ = $1;
}
	;

multiple_variable_specifier
	: variable_specifier
{
  $$ = args_to_string($1, 0);
  free_args($1);
}
	| multiple_variable_specifier ',' variable_name
{
  char *tmp_str = (char *) malloc(strlen($1) + strlen($3) + 3);
  sprintf(tmp_str, "%s, %s", $1, $3);
  free($1);
  free($3);
  $$ = tmp_str;
}
	;

variable_specifier_list
	: multiple_variable_specifier ';'
{ 
  char *tmp_str = (char *) malloc(strlen($1) + 2);
  sprintf(tmp_str, "%s;", $1);
  free($1);
  $$ = tmp_str;
}
	| variable_specifier_list multiple_variable_specifier ';'
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($2) + 3);
  sprintf(tmp_str, "%s\n%s;", $1, $2);
  free($1);
  free($2);
  $$ = tmp_str;
}
	;

parameter_list_opt
	: /* nothing */
{ 
  $$ = NULL; 
}
	| parameter_list
{
  $$ = reverse_arg_list($1);
}
	| parameter_list ',' ELIPSIS
{
  arg_t *new_arg = (arg_t *) malloc(sizeof(arg_t));
  new_arg->type = strdup("...");
  new_arg->name = NULL;
  new_arg->array = NULL;
  new_arg->next = $1;
  $$ = reverse_arg_list(new_arg);
}
	| ELIPSIS
{
  arg_t *new_arg = (arg_t *) malloc(sizeof(arg_t));
  new_arg->type = strdup("...");
  new_arg->name = NULL;
  new_arg->array = NULL;
  new_arg->next = NULL;
  $$ = new_arg;
}
	;

parameter_list
	: variable_specifier			
	| unnamed_parameter			
	| parameter_list ',' variable_specifier
{
  $3->next = $1;
  $$ = $3;
}
	| parameter_list ',' unnamed_parameter
{ 
  $3->next = $1;
  $$ = $3;
}
	;

unnamed_parameter
	: type_name				
{
  arg_t *new_arg = (arg_t *) malloc(sizeof(arg_t));
  new_arg->type = $1;
  new_arg->name = NULL;
  new_arg->array = NULL;
  new_arg->next = NULL;
  $$ = new_arg;
}
	| type_name '=' constant_value
{
  arg_t *new_arg = (arg_t *) malloc(sizeof(arg_t));
  new_arg->type = $1;
  new_arg->name = NULL;
  new_arg->array = NULL;
  new_arg->next = NULL;
  free($3);
  $$ = new_arg;
}
	| unnamed_parameter '[' constant_value ']'
{
  char *old_array = $1->array;
  int old_len = old_array ? strlen(old_array) : 0;
  $1->array = (char *) malloc(strlen($3) + old_len + 3);
  sprintf($1->array, "%s[%s]", old_array ? old_array : "", $3);
  free($3);
  if (old_array)
    free(old_array);
  $$ = $1;
}
	| unnamed_parameter '[' ']'
{ 
  char *old_array = $1->array;
  int old_len = old_array ? strlen(old_array) : 0;
  $1->array = (char *) malloc(old_len + 3);
  sprintf($1->array, "%s[]", old_array ? old_array : "");
  if (old_array)
    free(old_array);
  $$ = $1;
}
	;

function_skeleton
	: member_func_skel compound_statement
	| constructor_skeleton ':'
	  { if (collectMemberInitList() != 0) YYERROR; } compound_statement
	| template_specifier member_func_skel compound_statement 
{
  /* I think this is the correct behavior, but skel_elemcmp is wrong */
  /* $2->templ = $1; */
  free($1);
  $$ = $2;
}
	| template_specifier constructor_skeleton ':'
	  { if (collectMemberInitList() != 0) YYERROR; } compound_statement
{ 
  /* I think this is the correct behavior, but skel_elemcmp is wrong */
  /* $2->templ = $1; */
  free($1);
  $$ = $2;
}
	;

member_func_inlined
	: member_func_specifier compound_statement
	| constructor ':'
	  { if (collectMemberInitList() != 0) YYERROR; } compound_statement
	;

member_func_skel
	: member_func_skel_spec const_opt throw_decl 
{ 
    $1->const_flag = $2; 
    $1->throw_decl = $3;
    $$ = $1; 
}
	| constructor_skeleton
	| destructor_skeleton
	;

member_func_specifier
	: function_specifier const_opt throw_decl 
{ 
    $1->const_flag = $2; 
    $1->throw_decl = $3;
    $$ = $1; 
}
	| constructor
	| destructor
	;


member_func_skel_spec
	: type_name scoped_identifier '(' parameter_list_opt ')'
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem($1, $2, $4, FUNC_KIND);
/*  print_se(elem); */
  $$ = elem;
}
	| type_name IDENTIFIER '<' template_instance_arg_list '>' CLCL IDENTIFIER '(' parameter_list_opt ')'
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem($1,
			      (char *) malloc(strlen($2) + strlen($4) + strlen($7) + 5),
			      $9, FUNC_KIND);
  sprintf(elem->name,"%s<%s>::%s", $2, $4, $7);
  free($2);
  free($4);
  free($7);
  $$ = elem;
}
	| overloaded_op_skeleton
	;



function_specifier
	: type_name IDENTIFIER '(' parameter_list_opt ')'
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem($1, $2, $4, FUNC_KIND);
  print_se(elem);
  $$ = elem;
}
	| overloaded_op_specifier
	;

overloaded_op_skeleton
	: type_name scoped_identifier CLCL OPERATOR any_operator '(' parameter_list_opt ')'
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem($1, (char *)malloc(strlen($2) + strlen($5) + 12),
			      $7, FUNC_KIND);
  sprintf(elem->name, "%s::operator%s", $2, $5);
  free($2);
  free($5);
/*  print_se(elem); */
  $$ = elem;
}
	| scoped_identifier CLCL OPERATOR type_name '(' ')'
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem(strdup(""), 
			      (char *)malloc(strlen($1) + strlen($4) + 13),
			      NULL, FUNC_KIND);
  sprintf(elem->name, "%s::operator %s", $1, $4);
  free($1);
  free($4);
/*  print_se(elem); */
  $$ = elem;
}
	| type_name IDENTIFIER CLCL OPERATOR any_operator '(' parameter_list_opt ')'
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem($1, (char *)malloc(strlen($2) + strlen($5) + 12),
			      $7, FUNC_KIND);
  sprintf(elem->name, "%s::operator%s", $2, $5);
  free($2);
  free($5);
/*  print_se(elem); */
  $$ = elem;
}
	| IDENTIFIER CLCL OPERATOR type_name '(' ')'
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem(strdup(""),
			      (char *)malloc(strlen($1) + strlen($4) + 13),
			      NULL, FUNC_KIND);
  sprintf(elem->name, "%s::operator %s", $1, $4);
  free($1);
  free($4);
/*  print_se(elem); */
  $$ = elem;
}
	;

overloaded_op_specifier
	: type_name OPERATOR any_operator '(' parameter_list_opt ')'
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem($1, (char *)malloc(strlen($3) + 9), 
			      $5, FUNC_KIND);
  sprintf(elem->name, "operator%s", $3);
  free($3);
  print_se(elem);
  $$ = elem;
}
	| OPERATOR type_name '(' ')'
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem(strdup(""), (char *)malloc(strlen($2) + 10),
			      NULL, FUNC_KIND);
  sprintf(elem->name, "operator %s", $2);
  free($2);
  print_se(elem);
  $$ = elem;
}
	;

const_opt
	: /* nothing */				{ $$ = 0; }
	| CONST					{ $$ = 1; }
	;

throw_decl
	: /* nothing */				{ $$ = NULL; }
	| THROW '(' throw_list ')'
{ 
  char *tmp_str = (char *) malloc(strlen($3) + 8);
  sprintf(tmp_str, "throw(%s)", $3);
  free($3);
  $$ = tmp_str;
}
	;

throw_list
	: type_name
	| throw_list ',' type_name
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($3) + 3);
  sprintf(tmp_str, "%s, %s", $1, $3);
  free($1);
  free($3);
  $$ = tmp_str;
}
	;

destructor
	: '~' IDENTIFIER '(' ')'
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem(strdup(""), (char *) malloc(strlen($2) + 2),
			      NULL, FUNC_KIND);
  sprintf(elem->name,"~%s", $2);
  free($2);
/*   print_se(elem); */
  $$ = elem;
}
	;

destructor_skeleton
	: scoped_identifier CLCL '~' IDENTIFIER '(' ')'
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem(strdup(""), 
			      (char *) malloc(strlen($1) + strlen($4) + 4),
			      NULL, FUNC_KIND);
  sprintf(elem->name,"%s::~%s", $1, $4);
  free($1);
  free($4);
/*   print_se(elem); */
  $$ = elem;
}
	| IDENTIFIER CLCL '~' IDENTIFIER '(' ')'
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem(strdup(""), 
			      (char *) malloc(strlen($1) + strlen($4) + 4),
			      NULL, FUNC_KIND);
  sprintf(elem->name,"%s::~%s", $1, $4);
  free($1);
  free($4);
/*   print_se(elem); */
  $$ = elem;
}
	| IDENTIFIER '<' template_instance_arg_list '>' CLCL '~' IDENTIFIER '(' ')'
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem(strdup(""), 
			      (char *) malloc(strlen($1) + strlen($3) + strlen($7) + 6),
			      NULL, FUNC_KIND);
  sprintf(elem->name,"%s<%s>::~%s", $1, $3, $7);
  free($1);
  free($3);
  free($7);
  $$ = elem;
}
	| scoped_identifier '<' template_instance_arg_list '>' CLCL '~' IDENTIFIER '(' ')'
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem(strdup(""), 
			      (char *) malloc(strlen($1) + strlen($3) + strlen($7) + 6),
			      NULL, FUNC_KIND);
  sprintf(elem->name,"%s<%s>::~%s", $1, $3, $7);
  free($1);
  free($3);
  free($7);
  $$ = elem;
}
	;

constructor
	: IDENTIFIER '(' parameter_list_opt ')' throw_decl
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem(strdup(""), $1, $3, FUNC_KIND);
  elem->throw_decl = $5;
/*   print_se(elem); */
  $$ = elem;
}
	;

constructor_skeleton
	: scoped_identifier '(' parameter_list_opt ')' throw_decl
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem(strdup(""), $1, $3, FUNC_KIND);
  elem->throw_decl = $5;
/*  print_se(elem); */
  $$ = elem;
}
	| IDENTIFIER '<' template_instance_arg_list '>' CLCL IDENTIFIER '(' parameter_list_opt ')' throw_decl
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem(strdup(""), 
			      (char *) malloc(strlen($1) + strlen($3) + strlen($6) + 5),
			      $8, FUNC_KIND);
  sprintf(elem->name,"%s<%s>::%s", $1, $3, $6);
  free($1);
  free($3);
  free($6);
  elem->throw_decl = $10;
  $$ = elem;
}
	| scoped_identifier '<' template_instance_arg_list '>' CLCL IDENTIFIER '(' parameter_list_opt ')' throw_decl
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem(strdup(""), 
			      (char *) malloc(strlen($1) + strlen($3) + strlen($6) + 5),
			      $8, FUNC_KIND);
  sprintf(elem->name,"%s<%s>::%s", $1, $3, $6);
  free($1);
  free($3);
  free($6);
  elem->throw_decl = $10;
  $$ = elem;
}
	;

compound_statement
	: '{' { if (collectInlineDef() != 0) YYERROR; } '}'
	;

enum_specifier
	: ENUM '{' enumerator_list '}'		
{ 
  char *tmp_str = (char *) malloc(strlen($3) + 10);
  sprintf(tmp_str, "enum { %s }", $3);
  free($3);
  $$ = tmp_str;
}
	| ENUM IDENTIFIER '{' enumerator_list '}'
{ 
  char *tmp_str = (char *) malloc(strlen($2) + strlen($4) + 11);
  sprintf(tmp_str, "enum %s { %s }", $2, $4);
  free($2);
  free($4);
  $$ = tmp_str;
}
	| ENUM IDENTIFIER
{ 
  char *tmp_str = (char *) malloc(strlen($2) + 6);
  sprintf(tmp_str, "enum %s", $2);
  free($2);
  $$ = tmp_str;
}
	;

enumerator_list
	: enumerator
	| enumerator_list ',' enumerator
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($3) + 3);
  sprintf(tmp_str, "%s, %s", $1, $3);
  free($1);
  free($3);
  $$ = tmp_str;
}
	;

enumerator
	: IDENTIFIER
	| IDENTIFIER '=' constant_value
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($3) + 2);
  sprintf(tmp_str, "%s=%s", $1, $3);
  free($1);
  free($3);
  $$ = tmp_str;
}
	;

access_specifier_opt
	: /* nothing */
	| access_specifier
	;

access_specifier_list
	: access_specifier ':'
	| access_specifier_list access_specifier ':'
	;

access_specifier
	: PUBLIC
	| PRIVATE
	| PROTECTED
	;

unary_operator
	: '&'					{ $$ = strdup("&"); }
	| '*'					{ $$ = strdup("*"); }
	| '+'					{ $$ = strdup("+"); }
	| '-'					{ $$ = strdup("-"); }
	| '~'					{ $$ = strdup("~"); }
	| '!' 					{ $$ = strdup("!"); }
	;

binary_operator
	: '/'					{ $$ = strdup("/"); }
	| '%'					{ $$ = strdup("%"); }
	| '^'					{ $$ = strdup("^"); }
	| '|'					{ $$ = strdup("|"); }
	| '<'					{ $$ = strdup("<"); }
	| '>'					{ $$ = strdup(">"); }
	| ','					{ $$ = strdup(","); }
	;

assignment_operator
	: '='					{ $$ = strdup("="); }
	| MUL_ASSIGN				{ $$ = strdup("*="); }
	| DIV_ASSIGN				{ $$ = strdup("/="); }
	| MOD_ASSIGN				{ $$ = strdup("%="); }
	| ADD_ASSIGN				{ $$ = strdup("+="); }
	| SUB_ASSIGN				{ $$ = strdup("-="); }
	| LEFT_ASSIGN				{ $$ = strdup("<<="); }
	| RIGHT_ASSIGN				{ $$ = strdup(">>="); }
	| AND_ASSIGN				{ $$ = strdup("&="); }
	| XOR_ASSIGN				{ $$ = strdup("^="); }
	| OR_ASSIGN				{ $$ = strdup("|="); }
	;

any_operator
	: assignment_operator
	| unary_operator
	| binary_operator
	| NEW					{ $$ = strdup(" new"); }
	| DELETE				{ $$ = strdup(" delete"); }
	| PTR_OP				{ $$ = strdup("->"); }
	| MEM_PTR_OP				{ $$ = strdup("->*"); }
	| INC_OP				{ $$ = strdup("++"); }
	| DEC_OP				{ $$ = strdup("--"); }
	| LEFT_OP				{ $$ = strdup("<<"); }
	| RIGHT_OP				{ $$ = strdup(">>"); }
	| LE_OP					{ $$ = strdup("<="); }
	| GE_OP					{ $$ = strdup(">="); }
	| EQ_OP					{ $$ = strdup("=="); }
	| NE_OP					{ $$ = strdup("!="); }
	| AND_OP				{ $$ = strdup("&&"); }
	| OR_OP					{ $$ = strdup("||"); }
	| '[' ']'				{ $$ = strdup("[]"); }
	| '(' ')'				{ $$ = strdup("()"); }
	;

constant_value
	: expression
	| compound_statement			{ $$ = strdup("{ ... }"); }
	;

expression
	: additive_expression
	;

primary_expression
	: CONSTANT
	| '-' CONSTANT
{ 
  char *tmp_str = (char *) malloc(strlen($2) + 2);
  sprintf(tmp_str, "-%s", $2);
  free($2);
  $$ = tmp_str; 
}
	| STRING_LITERAL
	| IDENTIFIER
	| scoped_identifier
	| '(' expression ')'
{ 
  char *tmp_str = (char *) malloc(strlen($2) + 3);
  sprintf(tmp_str, "(%s)", $2);
  free($2);
  $$ = tmp_str; 
}
	;

multiplicative_expression
	: primary_expression
	| multiplicative_expression '*' primary_expression
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($3) + 4);
  sprintf(tmp_str, "%s * %s", $1, $3);
  free($1);
  free($3);
  $$ = tmp_str; 
}
	| multiplicative_expression '/' primary_expression
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($3) + 4);
  sprintf(tmp_str, "%s / %s", $1, $3);
  free($1);
  free($3);
  $$ = tmp_str; 
}
	| multiplicative_expression '%' primary_expression
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($3) + 4);
  sprintf(tmp_str, "%s %% %s", $1, $3);
  free($1);
  free($3);
  $$ = tmp_str; 
}
	;

additive_expression
	: multiplicative_expression
	| additive_expression '+' multiplicative_expression
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($3) + 4);
  sprintf(tmp_str, "%s + %s", $1, $3);
  free($1);
  free($3);
  $$ = tmp_str; 
}
	| additive_expression '-' multiplicative_expression
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($3) + 4);
  sprintf(tmp_str, "%s - %s", $1, $3);
  free($1);
  free($3);
  $$ = tmp_str; 
}
	;


union_specifier
	: UNION IDENTIFIER '{' variable_specifier_list '}'
{ 
  char *tmp_str = (char *) malloc(strlen($2) + strlen($4) + 12);
  sprintf(tmp_str, "union %s { %s }", $2, $4);
  free($2);
  free($4);
  $$ = tmp_str;
}
	| UNION '{' variable_specifier_list '}'
{ 
  char *tmp_str = (char *) malloc(strlen($3) + 11);
  sprintf(tmp_str, "union { %s }", $3);
  free($3);
  $$ = tmp_str;
}
	| UNION IDENTIFIER
{ 
  char *tmp_str = (char *) malloc(strlen($2) + 7);
  sprintf(tmp_str, "union %s", $2);
  free($2);
  $$ = tmp_str;
}
	;

class_specifier
	: CLASS IDENTIFIER '{' member_list_opt '}'
{
  syntaxelem_t *child;
  /* ret_type, name, args, kind */
  syntaxelem_t *tmp_elem = new_elem(strdup(""), $2, NULL, CLASS_KIND);
  tmp_elem->children = reverse_list($4);
  
  for (child = tmp_elem->children; child != NULL; child = child->next)
      child->parent = tmp_elem;

/*   print_se(tmp_elem); */
  $$ = tmp_elem;
}
	| CLASS IDENTIFIER ':' superclass_list '{' member_list_opt '}'
{
  syntaxelem_t *child;
  /* ret_type, name, args, kind */
  syntaxelem_t *tmp_elem = new_elem(strdup(""), $2, NULL, CLASS_KIND);
  tmp_elem->children = reverse_list($6);
  
  for (child = tmp_elem->children; child != NULL; child = child->next)
      child->parent = tmp_elem;

/*   print_se(tmp_elem); */
  $$ = tmp_elem;
}
	| template_specifier CLASS IDENTIFIER '{' member_list_opt '}'
{
  syntaxelem_t *child;
  /* ret_type, name, args, kind */
  syntaxelem_t *tmp_elem = new_elem(strdup(""), $3, NULL, CLASS_KIND);
  tmp_elem->children = reverse_list($5);
  tmp_elem->templ = $1;
  
  for (child = tmp_elem->children; child != NULL; child = child->next)
      child->parent = tmp_elem;

/*   print_se(tmp_elem); */
  $$ = tmp_elem;
}
	| template_specifier CLASS IDENTIFIER ':' superclass_list '{' member_list_opt '}'
{
  syntaxelem_t *child;
  /* ret_type, name, args, kind */
  syntaxelem_t *tmp_elem = new_elem(strdup(""), $3, NULL, CLASS_KIND);
  tmp_elem->children = reverse_list($7);
  tmp_elem->templ = $1;
  
  for (child = tmp_elem->children; child != NULL; child = child->next)
      child->parent = tmp_elem;

/*   print_se(tmp_elem); */
  $$ = tmp_elem;
}
	| STRUCT '{' member_list_opt '}'
{
  syntaxelem_t *child;
  /* ret_type, name, args, kind */
  syntaxelem_t *tmp_elem = new_elem(strdup(""), "unnamed_struct",
				    NULL, IGNORE_KIND);
  tmp_elem->children = reverse_list($3);
  
  for (child = tmp_elem->children; child != NULL; child = child->next)
      child->parent = tmp_elem;

/*   print_se(tmp_elem); */
  $$ = tmp_elem;
}
	| STRUCT IDENTIFIER '{' member_list_opt '}'
{
  syntaxelem_t *child;
  /* ret_type, name, args, kind */
  syntaxelem_t *tmp_elem = new_elem(strdup(""), $2, NULL, STRUCT_KIND);
  tmp_elem->children = reverse_list($4);
  
  for (child = tmp_elem->children; child != NULL; child = child->next)
      child->parent = tmp_elem;

/*   print_se(tmp_elem); */
  $$ = tmp_elem;
}
	| STRUCT IDENTIFIER ':' superclass_list '{' member_list_opt '}'
{
  syntaxelem_t *child;
  /* ret_type, name, args, kind */
  syntaxelem_t *tmp_elem = new_elem(strdup(""), $2, NULL, STRUCT_KIND);
  tmp_elem->children = reverse_list($6);
  
  for (child = tmp_elem->children; child != NULL; child = child->next)
      child->parent = tmp_elem;

/*   print_se(tmp_elem); */
  $$ = tmp_elem;
}
	| template_specifier STRUCT IDENTIFIER '{' member_list_opt '}'
{
  syntaxelem_t *child;
  /* ret_type, name, args, kind */
  syntaxelem_t *tmp_elem = new_elem(strdup(""), $3, NULL, STRUCT_KIND);
  tmp_elem->children = reverse_list($5);
  tmp_elem->templ = $1;
  
  for (child = tmp_elem->children; child != NULL; child = child->next)
      child->parent = tmp_elem;

/*   print_se(tmp_elem); */
  $$ = tmp_elem;
}
	| template_specifier STRUCT IDENTIFIER ':' superclass_list '{' member_list_opt '}'
{
  syntaxelem_t *child;
  /* ret_type, name, args, kind */
  syntaxelem_t *tmp_elem = new_elem(strdup(""), $3, NULL, STRUCT_KIND);
  tmp_elem->children = reverse_list($7);
  tmp_elem->templ = $1;
  
  for (child = tmp_elem->children; child != NULL; child = child->next)
      child->parent = tmp_elem;

/*   print_se(tmp_elem); */
  $$ = tmp_elem;
}
	;

superclass_list
	: superclass
	| superclass_list ',' superclass
	;

superclass
	: access_specifier_opt type_name	{ free($2); }
	;

friend_specifier
	: FRIEND member_func_specifier		{ $2->kind = IGNORE_KIND; }
	| FRIEND forward_decl
	;

member_list_opt
	: /* nothing  */ { $$ = NULL; }
	| member_list
	;

member_list
	: member_with_access			
{
  if ($1 != NULL)
    $1->next = NULL;
  
  $$ = $1; 
}
	| member_list member_with_access	
{ 
  if ($2 != NULL) {
    $2->next = $1;
    $$ = $2; 
  } else {
    $$ = $1;
  }
}
	;

member_or_error
	: member
	| error
{
  log_printf("member_with_access : error.  Attempting to recover...\n");
  yyerrok;
  yyclearin;
  if (error_recovery() != 0) {
      log_printf("ERROR recovery could not complete -- YYABORT.\n");
      YYABORT;
  }
  log_printf("ERROR recovery complete.\n");
  $$ = NULL;
}
	;

member_with_access
	: member_or_error
	| access_specifier_list member_or_error	{ $$ = $2; }
	;

member
	: member_specifier ';'
	| friend_specifier ';'		{ $$ = NULL; }
	| member_func_inlined		{ $1->kind = INLINED_KIND; $$ = $1; }
	| ';'				{ $$ = NULL; }
	;

member_specifier
	: multiple_variable_specifier
{ 
  /* ret_type, name, args, kind */
  syntaxelem_t *elem = new_elem(strdup(""), $1, NULL, IGNORE_KIND);
/*   print_se(elem); */
  $$ = elem;
}
	| member_func_specifier
	| EXTERN member_func_specifier 		{ $$ = $2; }
	| member_func_specifier '=' CONSTANT	{ $1->kind = IGNORE_KIND; free($3); $$ = $1; }
	| mem_type_specifier
	;

mem_type_specifier
	: type_specifier
	| type_specifier IDENTIFIER		{ free($2); $$ = $1; }
	| mem_type_specifier '[' ']'
	| mem_type_specifier '[' constant_value ']'
{
  free($3);
  $$ = $1; 
}
	;

template_arg
	: CLASS IDENTIFIER
{
  char *tmp_str = (char *) malloc(strlen($2) + 7);
  sprintf(tmp_str, "class %s", $2);
  free($2);
  $$ = tmp_str;
}
	| type_name IDENTIFIER
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($2) + 2);
  sprintf(tmp_str, "%s %s", $1, $2);
  free($1);
  free($2);
  $$ = tmp_str;
}
	;

template_arg_list
	: template_arg
	| template_arg_list ',' template_arg
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($3) + 3);
  sprintf(tmp_str, "%s, %s", $1, $3);
  free($1);
  free($3);
  $$ = tmp_str;
}
	;

template_instance_arg
	: CONSTANT
	| type_name
	;

template_instance_arg_list
	: template_instance_arg
	| template_instance_arg_list ',' template_instance_arg
{ 
  char *tmp_str = (char *) malloc(strlen($1) + strlen($3) + 3);
  sprintf(tmp_str, "%s, %s", $1, $3);
  free($1);
  free($3);
  $$ = tmp_str;
}
	;

template_specifier
	: TEMPLATE '<' template_arg_list '>'
{
  char *tmp_str = (char *) malloc(strlen($3) + 12);
  sprintf(tmp_str, "template <%s>", $3);
  free($3);
  $$ = tmp_str;
}
	;

%%

static int yyerror(char *s /*UNUSED*/)
{
  if (outfile != NULL)
    fflush(outfile);

  return 0;
}

static int error_recovery()
{
  extern char linebuf[];
  extern int lineno;
  extern int column;
  extern int tokens_seen;

#ifdef SGDEBUG
  log_printf("parse error at line %d, file %s:\n%s\n%*s\n",
	     lineno, currentFile, linebuf, column, "^");
  log_flush();
#endif /* SGDEBUG */

  if (tokens_seen == 0) {
    /*
     * if we've seen no tokens but we're in an error, we must have
     * hit an EOF, either by stdin, or on a file.  Just give up
     * now instead of complaining.
     */
    return -1;

  } else {
    fprintf(stderr, "parse error at line %d, file %s:\n%s\n%*s\n",
	    lineno, currentFile, linebuf, column, "^");
  }

  linebuf[0] = '\0';

  for (;;) {
    int result = yylex();

    if (result <= 0) {
      /* fatal error: Unexpected EOF during parse error recovery */

#ifdef SGDEBUG
      log_printf("EOF in error recovery, line %d, file %s\n",
		 lineno, currentFile);
      log_flush();
#endif /* SGDEBUG */

      fprintf(stderr, "EOF in error recovery, line %d, file %s\n",
	      lineno, currentFile);

      return -1;
    }

    switch(result) {
    case IDENTIFIER:
    case CONSTANT:
    case STRING_LITERAL:
    case CHAR:
    case SHORT:
    case INT:
    case LONG:
    case SIGNED:
    case UNSIGNED:
    case FLOAT:
    case DOUBLE:
    case VOID:
      free(yylval.string);
      break;
    case (int) '{':
      if (collectInlineDef() != 0)
	return -1;
      result = yylex();
      return 0;
    case (int) ';':
      return 0;
    }
  }
}
