parse.in   [plain text]


/* YACC parser for Objective-C++ syntax.
   Copyright (C) 1988, 1989, 1993, 1994, 1995 Free Software Foundation, Inc.
   Hacked by Michael Tiemann (tiemann@cygnus.com)

This file is part of GNU CC.

GNU CC 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, or (at your option)
any later version.

GNU CC 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 GNU CC; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */


/* This grammar is based on the GNU CC grammar.  */

/* Note: Bison automatically applies a default action of "$$ = $1" for
   all derivations; this is applied before the explicit action, if one
   is given.  Keep this in mind when reading the actions.  */

%{
/* Cause the `yydebug' variable to be defined.  */
#define YYDEBUG 1

#include "config.h"

#include <stdio.h>
#include <errno.h>

#include "tree.h"
#include "input.h"
#include "flags.h"
#include "lex.h"
#include "cp-tree.h"
#include "output.h"
#ifdef OBJCPLUS
#include "objc-act.h"

/* the `decl' list operators optimization is not appropriate for Objective-C */
#define build_decl_list 	build_tree_list
#define decl_tree_cons 		tree_cons

#endif

/* Since parsers are distinct for each language, put the language string
   definition here.  (fnf) */
char *language_string = "GNU C++";

extern tree void_list_node;
extern struct obstack permanent_obstack;

#ifndef errno
extern int errno;
#endif

extern int end_of_file;
extern int current_class_depth;
extern int flag_new_for_scope;

/* FSF LOCAL dje prefix attributes */
extern tree strip_attrs		PROTO((tree));
/* END FSF LOCAL */

void yyerror ();

/* Like YYERROR but do call yyerror.  */
#define YYERROR1 { yyerror ("syntax error"); YYERROR; }

#define OP0(NODE) (TREE_OPERAND (NODE, 0))
#define OP1(NODE) (TREE_OPERAND (NODE, 1))

/* Contains the statement keyword (if/while/do) to include in an
   error message if the user supplies an empty conditional expression.  */
static char *cond_stmt_keyword;

/* Nonzero if we have an `extern "C"' acting as an extern specifier.  */
int have_extern_spec;
int used_extern_spec;

void yyhook ();

/* Cons up an empty parameter list.  */
#ifdef __GNUC__
__inline
#endif
static tree
empty_parms ()
{
  tree parms;

  if (strict_prototype)
    parms = void_list_node;
  else
    parms = NULL_TREE;
  return parms;
}
%}

%start program

%union {long itype; tree ttype; char *strtype; enum tree_code code; }

/* All identifiers that are not reserved words
   and are not declared typedefs in the current block */
%token IDENTIFIER

/* All identifiers that are declared typedefs in the current block.
   In some contexts, they are treated just like IDENTIFIER,
   but they can also serve as typespecs in declarations.  */
%token TYPENAME

/* Reserved words that specify storage class.
   yylval contains an IDENTIFIER_NODE which indicates which one.  */
%token SCSPEC

/* Reserved words that specify type.
   yylval contains an IDENTIFIER_NODE which indicates which one.  */
%token TYPESPEC

/* Reserved words that qualify type: "const" or "volatile".
   yylval contains an IDENTIFIER_NODE which indicates which one.  */
%token TYPE_QUAL

/* Character or numeric constants.
   yylval is the node for the constant.  */
%token CONSTANT

/* String constants in raw form.
   yylval is a STRING_CST node.  */
%token STRING

/* "...", used for functions with variable arglists.  */
%token ELLIPSIS

/* the reserved words */
/* SCO include files test "ASM", so use something else. */
%token SIZEOF ENUM /* STRUCT UNION */ IF ELSE WHILE DO FOR SWITCH CASE DEFAULT
%token BREAK CONTINUE RETURN GOTO ASM_KEYWORD GCC_ASM_KEYWORD TYPEOF ALIGNOF
%token SIGOF
%token ATTRIBUTE EXTENSION LABEL

/* the reserved words... C++ extensions */
%token <ttype> AGGR
%token <itype> VISSPEC
%token DELETE NEW OVERLOAD THIS OPERATOR CXX_TRUE CXX_FALSE
%token NAMESPACE TYPENAME_KEYWORD USING
%token LEFT_RIGHT TEMPLATE
%token TYPEID DYNAMIC_CAST STATIC_CAST REINTERPRET_CAST CONST_CAST
%token <itype> SCOPE

ifwin32
/* Used to specify __declspec(xxx) in Windows.  */
%token DECLSPEC DLL_EXPORT DLL_IMPORT /* THREAD NAKED */
end ifwin32

/* the Objective-C keywords */
%token INTERFACE IMPLEMENTATION END SELECTOR DEFS ENCODE
%token CLASSNAME PROTOCOL OBJECTNAME CLASS ALIAS
%token PRIVATE PUBLIC PROTECTED

/* Objective-C string constants in raw form.
   yylval is a OBJC_STRING_CST node.  */
%token OBJC_STRING

/* Define the operator tokens and their precedences.
   The value is an integer because, if used, it is the tree code
   to use in the expression made from the operator.  */

%left EMPTY			/* used to resolve s/r with epsilon */

%left error

/* Add precedence rules to solve dangling else s/r conflict */
%nonassoc IF
%nonassoc ELSE

%left IDENTIFIER TYPENAME PTYPENAME SCSPEC TYPESPEC TYPE_QUAL ENUM AGGR ELLIPSIS TYPEOF SIGOF OPERATOR NSNAME TYPENAME_KEYWORD

%left '{' ',' ';'

%nonassoc THROW
%right <code> ':'
%right <code> ASSIGN '='
%right <code> '?'
%left <code> OROR
%left <code> ANDAND
%left <code> '|'
%left <code> '^'
%left <code> '&'
%left <code> MIN_MAX
%left <code> EQCOMPARE
%left <code> ARITHCOMPARE '<' '>'
%left <code> LSHIFT RSHIFT
%left <code> '+' '-'
%left <code> '*' '/' '%'
%left <code> POINTSAT_STAR DOT_STAR
%right <code> UNARY PLUSPLUS MINUSMINUS '~'
%left HYPERUNARY
%left <ttype> PAREN_STAR_PAREN LEFT_RIGHT
%left <code> POINTSAT '.' '(' '['

%right SCOPE			/* C++ extension */
%nonassoc NEW DELETE TRY CATCH

%type <code> unop

%type <ttype> identifier IDENTIFIER TYPENAME CONSTANT expr nonnull_exprlist
%type <ttype> paren_expr_or_null nontrivial_exprlist
%type <ttype> expr_no_commas cast_expr unary_expr primary string STRING
%type <ttype> typed_declspecs reserved_declspecs boolean.literal
%type <ttype> typed_typespecs reserved_typespecquals
%type <ttype> declmods typespec typespecqual_reserved
%type <ttype> SCSPEC TYPESPEC TYPE_QUAL nonempty_type_quals maybe_type_qual
%type <itype> initdecls notype_initdecls initdcl	/* C++ modification */
%type <ttype> init initlist maybeasm maybe_init
%type <ttype> asm_operands nonnull_asm_operands asm_operand asm_clobbers
%type <ttype> maybe_attribute attributes attribute attribute_list attrib
%type <ttype> any_word

%type <ttype> compstmt implicitly_scoped_stmt

%type <ttype> declarator notype_declarator after_type_declarator
%type <ttype> direct_notype_declarator direct_after_type_declarator

%type <ttype> structsp opt.component_decl_list component_decl_list
%type <ttype> component_decl component_decl_1 components notype_components
%type <ttype> component_declarator component_declarator0
%type <ttype> notype_component_declarator notype_component_declarator0
%type <ttype> after_type_component_declarator after_type_component_declarator0
%type <ttype> enumlist enumerator
%type <ttype> type_id absdcl type_quals
%type <ttype> direct_abstract_declarator conversion_declarator
%type <ttype> new_type_id new_declarator direct_new_declarator
%type <ttype> xexpr parmlist parms parm bad_parm full_parm
%type <ttype> identifiers_or_typenames
%type <ttype> fcast_or_absdcl regcast_or_absdcl
%type <ttype> expr_or_declarator complex_notype_declarator
%type <ttype> notype_unqualified_id unqualified_id qualified_id
%type <ttype> overqualified_id notype_qualified_id any_id
%type <ttype> complex_direct_notype_declarator functional_cast
%type <ttype> named_parm complex_parmlist typed_declspecs1 parms_comma

/* C++ extensions */
%token <ttype> TYPENAME_ELLIPSIS PTYPENAME
%token <ttype> PRE_PARSED_FUNCTION_DECL EXTERN_LANG_STRING
%token <ttype> PRE_PARSED_CLASS_DECL
%type <ttype> fn.def1 /* Not really! */
%type <ttype> fn.def2 return_id
%type <itype> ctor_initializer_opt
%type <ttype> named_class_head named_class_head_sans_basetype
%type <ttype> named_complex_class_head_sans_basetype
%type <ttype> unnamed_class_head
%type <ttype> class_head base_class_list
%type <itype> base_class_access_list
%type <ttype> base_class maybe_base_class_list base_class.1
%type <ttype> exception_specification_opt ansi_raise_identifier ansi_raise_identifiers
%type <ttype> operator_name
%type <ttype> object aggr
%type <itype> new delete
/* %type <ttype> primary_no_id */
%type <ttype> nonmomentary_expr maybe_parmlist
%type <itype> initdcl0 notype_initdcl0 member_init_list
%type <ttype> template_header template_parm_list template_parm
%type <ttype> template_type_parm
%type <ttype> template_type template_arg_list template_arg
%type <ttype> template_instantiation template_type_name tmpl.2
%type <ttype> template_instantiate_once template_instantiate_some
%type <itype> fn_tmpl_end
/* %type <itype> try_for_typename */
%type <ttype> condition xcond paren_cond_or_null
%type <ttype> type_name nested_name_specifier nested_type ptr_to_mem
%type <ttype> qualified_type_name complete_type_name notype_identifier
%type <ttype> complex_type_name nested_name_specifier_1
%type <itype> nomods_initdecls nomods_initdcl0
%type <ttype> new_initializer new_placement specialization type_specifier_seq
%type <ttype> using_decl .poplevel

/* in order to recognize aggr tags as defining and thus shadowing. */
%token TYPENAME_DEFN IDENTIFIER_DEFN PTYPENAME_DEFN
%type <ttype> named_class_head_sans_basetype_defn 
%type <ttype> identifier_defn IDENTIFIER_DEFN TYPENAME_DEFN PTYPENAME_DEFN

%token NSNAME
%type <ttype> NSNAME

/* Used in lex.c for parsing pragmas.  */
%token END_OF_LINE

ifwin32
/* Extra goodies for WINNT */
%type <ttype> DECLSPEC DLL_EXPORT DLL_IMPORT /* THREAD NAKED */
%type <ttype> declspec
%type <ttype> declspec_attribute
end ifwin32

/* the Objective-C productions */
%type <ttype> ivar_decl_list ivar_decls ivar_decl ivars ivar_declarator
%type <ttype> methoddecl unaryselector keywordselector selector methodtype
%type <ttype> keyworddecl receiver objcmessageexpr messageargs 
%type <ttype> keywordexpr keywordarglist keywordarg reservedword
%type <ttype> myparms myparm optparmlist objcselectorexpr 
%type <ttype> selectorarg keywordnamelist keywordname objcencodeexpr 
%type <ttype> objc_string protocolrefs identifier_list identifier_colon
%type <ttype> objcprotocolexpr CLASSNAME OBJC_STRING OBJECTNAME
%type <ttype> objc_openbracket.expr objc_closebracket
%type <ttype> objc_return_type_mods

/* lex.c and pt.c depends on this being the last token.  Define
   any new tokens before this one!  */
%token END_OF_SAVED_INPUT

%{
/* List of types and structure classes of the current declaration.  */
static tree current_declspecs;
/* List of prefix attributes in effect.
   Prefix attributes are parsed by the reserved_declspecs and declmods
   rules.  They create a list that contains *both* declspecs and attrs.  */
/* ??? It is not clear yet that all cases where an attribute can now appear in
   a declspec list have been updated.  */
static tree prefix_attributes;

/* When defining an aggregate, this is the most recent one being defined.  */
static tree current_aggr;

/* List of Objective-C specific information */

static tree objc_interface_context;
tree objc_implementation_context;
tree objc_method_context;
tree objc_ivar_chain;
static tree objc_ivar_context;
static enum tree_code objc_inherit_code;
int objc_receiver_context = 0;
int objc_declarator_context = 0;
int objc_msg_context = 0;
static int objc_public_flag;

extern char *token_buffer;

tree current_objc_implementation_context (void)
{
  return objc_implementation_context;
}

/* Tell yyparse how to print a token's value, if yydebug is set.  */

#define YYPRINT(FILE,YYCHAR,YYLVAL) yyprint(FILE,YYCHAR,YYLVAL)
extern void yyprint ();
extern tree combine_strings		PROTO((tree));
%}

%%
program: /* empty */
	| extdefs
		{
		  /* In case there were missing closebraces,
		     get us back to the global binding level.  */
		  while (! global_bindings_p ())
		    poplevel (0, 0, 0);
                  objc_finish ();
                  finish_file ();
		}
	;

/* the reason for the strange actions in this rule
 is so that notype_initdecls when reached via datadef
 can find a valid list of type and sc specs in $0. */

extdefs:
	  { $<ttype>$ = NULL_TREE; } lang_extdef
		{ $<ttype>$ = NULL_TREE; }
	| extdefs lang_extdef
		{ $<ttype>$ = NULL_TREE; }
	;

extdefs_opt:
	  extdefs
	| /* empty */
	;

.hush_warning:
		{ have_extern_spec = 1;
		  used_extern_spec = 0;
		  $<ttype>$ = NULL_TREE; }
	;
.warning_ok:
		{ have_extern_spec = 0; }
	;

asm_keyword:
	  ASM_KEYWORD
	| GCC_ASM_KEYWORD
	;

lang_extdef:
	  { if (pending_lang_change) do_pending_lang_change(); }
	  extdef
	  { if (! toplevel_bindings_p () && ! pseudo_global_level_p())
	      pop_everything ();
	    prefix_attributes = NULL_TREE; }
	;

ifwin32
declspec:	DECLSPEC  '(' declspec_attribute ')' {$$ = $3;}
	;

declspec_attribute:
	DLL_EXPORT
	|	DLL_IMPORT
/*	|	THREAD
	|	NAKED	*/
	;
end ifwin32

extdef:
	  fndef
		{ if (pending_inlines) do_pending_inlines (); }
	| datadef
		{ if (pending_inlines) do_pending_inlines (); }
	| objcdef
	| template_def
		{ if (pending_inlines) do_pending_inlines (); }
	| overloaddef
	| asm_keyword '(' string ')' ';'
		{ if (TREE_CHAIN ($3)) $3 = combine_strings ($3);
		  assemble_asm ($3); }
	| extern_lang_string '{' extdefs_opt '}'
		{ pop_lang_context (); }
	| extern_lang_string .hush_warning fndef .warning_ok
		{ if (pending_inlines) do_pending_inlines ();
		  pop_lang_context (); }
	| extern_lang_string .hush_warning datadef .warning_ok
		{ if (pending_inlines) do_pending_inlines ();
		  pop_lang_context (); }
	| NAMESPACE identifier '{'
		{ push_namespace ($2); }
	  extdefs_opt '}'
		{ pop_namespace (); }
	| NAMESPACE '{'
		{ push_namespace (NULL_TREE); }
	  extdefs_opt '}'
		{ pop_namespace (); }
	| NAMESPACE identifier '=' any_id ';'
		{ do_namespace_alias ($2, $4); }
	| using_decl ';'
		{ do_toplevel_using_decl ($1); }
	| USING NAMESPACE any_id ';'
		{ do_using_directive ($3); }
	;

using_decl:
	  USING qualified_id
		{ $$ = $2; }
	| USING global_scope qualified_id
		{ $$ = $3; }
	| USING global_scope unqualified_id
		{ $$ = $3; }
	;

any_id:
	  unqualified_id
	| qualified_id
	| global_scope qualified_id
		{ $$ = $2; }
	| global_scope unqualified_id
		{ $$ = $2; }
	;

extern_lang_string:
	EXTERN_LANG_STRING
		{ push_lang_context ($1); }
	| extern_lang_string EXTERN_LANG_STRING
		{ if (current_lang_name != $2)
		    cp_error ("use of linkage spec `%D' is different from previous spec `%D'", $2, current_lang_name);
		  pop_lang_context (); push_lang_context ($2); }
	;

template_header:
	  TEMPLATE '<'
		{ begin_template_parm_list (); }
	  template_parm_list '>'
		{ $$ = end_template_parm_list ($4); }
	;

template_parm_list:
	  template_parm
		{ $$ = process_template_parm (NULL_TREE, $1); }
	| template_parm_list ',' template_parm
		{ $$ = process_template_parm ($1, $3); }
	;

template_type_parm:
	  aggr
		{ 
		  $$ = build_tree_list ($1, NULL_TREE);
		 ttpa:
		  if (TREE_PURPOSE ($$) == signature_type_node)
		    sorry ("signature as template type parameter");
		  else if (TREE_PURPOSE ($$) != class_type_node)
		    pedwarn ("template type parameters must use the keyword `class'");
		}
	| aggr identifier
		{ $$ = build_tree_list ($1, $2); goto ttpa; }
	| TYPENAME_KEYWORD
		{ $$ = build_tree_list (class_type_node, NULL_TREE); }
	| TYPENAME_KEYWORD identifier
		{ $$ = build_tree_list (class_type_node, $2); }
	;

template_parm:
	/* The following rules introduce a new reduce/reduce
	   conflict on the ',' and '>' input tokens: they are valid
	   prefixes for a `structsp', which means they could match a
	   nameless parameter.  See 14.6, paragraph 3.
	   By putting them before the `parm' rule, we get
	   their match before considering them nameless parameter
	   declarations.  */
	  template_type_parm
		{ $$ = build_tree_list (NULL_TREE, $$); }
	| template_type_parm '=' typespec
		{ $$ = build_tree_list ($3, $$); }
	| full_parm
	;

overloaddef:
	  OVERLOAD ov_identifiers ';'
		{ warning ("use of `overload' is an anachronism"); }
	;

ov_identifiers: IDENTIFIER
		{ declare_overloaded ($1); }
	| ov_identifiers ',' IDENTIFIER
		{ declare_overloaded ($3); }
	;
	  
template_def:
	/* Class template declarations go here; they aren't normal class
	   declarations, because we can't process the bodies yet.  */
	  template_header named_class_head_sans_basetype '{'
		{ yychar = '{'; goto template1; }
	 ';'
	| template_header named_class_head_sans_basetype_defn '{'
		{ yychar = '{'; goto template1; }
	 ';'
	| template_header named_class_head_sans_basetype ':'
		{ yychar = ':'; goto template1; }
	 ';'
	| template_header named_class_head_sans_basetype_defn ':'
		{
		  yychar = ':';
		template1:
		  if (current_aggr == signature_type_node)
		    sorry ("template type defining a signature");
		  /* Maybe pedantic warning for union?
		     How about an enum? :-)  */
		  end_template_decl ($1, $2, current_aggr, 1);
		  reinit_parse_for_template (yychar, $1, $2);
		  yychar = YYEMPTY;
		}
	  ';'
	| template_header named_class_head_sans_basetype ';'
		{
		  end_template_decl ($1, $2, current_aggr, 0);
		  /* declare $2 as template name with $1 parm list */
		}
	| template_header named_class_head_sans_basetype_defn ';'
		{
		  end_template_decl ($1, $2, current_aggr, 0);
		  /* declare $2 as template name with $1 parm list */
		}
	| template_header /* notype_initdcl0 ';' */
	  notype_declarator exception_specification_opt maybeasm maybe_attribute
	  fn_tmpl_end
		{
		  tree d;
		  int momentary;
		  int def = ($6 != ';');
		  momentary = suspend_momentary ();
		  d = start_decl ($<ttype>2, /*current_declspecs*/NULL_TREE, 0,
				  $3);
		  cplus_decl_attributes (d, $5, /*prefix_attributes*/NULL_TREE);
		  cp_finish_decl (d, NULL_TREE, $4, 0, 0);
		  end_template_decl ($1, d, 0, def);
		  if (def)
		    reinit_parse_for_template ((int) $6, $1, d);
		  resume_momentary (momentary);
		}
	| template_header typed_declspecs /*initdcl0*/
	  declarator exception_specification_opt maybeasm maybe_attribute
	  fn_tmpl_end
		{
		  tree d, specs, attrs;
		  int momentary;
		  int def = ($7 != ';');
		  split_specs_attrs ($2, &specs, &attrs);
		  momentary = suspend_momentary ();
		  d = start_decl ($<ttype>3, specs, 0, $<ttype>4);
		  cplus_decl_attributes (d, $6, attrs);
		  cp_finish_decl (d, NULL_TREE, $5, 0, 0);
		  end_template_decl ($1, d, 0, def);
		  if (def)
		    {
		      reinit_parse_for_template ((int) $7, $1, d);
		      yychar = YYEMPTY;
		    }
		  note_list_got_semicolon ($<ttype>2);
		  resume_momentary (momentary);
		}
	| template_header declmods notype_declarator fn_tmpl_end
		{
		  tree d, specs, attrs;
		  int def = ($4 != ';');
		  split_specs_attrs ($2, &specs, &attrs);
		  d = start_decl ($<ttype>3, specs, 0, NULL_TREE);
		  cplus_decl_attributes (d, NULL_TREE, attrs);
		  cp_finish_decl (d, NULL_TREE, NULL_TREE, 0, 0);
		  end_template_decl ($1, d, 0, def);
		  if (def)
		    reinit_parse_for_template ((int) $4, $1, d);
		}
	/* Try to recover from syntax errors in templates.  */
	| template_header error '}'	{ end_template_decl ($1, 0, 0, 0); }
	| template_header error ';'	{ end_template_decl ($1, 0, 0, 0); }
	;

fn_tmpl_end: '{'		{ $$ = '{'; }
	| ':'			{ $$ = ':'; }
	| ';'			{ $$ = ';'; }
	| '='			{ $$ = '='; }
	| RETURN		{ $$ = RETURN; }
	;

datadef:
	  nomods_initdecls ';'
		{}
	| declmods notype_initdecls ';'
		{}
	/* Normal case to make fast: "const i;".  */
	| declmods notype_declarator ';'
		{ tree d, specs, attrs;
		  split_specs_attrs ($1, &specs, &attrs);
		  d = start_decl ($<ttype>2, specs, 0, NULL_TREE);
		  cplus_decl_attributes (d, NULL_TREE, attrs);
		  cp_finish_decl (d, NULL_TREE, NULL_TREE, 0, 0);
		}
	| typed_declspecs initdecls ';'
		{
		  note_list_got_semicolon ($<ttype>$);
		}
	/* Normal case: make this fast.  */
	| typed_declspecs declarator ';'
		{ tree d, specs, attrs;
		  split_specs_attrs ($1, &specs, &attrs);
		  d = start_decl ($<ttype>2, specs, 0, NULL_TREE);
		  cplus_decl_attributes (d, NULL_TREE, attrs);
		  cp_finish_decl (d, NULL_TREE, NULL_TREE, 0, 0);
		  note_list_got_semicolon ($<ttype>$);
		}
        | declmods ';'
	  { pedwarn ("empty declaration"); }
	| explicit_instantiation ';'
	| typed_declspecs ';'
	  {
	    tree t, attrs;
	    split_specs_attrs ($1, &t, &attrs);
	    shadow_tag (t);
	    if (TREE_CODE (t) == TREE_LIST
		&& TREE_PURPOSE (t) == NULL_TREE)
	      {
		t = TREE_VALUE (t);
		if (IS_AGGR_TYPE (t)
		    && IDENTIFIER_TEMPLATE (TYPE_IDENTIFIER (t)))
		  {
		    if (CLASSTYPE_USE_TEMPLATE (t) == 0)
		      SET_CLASSTYPE_TEMPLATE_SPECIALIZATION (t);
		    else if (CLASSTYPE_TEMPLATE_INSTANTIATION (t))
		      error ("override declaration for already-expanded template");
		  }
	      }
	    note_list_got_semicolon ($<ttype>$);
	  }
	| error ';'
	| error '}'
	| ';'
	;

ctor_initializer_opt:
	  nodecls
		{ $$ = 0; }
	| base_init
		{ $$ = 1; }
	;

maybe_return_init:
	  /* empty */
	| return_init
	| return_init ';'
	;

eat_saved_input:
	  /* empty */
	| END_OF_SAVED_INPUT
	;

fndef:
	  fn.def1 maybe_return_init ctor_initializer_opt compstmt_or_error
		{
		  finish_function (lineno, (int)$3, 0);
		  if ($<ttype>$) process_next_inline ($<ttype>$);
		}
	| fn.def1 maybe_return_init function_try_block
		{
		  if ($<ttype>$) process_next_inline ($<ttype>$);
		}
	  eat_saved_input
	| typed_declspecs declarator error
		{}
	| declmods notype_declarator error
		{}
	| notype_declarator error
		{}
	;

fn.def1:
	  typed_declspecs declarator exception_specification_opt
		{ tree specs, attrs;
		  split_specs_attrs ($1, &specs, &attrs);
		  if (! start_function (specs, $2, $3, attrs, 0))
		    YYERROR1;
		  reinit_parse_for_function ();
		  $$ = NULL_TREE; }
	| declmods notype_declarator exception_specification_opt
		{ tree specs = strip_attrs ($1);
		  if (! start_function (specs, $2, $3, NULL_TREE, 0))
		    YYERROR1;
		  reinit_parse_for_function ();
		  $$ = NULL_TREE; }
	| notype_declarator exception_specification_opt
		{ if (! start_function (NULL_TREE, $$, $2, NULL_TREE, 0))
		    YYERROR1;
		  reinit_parse_for_function ();
		  $$ = NULL_TREE; }
	| PRE_PARSED_FUNCTION_DECL
		{ start_function (NULL_TREE, TREE_VALUE ($$),
				  NULL_TREE, NULL_TREE, 1);
		  reinit_parse_for_function (); }
	;

/* more C++ complexity.  See component_decl for a comment on the
   reduce/reduce conflict introduced by these rules.  */
fn.def2:
	  typed_declspecs '(' parmlist ')' type_quals exception_specification_opt
		{ tree specs = strip_attrs ($1);
		  $$ = build_parse_node (CALL_EXPR, TREE_VALUE (specs), $3, $5);
		  $$ = start_method (TREE_CHAIN (specs), $$, $6);
		 rest_of_mdef:
		  if (! $$)
		    YYERROR1;
		  if (yychar == YYEMPTY)
		    yychar = YYLEX;
		  reinit_parse_for_method (yychar, $$); }
	| typed_declspecs LEFT_RIGHT type_quals exception_specification_opt
		{ tree specs = strip_attrs ($1);
		  $$ = build_parse_node (CALL_EXPR, TREE_VALUE (specs),
					 empty_parms (), $3);
		  $$ = start_method (TREE_CHAIN (specs), $$, $4);
		  goto rest_of_mdef;
		}
	| typed_declspecs declarator exception_specification_opt
		{ tree specs = strip_attrs ($1);
		  $$ = start_method (specs, $2, $3); goto rest_of_mdef; }
	| declmods notype_declarator exception_specification_opt
		{ tree specs = strip_attrs ($1);
		  $$ = start_method (specs, $2, $3); goto rest_of_mdef; }
	| notype_declarator exception_specification_opt
		{ $$ = start_method (NULL_TREE, $$, $2); goto rest_of_mdef; }
	;

return_id: RETURN IDENTIFIER
		{
		  if (! current_function_parms_stored)
		    store_parm_decls ();
		  $$ = $2;
		}
	;

return_init: return_id maybe_init
		{ store_return_init ($<ttype>$, $2); }
	| return_id '(' nonnull_exprlist ')'
		{ store_return_init ($<ttype>$, $3); }
	| return_id LEFT_RIGHT
		{ store_return_init ($<ttype>$, NULL_TREE); }
	;

base_init:
	  ':' .set_base_init member_init_list
		{
		  if ($3 == 0)
		    error ("no base initializers given following ':'");
		  setup_vtbl_ptr ();
		  /* Always keep the BLOCK node associated with the outermost
		     pair of curley braces of a function.  These are needed
		     for correct operation of dwarfout.c.  */
		  keep_next_level ();
		}
	;

.set_base_init:
	/* empty */
		{
		  if (! current_function_parms_stored)
		    store_parm_decls ();

		  if (DECL_CONSTRUCTOR_P (current_function_decl))
		    {
		      /* Make a contour for the initializer list.  */
		      pushlevel (0);
		      clear_last_expr ();
		      expand_start_bindings (0);
		    }
		  else if (current_class_type == NULL_TREE)
		    error ("base initializers not allowed for non-member functions");
		  else if (! DECL_CONSTRUCTOR_P (current_function_decl))
		    error ("only constructors take base initializers");
		}
	;

member_init_list:
	  /* empty */
		{ $$ = 0; }
	| member_init
		{ $$ = 1; }
	| member_init_list ',' member_init
	| member_init_list error
	;

member_init: '(' nonnull_exprlist ')'
		{
		  if (current_class_name && !flag_traditional)
		    pedwarn ("anachronistic old style base class initializer");
		  expand_member_init (C_C_D, NULL_TREE, $2);
		}
	| LEFT_RIGHT
		{
		  if (current_class_name && !flag_traditional)
		    pedwarn ("anachronistic old style base class initializer");
		  expand_member_init (C_C_D, NULL_TREE, void_type_node);
		}
	| notype_identifier '(' nonnull_exprlist ')'
		{ expand_member_init (C_C_D, $<ttype>$, $3); }
	| notype_identifier LEFT_RIGHT
		{ expand_member_init (C_C_D, $<ttype>$, void_type_node); }
	| complete_type_name '(' nonnull_exprlist ')'
		{ expand_member_init (C_C_D, $<ttype>$, $3); }
	| complete_type_name LEFT_RIGHT
		{ expand_member_init (C_C_D, $<ttype>$, void_type_node); }
	/* GNU extension */
	| notype_qualified_id '(' nonnull_exprlist ')'
		{
		  do_member_init (OP0 ($1), OP1 ($1), $3);
		}
	| notype_qualified_id LEFT_RIGHT
		{
		  do_member_init (OP0 ($1), OP1 ($1), void_type_node);
		}
	;

identifier:
	  IDENTIFIER
	| TYPENAME
	| PTYPENAME
	| NSNAME
 	| OBJECTNAME
 	| CLASSNAME
	;

notype_identifier:
	  IDENTIFIER
	| PTYPENAME 
	| NSNAME %prec EMPTY
	;

identifier_defn:
	  IDENTIFIER_DEFN
	| TYPENAME_DEFN
	| PTYPENAME_DEFN
	;

explicit_instantiation:
	  TEMPLATE specialization template_instantiation
		{ do_type_instantiation ($3 ? $3 : $2, NULL_TREE); }
	| TEMPLATE typed_declspecs declarator
		{ tree specs = strip_attrs ($2);
		  do_function_instantiation (specs, $3, NULL_TREE); }
	| TEMPLATE notype_declarator
		{ do_function_instantiation (NULL_TREE, $2, NULL_TREE); }
	| SCSPEC TEMPLATE specialization template_instantiation
		{ do_type_instantiation ($4 ? $4 : $3, $1); }
	| SCSPEC TEMPLATE typed_declspecs declarator
		{ tree specs = strip_attrs ($3);
		  do_function_instantiation (specs, $4, $1); }
	| SCSPEC TEMPLATE notype_declarator
		{ do_function_instantiation (NULL_TREE, $3, $1); }
	;

template_type:
	  template_type_name tmpl.2 template_instantiation
		{ if ($3) $$ = $3; }
	;

template_type_name:
	  PTYPENAME '<' template_arg_list '>'
		{ $$ = lookup_template_class ($$, $3, NULL_TREE); }
	| PTYPENAME '<' '>'
		{ $$ = lookup_template_class ($$, NULL_TREE, NULL_TREE); }
	| TYPENAME  '<' template_arg_list '>'
		{ $$ = lookup_template_class ($$, $3, NULL_TREE); }
	;

tmpl.2: 
	  /* empty */ %prec EMPTY
		{ $$ = instantiate_class_template ($<ttype>0, 1); }
	;

template_arg_list:
	  template_arg
		{ $$ = build_tree_list (NULL_TREE, $$); }
	| template_arg_list ',' template_arg
		{ $$ = chainon ($$, build_tree_list (NULL_TREE, $3)); }
	;

template_arg:
	  type_id
		{ $$ = groktypename ($$); }
	| expr_no_commas  %prec UNARY
	;

template_instantiate_once:
	  PRE_PARSED_CLASS_DECL maybe_base_class_list
		{
		  tree t, decl, tmpl;

		  tmpl = TREE_PURPOSE (IDENTIFIER_TEMPLATE ($1));
		  t = xref_tag (DECL_TEMPLATE_INFO (tmpl)->aggr, $1, $2, 0);
		  set_current_level_tags_transparency (1);
		  my_friendly_assert (TREE_CODE (t) == RECORD_TYPE
				      || TREE_CODE (t) == UNION_TYPE, 257);
		  $<ttype>$ = t;

		  /* Now, put a copy of the decl in global scope, to avoid
		     recursive expansion.  */
		  decl = IDENTIFIER_LOCAL_VALUE ($1);
		  if (!decl)
		    decl = IDENTIFIER_CLASS_VALUE ($1);
		  /* Now, put a copy of the decl in global scope, to avoid
		     recursive expansion.  */
                  if (decl)
                    {
		      /* Need to copy it to clear the chain pointer,
			 and need to get it into permanent storage.  */
                      my_friendly_assert (TREE_CODE (decl) == TYPE_DECL, 258);
		      push_obstacks (&permanent_obstack, &permanent_obstack);
                      decl = copy_node (decl);
		      if (DECL_LANG_SPECIFIC (decl))
			copy_lang_decl (decl);
		      pop_obstacks ();
		      pushdecl_top_level (decl);
		    }
		  /* Kludge; see instantiate_class_template.  */
		  TYPE_BEING_DEFINED (t) = 0;
		}
	  left_curly opt.component_decl_list '}'
		{
		  tree t = finish_struct ($<ttype>3, $5, 0);

		  pop_obstacks ();
		  end_template_instantiation ($1);

		  repo_template_used (t);

                  /* Now go after the methods & class data.  */
                  instantiate_member_templates ($1);

		  pop_tinst_level();

		  CLASSTYPE_GOT_SEMICOLON (t) = 1;
		}
	;

template_instantiation:
          /* empty */
                { $$ = NULL_TREE; }
        | template_instantiate_once
                { $$ = $1; }
        ;

template_instantiate_some:
          /* empty */
                { $$ = NULL_TREE; /* never used from here... */}
        | template_instantiate_once template_instantiate_some
                { $$ = $1; /*???*/ }
        ;

unop:     '-'
		{ $$ = NEGATE_EXPR; }
	| '+'
		{ $$ = CONVERT_EXPR; }
	| PLUSPLUS
		{ $$ = PREINCREMENT_EXPR; }
	| MINUSMINUS
		{ $$ = PREDECREMENT_EXPR; }
	| '!'
		{ $$ = TRUTH_NOT_EXPR; }
	;

expr:	  nontrivial_exprlist
		{ $$ = build_x_compound_expr ($$); }
	| expr_no_commas
	;

paren_expr_or_null:
	LEFT_RIGHT
		{ error ("ANSI C++ forbids an empty condition for `%s'",
			 cond_stmt_keyword);
		  $$ = integer_zero_node; }
	| '(' expr ')'
		{ $$ = condition_conversion ($2); }
	;

paren_cond_or_null:
	LEFT_RIGHT
		{ error ("ANSI C++ forbids an empty condition for `%s'",
			 cond_stmt_keyword);
		  $$ = integer_zero_node; }
	| '(' condition ')'
		{ $$ = condition_conversion ($2); }
	;

xcond:
	/* empty */
		{ $$ = NULL_TREE; }
	| condition
		{ $$ = condition_conversion ($$); }
	| error
		{ $$ = NULL_TREE; }
	;

condition:
	type_specifier_seq declarator exception_specification_opt maybeasm maybe_attribute '='
		{ {
		  tree d;
		  for (d = getdecls (); d; d = TREE_CHAIN (d))
		    if (TREE_CODE (d) == TYPE_DECL) {
		      tree s = TREE_TYPE (d);
		      if (TREE_CODE (s) == RECORD_TYPE)
			cp_error ("definition of class `%T' in condition", s);
		      else if (TREE_CODE (s) == ENUMERAL_TYPE)
			cp_error ("definition of enum `%T' in condition", s);
		    }
		  }
		  current_declspecs = $1;
		  $<itype>6 = suspend_momentary ();
		  $<ttype>$ = start_decl ($<ttype>2, current_declspecs, 1, $3);
		  cplus_decl_attributes ($<ttype>$, $5,
					 /*prefix_attributes*/ NULL_TREE);
		}
	init
		{ 
		  cp_finish_decl ($<ttype>7, $8, $5, 0, LOOKUP_ONLYCONVERTING);
		  resume_momentary ($<itype>6);
		  $$ = $<ttype>7; 
		  if (TREE_CODE (TREE_TYPE ($$)) == ARRAY_TYPE)
		    cp_error ("definition of array `%#D' in condition", $$); 
		}
	| expr
	;

compstmtend:
	  '}'
	| maybe_label_decls stmts '}'
	| maybe_label_decls stmts error '}'
	| maybe_label_decls error '}'
	;

already_scoped_stmt:
	  '{' compstmtend
		{ finish_stmt (); }
	| simple_stmt
	;


nontrivial_exprlist:
	  expr_no_commas ',' expr_no_commas
		{ $$ = tree_cons (NULL_TREE, $$, 
		                  build_tree_list (NULL_TREE, $3)); }
	| expr_no_commas ',' error
		{ $$ = tree_cons (NULL_TREE, $$, 
		                  build_tree_list (NULL_TREE, error_mark_node)); }
	| nontrivial_exprlist ',' expr_no_commas
		{ chainon ($$, build_tree_list (NULL_TREE, $3)); }
	| nontrivial_exprlist ',' error
		{ chainon ($$, build_tree_list (NULL_TREE, error_mark_node)); }
	;

nonnull_exprlist:
	  expr_no_commas
		{ $$ = build_tree_list (NULL_TREE, $$); }
	| nontrivial_exprlist
	;

unary_expr:
	  primary %prec UNARY
		{
#if 0
		  if (TREE_CODE ($$) == TYPE_EXPR)
		    $$ = build_component_type_expr (C_C_D, $$, NULL_TREE, 1);
#endif
		}
	/* __extension__ turns off -pedantic for following primary.  */
	| EXTENSION
		{ $<itype>1 = pedantic;
		  pedantic = 0; }
	  cast_expr	  %prec UNARY
		{ $$ = $3;
		  pedantic = $<itype>1; }
	| '*' cast_expr   %prec UNARY
		{ $$ = build_x_indirect_ref ($2, "unary *"); }
	| '&' cast_expr   %prec UNARY
		{ $$ = build_x_unary_op (ADDR_EXPR, $2); }
	| '~' cast_expr
		{ $$ = build_x_unary_op (BIT_NOT_EXPR, $2); }
	| unop cast_expr  %prec UNARY
		{ $$ = build_x_unary_op ($1, $2);
		  if ($1 == NEGATE_EXPR && TREE_CODE ($2) == INTEGER_CST)
		    TREE_NEGATED_INT ($$) = 1;
		  overflow_warning ($$);
		}
	/* Refer to the address of a label as a pointer.  */
	| ANDAND identifier
		{ tree label = lookup_label ($2);
		  if (label == NULL_TREE)
		    $$ = null_pointer_node;
		  else
		    {
		      TREE_USED (label) = 1;
		      $$ = build1 (ADDR_EXPR, ptr_type_node, label);
		      TREE_CONSTANT ($$) = 1;
		    }
		}
	| SIZEOF unary_expr  %prec UNARY
		{ if (TREE_CODE ($2) == COMPONENT_REF
		      && DECL_BIT_FIELD (TREE_OPERAND ($2, 1)))
		    error ("sizeof applied to a bit-field");
		  /* ANSI says arrays and functions are converted inside comma.
		     But we can't really convert them in build_compound_expr
		     because that would break commas in lvalues.
		     So do the conversion here if operand was a comma.  */
		  if (TREE_CODE ($2) == COMPOUND_EXPR
		      && (TREE_CODE (TREE_TYPE ($2)) == ARRAY_TYPE
			  || TREE_CODE (TREE_TYPE ($2)) == FUNCTION_TYPE))
		    $2 = default_conversion ($2);
		  else if (TREE_CODE ($2) == TREE_LIST)
	            {
		      tree t = TREE_VALUE ($2);
		      if (t != NULL_TREE
			  && ((TREE_TYPE (t)
			      && TREE_CODE (TREE_TYPE (t)) == FUNCTION_TYPE)
			      || is_overloaded_fn (t)))
			pedwarn ("ANSI C++ forbids taking the sizeof a function type");
		    }
		  $$ = c_sizeof (TREE_TYPE ($2)); }
	| SIZEOF '(' type_id ')'  %prec HYPERUNARY
		{ $$ = c_sizeof (groktypename ($3)); }
	| ALIGNOF unary_expr  %prec UNARY
		{ $$ = grok_alignof ($2); }
	| ALIGNOF '(' type_id ')'  %prec HYPERUNARY
		{ $$ = c_alignof (groktypename ($3)); }

	/* The %prec EMPTY's here are required by the = init initializer
	   syntax extension; see below.  */
	| new new_type_id %prec EMPTY
		{ $$ = build_new (NULL_TREE, $2, NULL_TREE, $1); }
	| new new_type_id new_initializer
		{ $$ = build_new (NULL_TREE, $2, $3, $1); }
	| new new_placement new_type_id %prec EMPTY
		{ $$ = build_new ($2, $3, NULL_TREE, $1); }
	| new new_placement new_type_id new_initializer
		{ $$ = build_new ($2, $3, $4, $1); }
	| new '(' type_id ')' %prec EMPTY
		{ $$ = build_new (NULL_TREE, groktypename($3),
				  NULL_TREE, $1); }
	| new '(' type_id ')' new_initializer
		{ $$ = build_new (NULL_TREE, groktypename($3), $5, $1); }
	| new new_placement '(' type_id ')' %prec EMPTY
		{ $$ = build_new ($2, groktypename($4), NULL_TREE, $1); }
	| new new_placement '(' type_id ')' new_initializer
		{ $$ = build_new ($2, groktypename($4), $6, $1); }

	| delete cast_expr  %prec UNARY
		{ $$ = delete_sanity ($2, NULL_TREE, 0, $1); }
	| delete '[' ']' cast_expr  %prec UNARY
		{ $$ = delete_sanity ($4, NULL_TREE, 1, $1);
		  if (yychar == YYEMPTY)
		    yychar = YYLEX; }
	| delete '[' expr ']' cast_expr %prec UNARY
		{ $$ = delete_sanity ($5, $3, 2, $1);
		  if (yychar == YYEMPTY)
		    yychar = YYLEX; }
	| delete objc_openbracket.expr objc_closebracket cast_expr %prec UNARY
		{ $$ = delete_sanity ($4, $2, 2, $1);
		  if (yychar == YYEMPTY)
		    yychar = YYLEX; }
	;

new_placement:
	  '(' nonnull_exprlist ')'
		{ $$ = $2; }
	| '{' nonnull_exprlist '}'
		{
		  $$ = $2; 
		  pedwarn ("old style placement syntax, use () instead");
		}
	;

new_initializer:
	  '(' nonnull_exprlist ')'
		{ $$ = $2; }
	| LEFT_RIGHT
		{ $$ = NULL_TREE; }
	| '(' typespec ')'
		{
		  cp_error ("`%T' is not a valid expression", $2);
		  $$ = error_mark_node;
		}
	/* GNU extension so people can use initializer lists.  Note that
	   this alters the meaning of `new int = 1', which was previously
	   syntactically valid but semantically invalid.  */
	| '=' init
		{
		  if (pedantic)
		    pedwarn ("ANSI C++ forbids initialization of new expression with `='");
		  $$ = $2;
		}
	;

/* This is necessary to postpone reduction of `int ((int)(int)(int))'.  */
regcast_or_absdcl:
	  '(' type_id ')' %prec EMPTY
		{ $2 = tree_cons (NULL_TREE, $2, void_list_node);
		  TREE_PARMLIST ($2) = 1;
		  $$ = build_parse_node (CALL_EXPR, NULL_TREE, $2, 
					 NULL_TREE); }
	| regcast_or_absdcl '(' type_id ')' %prec EMPTY
		{ $3 = tree_cons (NULL_TREE, $3, void_list_node);
		  TREE_PARMLIST ($3) = 1;
		  $$ = build_parse_node (CALL_EXPR, $$, $3, NULL_TREE); }
	;

cast_expr:
	  unary_expr
	| regcast_or_absdcl unary_expr  %prec UNARY
		{ $$ = reparse_absdcl_as_casts ($$, $2); }
	| regcast_or_absdcl '{' initlist maybecomma '}'  %prec UNARY
		{ 
		  tree init = build_nt (CONSTRUCTOR, NULL_TREE,
					nreverse ($3)); 
		  if (pedantic)
		    pedwarn ("ANSI C++ forbids constructor-expressions");
		  /* Indicate that this was a GNU C constructor expression.  */
		  TREE_HAS_CONSTRUCTOR (init) = 1;

		  $$ = reparse_absdcl_as_casts ($$, init);
		}
	;

expr_no_commas:
	  cast_expr
	/* Handle general members.  */
	| expr_no_commas POINTSAT_STAR expr_no_commas
		{ $$ = build_x_binary_op (MEMBER_REF, $$, $3); }
	| expr_no_commas DOT_STAR expr_no_commas
		{ $$ = build_m_component_ref ($$, $3); }
	| expr_no_commas '+' expr_no_commas
		{ $$ = build_x_binary_op ($2, $$, $3); }
	| expr_no_commas '-' expr_no_commas
		{ $$ = build_x_binary_op ($2, $$, $3); }
	| expr_no_commas '*' expr_no_commas
		{ $$ = build_x_binary_op ($2, $$, $3); }
	| expr_no_commas '/' expr_no_commas
		{ $$ = build_x_binary_op ($2, $$, $3); }
	| expr_no_commas '%' expr_no_commas
		{ $$ = build_x_binary_op ($2, $$, $3); }
	| expr_no_commas LSHIFT expr_no_commas
		{ $$ = build_x_binary_op ($2, $$, $3); }
	| expr_no_commas RSHIFT expr_no_commas
		{ $$ = build_x_binary_op ($2, $$, $3); }
	| expr_no_commas ARITHCOMPARE expr_no_commas
		{ $$ = build_x_binary_op ($2, $$, $3); }
	| expr_no_commas '<' expr_no_commas
		{ $$ = build_x_binary_op (LT_EXPR, $$, $3); }
	| expr_no_commas '>' expr_no_commas
		{ $$ = build_x_binary_op (GT_EXPR, $$, $3); }
	| expr_no_commas EQCOMPARE expr_no_commas
		{ $$ = build_x_binary_op ($2, $$, $3); }
	| expr_no_commas MIN_MAX expr_no_commas
		{ $$ = build_x_binary_op ($2, $$, $3); }
	| expr_no_commas '&' expr_no_commas
		{ $$ = build_x_binary_op ($2, $$, $3); }
	| expr_no_commas '|' expr_no_commas
		{ $$ = build_x_binary_op ($2, $$, $3); }
	| expr_no_commas '^' expr_no_commas
		{ $$ = build_x_binary_op ($2, $$, $3); }
	| expr_no_commas ANDAND expr_no_commas
		{ $$ = build_x_binary_op (TRUTH_ANDIF_EXPR, $$, $3); }
	| expr_no_commas OROR expr_no_commas
		{ $$ = build_x_binary_op (TRUTH_ORIF_EXPR, $$, $3); }
	| expr_no_commas '?' xexpr ':' expr_no_commas
		{ $$ = build_x_conditional_expr ($$, $3, $5); }
	| expr_no_commas '=' expr_no_commas
		{ $$ = build_modify_expr ($$, NOP_EXPR, $3);
                  C_SET_EXP_ORIGINAL_CODE ($$, MODIFY_EXPR); }
	| expr_no_commas ASSIGN expr_no_commas
		{ register tree rval;
		  if ((rval = build_opfncall (MODIFY_EXPR, LOOKUP_NORMAL, $$, $3,
					     make_node ($2))))
		    $$ = rval;
		  else
		    $$ = build_modify_expr ($$, $2, $3); }
	| THROW
		{ $$ = build_throw (NULL_TREE); }
	| THROW expr_no_commas
		{ $$ = build_throw ($2); }
/* These extensions are not defined.  The second arg to build_m_component_ref
   is old, build_m_component_ref now does an implicit
   build_indirect_ref (x, NULL_PTR) on the second argument.
	| object '&' expr_no_commas   %prec UNARY
		{ $$ = build_m_component_ref ($$, build_x_unary_op (ADDR_EXPR, $3)); }
	| object unop expr_no_commas  %prec UNARY
		{ $$ = build_m_component_ref ($$, build_x_unary_op ($2, $3)); }
	| object '(' type_id ')' expr_no_commas  %prec UNARY
		{ tree type = groktypename ($3);
		  $$ = build_m_component_ref ($$, build_c_cast (type, $5, 0)); }
	| object primary_no_id  %prec UNARY
		{ $$ = build_m_component_ref ($$, $2); }
*/
	;

notype_unqualified_id:
	  '~' see_typename identifier
		{ $$ = build_parse_node (BIT_NOT_EXPR, $3); }
	| operator_name
	| IDENTIFIER
	| PTYPENAME
	| NSNAME %prec EMPTY
	;

unqualified_id:
	  notype_unqualified_id
	| TYPENAME
	;

expr_or_declarator:
	  notype_unqualified_id
	| '*' expr_or_declarator %prec UNARY
		{ $$ = build_parse_node (INDIRECT_REF, $2); }
	| '&' expr_or_declarator %prec UNARY
		{ $$ = build_parse_node (ADDR_EXPR, $2); }
	| '(' expr_or_declarator ')'
		{ $$ = $2; }
	;

direct_notype_declarator:
	  complex_direct_notype_declarator
	| notype_unqualified_id
	| '(' expr_or_declarator ')'
		{ $$ = finish_decl_parsing ($2); }
	;

primary:
	  notype_unqualified_id
		{
		  if (TREE_CODE ($$) == BIT_NOT_EXPR)
		    $$ = build_x_unary_op (BIT_NOT_EXPR, TREE_OPERAND ($$, 0));
		  else if (IDENTIFIER_OPNAME_P ($$))
		    {
		      tree op = $$;
		      $$ = lookup_name (op, 0);
		      if ($$ == NULL_TREE)
			{
			  if (op != ansi_opname[ERROR_MARK])
			    error ("operator %s not defined",
				   operator_name_string (op));
			  $$ = error_mark_node;
			}
		    }
		  else
		    $$ = do_identifier ($$);
		}		
	| CONSTANT
	| boolean.literal
	| string
		{ $$ = combine_strings ($$); }
	| '(' expr ')'
		{ char class;
		  $$ = $2;
		  class = TREE_CODE_CLASS (TREE_CODE ($$));
		  if (class == 'e' || class == '1'
		      || class == '2' || class == '<')
                    /* This inhibits warnings in truthvalue_conversion. */
		    C_SET_EXP_ORIGINAL_CODE ($$, ERROR_MARK); }
	| '(' expr_or_declarator ')'
		{ char class;
		  $$ = reparse_decl_as_expr (NULL_TREE, $2);
		  class = TREE_CODE_CLASS (TREE_CODE ($$));
		  if (class == 'e' || class == '1'
		      || class == '2' || class == '<')
                    /* This inhibits warnings in truthvalue_conversion. */
		    C_SET_EXP_ORIGINAL_CODE ($$, ERROR_MARK); }
	| '(' error ')'
		{ $$ = error_mark_node; }
	| '('
		{ if (current_function_decl == 0)
		    {
		      error ("braced-group within expression allowed only inside a function");
		      YYERROR;
		    }
		  keep_next_level ();
		  $<ttype>$ = expand_start_stmt_expr (); }
	  compstmt ')'
		{ tree rtl_exp;
		  if (pedantic)
		    pedwarn ("ANSI C++ forbids braced-groups within expressions");
		  rtl_exp = expand_end_stmt_expr ($<ttype>2);
		  /* The statements have side effects, so the group does.  */
		  TREE_SIDE_EFFECTS (rtl_exp) = 1;

		  if (TREE_CODE ($3) == BLOCK)
		    {
		      /* Make a BIND_EXPR for the BLOCK already made.  */
		      $$ = build (BIND_EXPR, TREE_TYPE (rtl_exp),
				  NULL_TREE, rtl_exp, $3);
		      /* Remove the block from the tree at this point.
			 It gets put back at the proper place
			 when the BIND_EXPR is expanded.  */
		      delete_block ($3);
		    }
		  else
		    $$ = $3;
		}
	| primary '(' nonnull_exprlist ')'
                { /* [eichin:19911016.1902EST] */
                  $<ttype>$ = build_x_function_call ($1, $3, current_class_decl); 
                  /* here we instantiate_class_template as needed... */
                  do_pending_templates ();
                } template_instantiate_some {
                  if (TREE_CODE ($<ttype>5) == CALL_EXPR
                      && TREE_TYPE ($<ttype>5) != void_type_node)
	            $$ = require_complete_type ($<ttype>5);
                  else
                    $$ = $<ttype>5;
                }
	| primary LEFT_RIGHT
                {
		  $$ = build_x_function_call ($$, NULL_TREE, current_class_decl);
		  if (TREE_CODE ($$) == CALL_EXPR
		      && TREE_TYPE ($$) != void_type_node)
		    $$ = require_complete_type ($$);
                }
	| primary '[' expr ']'
		{ $$ = grok_array_decl ($$, $3); }
	| primary PLUSPLUS
		{ /* If we get an OFFSET_REF, turn it into what it really
		     means (e.g., a COMPONENT_REF).  This way if we've got,
		     say, a reference to a static member that's being operated
		     on, we don't end up trying to find a member operator for
		     the class it's in.  */
		  if (TREE_CODE ($$) == OFFSET_REF)
		    $$ = resolve_offset_ref ($$);
		  $$ = build_x_unary_op (POSTINCREMENT_EXPR, $$); }
	| primary MINUSMINUS
		{ if (TREE_CODE ($$) == OFFSET_REF)
		    $$ = resolve_offset_ref ($$);
		  $$ = build_x_unary_op (POSTDECREMENT_EXPR, $$); }
	/* C++ extensions */
	| THIS
		{ if (current_class_decl)
		    {
#ifdef WARNING_ABOUT_CCD
		      TREE_USED (current_class_decl) = 1;
#endif
		      $$ = current_class_decl;
		    }
		  else if (current_function_decl
			   && DECL_STATIC_FUNCTION_P (current_function_decl))
		    {
		      error ("`this' is unavailable for static member functions");
		      $$ = error_mark_node;
		    }
		  else
		    {
		      if (current_function_decl)
			error ("invalid use of `this' in non-member function");
		      else
			error ("invalid use of `this' at top level");
		      $$ = error_mark_node;
		    }
		}
	| TYPE_QUAL '(' nonnull_exprlist ')'
		{
		  tree type;
		  tree id = $$;

		  /* This is a C cast in C++'s `functional' notation.  */
		  if ($3 == error_mark_node)
		    {
		      $$ = error_mark_node;
		      break;
		    }
#if 0
		  if ($3 == NULL_TREE)
		    {
		      error ("cannot cast null list to type `%s'",
		             IDENTIFIER_POINTER (TYPE_NAME (id)));
		      $$ = error_mark_node;
		      break;
		    }
#endif
#if 0
		  /* type is not set! (mrs) */
		  if (type == error_mark_node)
		    $$ = error_mark_node;
		  else
#endif
		    {
		      if (id == ridpointers[(int) RID_CONST])
		        type = build_type_variant (integer_type_node, 1, 0);
		      else if (id == ridpointers[(int) RID_VOLATILE])
		        type = build_type_variant (integer_type_node, 0, 1);
#if 0
		      /* should not be able to get here (mrs) */
		      else if (id == ridpointers[(int) RID_FRIEND])
		        {
		          error ("cannot cast expression to `friend' type");
		          $$ = error_mark_node;
		          break;
		        }
#endif
		      else my_friendly_abort (79);
		      $$ = build_c_cast (type, build_compound_expr ($3), 1);
		    }
		}
	| functional_cast
	| DYNAMIC_CAST '<'
		{ dont_allow_type_definitions = "inside dynamic_cast"; }
	  type_id '>'
		{ dont_allow_type_definitions = 0; }
	  '(' expr ')'
		{ tree type = groktypename ($4);
		  $$ = build_dynamic_cast (type, $8); }
	| STATIC_CAST '<'
		{ dont_allow_type_definitions = "inside static_cast"; }
	  type_id '>'
		{ dont_allow_type_definitions = 0; }
	  '(' expr ')'
		{ tree type = groktypename ($4);
		  $$ = build_static_cast (type, $8); }
	| REINTERPRET_CAST '<'
		{ dont_allow_type_definitions = "inside reinterpret_cast"; }
	  type_id '>'
		{ dont_allow_type_definitions = 0; }
	  '(' expr ')'
		{ tree type = groktypename ($4);
		  $$ = build_reinterpret_cast (type, $8); }
	| CONST_CAST '<'
		{ dont_allow_type_definitions = "inside const_cast"; }
	  type_id '>'
		{ dont_allow_type_definitions = 0; }
	  '(' expr ')'
		{ tree type = groktypename ($4);
		  $$ = build_const_cast (type, $8); }
	| TYPEID '(' expr ')'
		{ $$ = build_typeid ($3); }
	| TYPEID '(' type_id ')'
		{ tree type = groktypename ($3);
		  $$ = get_typeid (TYPE_MAIN_VARIANT (type)); }
	| global_scope IDENTIFIER
		{
		do_scoped_id:
		  $$ = IDENTIFIER_GLOBAL_VALUE ($2);
		  if (yychar == YYEMPTY)
		    yychar = YYLEX;
		  if (! $$)
		    {
		      if (yychar == '(' || yychar == LEFT_RIGHT)
			$$ = implicitly_declare ($2);
		      else
			{
			  if (IDENTIFIER_GLOBAL_VALUE ($2) != error_mark_node)
			    error ("undeclared variable `%s' (first use here)",
				   IDENTIFIER_POINTER ($2));
			  $$ = error_mark_node;
			  /* Prevent repeated error messages.  */
			  IDENTIFIER_GLOBAL_VALUE ($2) = error_mark_node;
			}
		    }
		  else
		    {
		      if (TREE_CODE ($$) == ADDR_EXPR)
			assemble_external (TREE_OPERAND ($$, 0));
		      else
			assemble_external ($$);
		      TREE_USED ($$) = 1;
		    }
		  if (TREE_CODE ($$) == CONST_DECL)
		    {
		      /* XXX CHS - should we set TREE_USED of the constant? */
		      $$ = DECL_INITIAL ($$);
		      /* This is to prevent an enum whose value is 0
			 from being considered a null pointer constant.  */
		      $$ = build1 (NOP_EXPR, TREE_TYPE ($$), $$);
		      TREE_CONSTANT ($$) = 1;
		    }

		}
	| global_scope operator_name
		{
		  got_scope = NULL_TREE;
		  if (TREE_CODE ($2) == IDENTIFIER_NODE)
		    goto do_scoped_id;
		  $$ = $2;
		}
	| overqualified_id %prec HYPERUNARY
		{ $$ = build_offset_ref (OP0 ($$), OP1 ($$)); }
	| overqualified_id '(' nonnull_exprlist ')'
		{ $$ = build_member_call (OP0 ($$), OP1 ($$), $3); }
	| overqualified_id LEFT_RIGHT
		{ $$ = build_member_call (OP0 ($$), OP1 ($$), NULL_TREE); }
	| object unqualified_id  %prec UNARY
		{ got_object = NULL_TREE;
		  $$ = build_component_ref ($$, $2, NULL_TREE, 1); }
	| object overqualified_id %prec UNARY
		{ got_object = NULL_TREE;
		  $$ = build_object_ref ($$, OP0 ($2), OP1 ($2)); }
	| object unqualified_id '(' nonnull_exprlist ')'
		{
		  got_object = NULL_TREE;
#if 0
		  /* This is a future direction of this code, but because
		     build_x_function_call cannot always undo what is done
		     in build_component_ref entirely yet, we cannot do this. */
		  $$ = build_x_function_call (build_component_ref ($$, $2, NULL_TREE, 1), $4, $$);
		  if (TREE_CODE ($$) == CALL_EXPR
		      && TREE_TYPE ($$) != void_type_node)
		    $$ = require_complete_type ($$);
#else
		  $$ = build_method_call ($$, $2, $4, NULL_TREE,
					  (LOOKUP_NORMAL|LOOKUP_AGGR));
#endif
		}
	| object unqualified_id LEFT_RIGHT
		{
		  got_object = NULL_TREE;
#if 0
		  /* This is a future direction of this code, but because
		     build_x_function_call cannot always undo what is done
		     in build_component_ref entirely yet, we cannot do this. */
		  $$ = build_x_function_call (build_component_ref ($$, $2, NULL_TREE, 1), NULL_TREE, $$);
		  if (TREE_CODE ($$) == CALL_EXPR
		      && TREE_TYPE ($$) != void_type_node)
		    $$ = require_complete_type ($$);
#else
		  $$ = build_method_call ($$, $2, NULL_TREE, NULL_TREE,
					  (LOOKUP_NORMAL|LOOKUP_AGGR));
#endif
		}
	| object OBJECTNAME LEFT_RIGHT
		{
		  got_object = NULL_TREE;
		  $$ = build_method_call ($$, $2, NULL_TREE, NULL_TREE,
					  (LOOKUP_NORMAL|LOOKUP_AGGR));
		}
	| object overqualified_id '(' nonnull_exprlist ')'
		{
		  got_object = NULL_TREE;
		  if (IS_SIGNATURE (IDENTIFIER_TYPE_VALUE (OP0 ($2))))
		    {
		      warning ("signature name in scope resolution ignored");
		      $$ = build_method_call ($$, OP1 ($2), $4, NULL_TREE,
					      (LOOKUP_NORMAL|LOOKUP_AGGR));
		    }
		  else
		    $$ = build_scoped_method_call ($$, OP0 ($2), OP1 ($2), $4);
		}
	| object overqualified_id LEFT_RIGHT
		{
		  got_object = NULL_TREE;
		  if (IS_SIGNATURE (IDENTIFIER_TYPE_VALUE (OP0 ($2))))
		    {
		      warning ("signature name in scope resolution ignored");
		      $$ = build_method_call ($$, OP1 ($2), NULL_TREE, NULL_TREE,
					      (LOOKUP_NORMAL|LOOKUP_AGGR));
		    }
		  else
		    $$ = build_scoped_method_call ($$, OP0 ($2), OP1 ($2), NULL_TREE);
		}
	/* p->int::~int() is valid -- 12.4 */
	| object '~' TYPESPEC LEFT_RIGHT
		{
		  got_object = NULL_TREE;
		  if (IDENTIFIER_GLOBAL_VALUE ($3)
		      && (TREE_CODE (TREE_TYPE ($1)) 
			  != TREE_CODE (TREE_TYPE (IDENTIFIER_GLOBAL_VALUE ($3)))))
		    cp_error ("`%E' is not of type `%T'", $1, $3);
		  $$ = convert (void_type_node, $1);
		}
	| object TYPESPEC SCOPE '~' TYPESPEC LEFT_RIGHT
		{
		  got_object = NULL_TREE;
		  if ($2 != $5)
		    cp_error ("destructor specifier `%T::~%T()' must have matching names", $2, $5);
		  if (TREE_CODE (TREE_TYPE ($1))
		      != TREE_CODE (TREE_TYPE (IDENTIFIER_GLOBAL_VALUE ($2))))
		    cp_error ("`%E' is not of type `%T'", $1, $2);
		  $$ = convert (void_type_node, $1);
		}
	/* Objective-C expressions */
	| objcmessageexpr
		{ $$ = build_message_expr ($1); }
	| objcselectorexpr
		{ $$ = build_selector_expr ($1); }
	| objcprotocolexpr
		{ $$ = build_protocol_expr ($1); }
	| objc_string
		{ $$ = build_objc_string_object ($1); }
	| object error
		{
		  got_object = NULL_TREE;
		  $$ = error_mark_node;
		}
	;

/* Not needed for now.

primary_no_id:
	  '(' expr ')'
		{ $$ = $2; }
	| '(' error ')'
		{ $$ = error_mark_node; }
	| '('
		{ if (current_function_decl == 0)
		    {
		      error ("braced-group within expression allowed only inside a function");
		      YYERROR;
		    }
		  $<ttype>$ = expand_start_stmt_expr (); }
	  compstmt ')'
		{ if (pedantic)
		    pedwarn ("ANSI C++ forbids braced-groups within expressions");
		  $$ = expand_end_stmt_expr ($<ttype>2); }
	| primary_no_id '(' nonnull_exprlist ')'
		{ $$ = build_x_function_call ($$, $3, current_class_decl); }
	| primary_no_id LEFT_RIGHT
		{ $$ = build_x_function_call ($$, NULL_TREE, current_class_decl); }
	| primary_no_id '[' expr ']'
		{ goto do_array; }
	| primary_no_id PLUSPLUS
		{ $$ = build_x_unary_op (POSTINCREMENT_EXPR, $$); }
	| primary_no_id MINUSMINUS
		{ $$ = build_x_unary_op (POSTDECREMENT_EXPR, $$); }
	| SCOPE IDENTIFIER
		{ goto do_scoped_id; }
	| SCOPE operator_name
		{ if (TREE_CODE ($2) == IDENTIFIER_NODE)
		    goto do_scoped_id;
		  goto do_scoped_operator;
		}
	;
*/

new:	  NEW
		{ $$ = 0; }
	| global_scope NEW
		{ got_scope = NULL_TREE; $$ = 1; }
	;

delete:	  DELETE
		{ $$ = 0; }
	| global_scope delete
		{ got_scope = NULL_TREE; $$ = 1; }
	;

boolean.literal:
	  CXX_TRUE
		{ $$ = boolean_true_node; }
	| CXX_FALSE
		{ $$ = boolean_false_node; }
	;

/* Produces a STRING_CST with perhaps more STRING_CSTs chained onto it.  */
string:
	  STRING
	| string STRING
		{ $$ = chainon ($$, $2); }
	| objcencodeexpr
		{ $$ = build_encode_expr ($1); }
	| string objcencodeexpr
		{ $$ = chainon ($$, build_encode_expr ($1)); }
	;

/* Produces an OBJC_STRING_CST with perhaps more OBJC_STRING_CSTs chained
   onto it.  */
objc_string:
	  OBJC_STRING
	| objc_string OBJC_STRING
		{ $$ = chainon ($1, $2); }
	;

nodecls:
	  /* empty */
		{
		  if (! current_function_parms_stored)
		    store_parm_decls ();
		  setup_vtbl_ptr ();
		  /* Always keep the BLOCK node associated with the outermost
		     pair of curley braces of a function.  These are needed
		     for correct operation of dwarfout.c.  */
		  keep_next_level ();
		}
	;

object:	  primary '.'
		{ got_object = TREE_TYPE ($$); }
	| primary POINTSAT
		{
		  $$ = build_x_arrow ($$); 
		  got_object = TREE_TYPE ($$);
		}
	;

decl:
	/* Normal case: make this fast.  */
	  typespec declarator ';'
		{ tree d = get_decl_list ($1);
		  int yes = suspend_momentary ();
		  d = start_decl ($2, d, 0, NULL_TREE);
		  cp_finish_decl (d, NULL_TREE, NULL_TREE, 0, 0);
		  resume_momentary (yes);
		  if (IS_AGGR_TYPE_CODE (TREE_CODE ($1)))
		    note_got_semicolon ($1);
		}
	| typed_declspecs declarator ';'
		{ tree d, specs, attrs;
		  int yes;
		  split_specs_attrs ($1, &specs, &attrs);
		  yes = suspend_momentary ();
		  d = start_decl ($2, specs, 0, NULL_TREE);
		  cplus_decl_attributes (d, NULL_TREE, attrs);
		  cp_finish_decl (d, NULL_TREE, NULL_TREE, 0, 0);
		  resume_momentary (yes);
		  note_list_got_semicolon ($1);
		}
	| typespec initdecls ';'
		{
		  resume_momentary ($2);
		  if (IS_AGGR_TYPE_CODE (TREE_CODE ($1)))
		    note_got_semicolon ($1);
		}
	| typed_declspecs initdecls ';'
		{
		  resume_momentary ($2);
		  note_list_got_semicolon ($1);
		}
	| declmods notype_initdecls ';'
		{ resume_momentary ($2); }
	| typed_declspecs ';'
		{
		  shadow_tag ($1);
		  note_list_got_semicolon ($1);
		}
	| declmods ';'
		{ warning ("empty declaration"); }
	;

/* Any kind of declarator (thus, all declarators allowed
   after an explicit typespec).  */

declarator:
	  after_type_declarator %prec EMPTY
	| notype_declarator %prec EMPTY
	;

/* This is necessary to postpone reduction of `int()()()()'.  */
fcast_or_absdcl:
	  LEFT_RIGHT %prec EMPTY
		{ $$ = build_parse_node (CALL_EXPR, NULL_TREE, empty_parms (),
					 NULL_TREE); }
	| fcast_or_absdcl LEFT_RIGHT %prec EMPTY
		{ $$ = build_parse_node (CALL_EXPR, $$, empty_parms (), 
					 NULL_TREE); }
	;

/* ANSI type-id (8.1) */
type_id:
	  typed_typespecs absdcl
		{ $$ = build_decl_list ($$, $2); }
	| nonempty_type_quals absdcl
		{ $$ = build_decl_list ($$, $2); }
	| typespec absdcl
		{ $$ = build_decl_list (get_decl_list ($$), $2); }
	| typed_typespecs %prec EMPTY
		{ $$ = build_decl_list ($$, NULL_TREE); }
	| nonempty_type_quals %prec EMPTY
		{ $$ = build_decl_list ($$, NULL_TREE); }
	;

/* Declspecs which contain at least one type specifier or typedef name.
   (Just `const' or `volatile' is not enough.)
   A typedef'd name following these is taken as a name to be declared.
   In the result, declspecs have a non-NULL TREE_VALUE, attributes do not.  */

typed_declspecs:
	  typed_typespecs %prec EMPTY
	| typed_declspecs1
	;

typed_declspecs1:
	  declmods typespec
		{ $$ = decl_tree_cons (NULL_TREE, $2, $$); }
	| typespec reserved_declspecs	%prec HYPERUNARY
		{ $$ = decl_tree_cons (NULL_TREE, $$, $2); }
	| typespec reserved_typespecquals reserved_declspecs
		{ $$ = decl_tree_cons (NULL_TREE, $$, chainon ($2, $3)); }
	| declmods typespec reserved_declspecs
		{ $$ = decl_tree_cons (NULL_TREE, $2, chainon ($3, $$)); }
	| declmods typespec reserved_typespecquals
		{ $$ = decl_tree_cons (NULL_TREE, $2, chainon ($3, $$)); }
	| declmods typespec reserved_typespecquals reserved_declspecs
		{ $$ = decl_tree_cons (NULL_TREE, $2, 
				       chainon ($3, chainon ($4, $$))); }
	;

reserved_declspecs:
	  SCSPEC
		{ if (extra_warnings)
		    warning ("`%s' is not at beginning of declaration",
			     IDENTIFIER_POINTER ($$));
		  $$ = build_decl_list (NULL_TREE, $$); }
	| reserved_declspecs typespecqual_reserved
		{ $$ = decl_tree_cons (NULL_TREE, $2, $$); }
	| reserved_declspecs SCSPEC
		{ if (extra_warnings)
		    warning ("`%s' is not at beginning of declaration",
			     IDENTIFIER_POINTER ($2));
		  $$ = decl_tree_cons (NULL_TREE, $2, $$); }
	| reserved_declspecs attributes
		{ $$ = decl_tree_cons ($2, NULL_TREE, $1); }
	| attributes
		{ $$ = decl_tree_cons ($1, NULL_TREE, NULL_TREE); }
	;

/* List of just storage classes and type modifiers.
   A declaration can start with just this, but then it cannot be used
   to redeclare a typedef-name.
   In the result, declspecs have a non-NULL TREE_VALUE, attributes do not.  */

declmods:
	  nonempty_type_quals %prec EMPTY
		{ TREE_STATIC ($$) = 1; }
	| SCSPEC
		{ $$ = IDENTIFIER_AS_LIST ($$); }
ifwin32
	| declspec
		{ $$ = decl_tree_cons (NULL_TREE, $1, NULL_TREE); }
end ifwin32
	| declmods TYPE_QUAL
		{ $$ = decl_tree_cons (NULL_TREE, $2, $$);
		  TREE_STATIC ($$) = 1; }
	| declmods SCSPEC
		{ if (extra_warnings && TREE_STATIC ($$))
		    warning ("`%s' is not at beginning of declaration",
			     IDENTIFIER_POINTER ($2));
		  $$ = decl_tree_cons (NULL_TREE, $2, $$);
		  TREE_STATIC ($$) = TREE_STATIC ($1); }
ifwin32
	| declmods declspec
		{ $$ = decl_tree_cons (NULL_TREE, $2, $1); }
end ifwin32
	| declmods attributes
		{ $$ = decl_tree_cons ($2, NULL_TREE, $1); }
	| attributes
		{ $$ = decl_tree_cons ($1, NULL_TREE, NULL_TREE); }
	;

/* Used instead of declspecs where storage classes are not allowed
   (that is, for typenames and structure components).

   C++ can takes storage classes for structure components.
   Don't accept a typedef-name if anything but a modifier precedes it.  */

typed_typespecs:
	  typespec  %prec EMPTY
		{ $$ = get_decl_list ($$); }
	| nonempty_type_quals typespec
		{ $$ = decl_tree_cons (NULL_TREE, $2, $$); }
	| typespec reserved_typespecquals
		{ $$ = decl_tree_cons (NULL_TREE, $$, $2); }
	| nonempty_type_quals typespec reserved_typespecquals
		{ $$ = decl_tree_cons (NULL_TREE, $2, chainon ($3, $$)); }
	;

reserved_typespecquals:
	  typespecqual_reserved
		{ $$ = build_decl_list (NULL_TREE, $$); }
	| reserved_typespecquals typespecqual_reserved
		{ $$ = decl_tree_cons (NULL_TREE, $2, $$); }
	;

/* A typespec (but not a type qualifier).
   Once we have seen one of these in a declaration,
   if a typedef name appears then it is being redeclared.  */

typespec: structsp
	| TYPESPEC  %prec EMPTY
        | CLASSNAME protocolrefs
		{ $$ = get_static_reference ($1, $2); }
	| OBJECTNAME protocolrefs
	        { $$ = get_object_reference ($2); }
	| complete_type_name
	| TYPEOF '(' expr ')'
		{ $$ = TREE_TYPE ($3);
		  if (pedantic && !in_system_header)
		    pedwarn ("ANSI C++ forbids `typeof'"); }
	| TYPEOF '(' type_id ')'
		{ $$ = groktypename ($3);
		  if (pedantic && !in_system_header)
		    pedwarn ("ANSI C++ forbids `typeof'"); }
	| SIGOF '(' expr ')'
		{ tree type = TREE_TYPE ($3);

		  if (IS_AGGR_TYPE (type))
		    {
		      sorry ("sigof type specifier");
		      $$ = type;
		    }
		  else
		    {
		      error ("`sigof' applied to non-aggregate expression");
		      $$ = error_mark_node;
		    }
		}
	| SIGOF '(' type_id ')'
		{ tree type = groktypename ($3);

		  if (IS_AGGR_TYPE (type))
		    {
		      sorry ("sigof type specifier");
		      $$ = type;
		    }
		  else
		    {
		      error("`sigof' applied to non-aggregate type");
		      $$ = error_mark_node;
		    }
		}
	;

/* A typespec that is a reserved word, or a type qualifier.  */

typespecqual_reserved: TYPESPEC
	| TYPE_QUAL
	| structsp
	;

initdecls:
	  initdcl0
	| initdecls ',' initdcl
	;

notype_initdecls:
	  notype_initdcl0
	| notype_initdecls ',' initdcl
	;

nomods_initdecls:
	  nomods_initdcl0
	| nomods_initdecls ',' initdcl
	;

maybeasm:
	  /* empty */
		{ $$ = NULL_TREE; }
	| asm_keyword '(' string ')'
		{ if (TREE_CHAIN ($3)) $3 = combine_strings ($3); $$ = $3; }
	;

initdcl0:
	  declarator exception_specification_opt maybeasm maybe_attribute '='
		{ split_specs_attrs ($<ttype>0, &current_declspecs,
				     &prefix_attributes);
		  if (TREE_CODE (current_declspecs) != TREE_LIST)
		    current_declspecs = get_decl_list (current_declspecs);
		  if (have_extern_spec && !used_extern_spec)
		    {
		      current_declspecs = decl_tree_cons
			(NULL_TREE, get_identifier ("extern"), 
			 current_declspecs);
		      used_extern_spec = 1;
		    }
		  $<itype>5 = suspend_momentary ();
		  $<ttype>$ = start_decl ($<ttype>1, current_declspecs, 1, $2);
		  cplus_decl_attributes ($<ttype>$, $4, prefix_attributes); }
	  init
/* Note how the declaration of the variable is in effect while its init is parsed! */
		{ cp_finish_decl ($<ttype>6, $7, $3, 0, LOOKUP_ONLYCONVERTING);
		  $$ = $<itype>5; }
	| declarator exception_specification_opt maybeasm maybe_attribute
		{ tree d;
		  split_specs_attrs ($<ttype>0, &current_declspecs,
				     &prefix_attributes);
		  if (TREE_CODE (current_declspecs) != TREE_LIST)
		    current_declspecs = get_decl_list (current_declspecs);
		  if (have_extern_spec && !used_extern_spec)
		    {
		      current_declspecs = decl_tree_cons
			(NULL_TREE, get_identifier ("extern"), 
			 current_declspecs);
		      used_extern_spec = 1;
		    }
		  $$ = suspend_momentary ();
		  d = start_decl ($<ttype>1, current_declspecs, 0, $2);
		  cplus_decl_attributes (d, $4, prefix_attributes);
		  cp_finish_decl (d, NULL_TREE, $3, 0, 0); }
	;

initdcl:
	  declarator exception_specification_opt maybeasm maybe_attribute '='
		{ $<ttype>$ = start_decl ($<ttype>1, current_declspecs, 1, $2);
		  cplus_decl_attributes ($<ttype>$, $4, prefix_attributes); }
	  init
/* Note how the declaration of the variable is in effect while its init is parsed! */
		{ cp_finish_decl ($<ttype>6, $7, $3, 0, LOOKUP_ONLYCONVERTING); }
	| declarator exception_specification_opt maybeasm maybe_attribute
		{ $<ttype>$ = start_decl ($<ttype>1, current_declspecs, 0, $2);
		  cplus_decl_attributes ($<ttype>$, $4, prefix_attributes);
		  cp_finish_decl ($<ttype>$, NULL_TREE, $3, 0, 0); }
	;

notype_initdcl0:
	  notype_declarator exception_specification_opt maybeasm maybe_attribute '='
		{ split_specs_attrs ($<ttype>0, &current_declspecs,
				     &prefix_attributes);
		  $<itype>5 = suspend_momentary ();
		  $<ttype>$ = start_decl ($<ttype>1, current_declspecs, 1, $2);
		  cplus_decl_attributes ($<ttype>$, $4, prefix_attributes); }
	  init
/* Note how the declaration of the variable is in effect while its init is parsed! */
		{ cp_finish_decl ($<ttype>6, $7, $3, 0, LOOKUP_ONLYCONVERTING);
		  $$ = $<itype>5; }
	| notype_declarator exception_specification_opt maybeasm maybe_attribute
		{ tree d;
		  split_specs_attrs ($<ttype>0, &current_declspecs,
				     &prefix_attributes);
		  $$ = suspend_momentary ();
		  d = start_decl ($<ttype>1, current_declspecs, 0, $2);
		  cplus_decl_attributes (d, $4, prefix_attributes);
		  cp_finish_decl (d, NULL_TREE, $3, 0, 0); }
	;

nomods_initdcl0:
	  notype_declarator exception_specification_opt maybeasm maybe_attribute '='
		{ current_declspecs = NULL_TREE;
		  prefix_attributes = NULL_TREE;
		  $<itype>5 = suspend_momentary ();
		  $<ttype>$ = start_decl ($1, current_declspecs, 1, $2);
		  cplus_decl_attributes ($<ttype>$, $4, prefix_attributes); }
	  init
/* Note how the declaration of the variable is in effect while its init is parsed! */
		{ cp_finish_decl ($<ttype>6, $7, $3, 0, LOOKUP_ONLYCONVERTING);
		  $$ = $<itype>5; }
	| notype_declarator exception_specification_opt maybeasm maybe_attribute
		{ tree d;
		  current_declspecs = NULL_TREE;
		  prefix_attributes = NULL_TREE;
		  $$ = suspend_momentary ();
		  d = start_decl ($1, current_declspecs, 0, $2);
		  cplus_decl_attributes (d, $4, prefix_attributes);
		  cp_finish_decl (d, NULL_TREE, $3, 0, 0); }
	;

/* the * rules are dummies to accept the Apollo extended syntax
   so that the header files compile. */
maybe_attribute:
      /* empty */
  		{ $$ = NULL_TREE; }
	| attributes
		{ $$ = $1; }
	;
 
attributes:
      attribute
		{ $$ = $1; }
	| attributes attribute
		{ $$ = chainon ($1, $2); }
	;

attribute:
      ATTRIBUTE '(' '(' attribute_list ')' ')'
		{ $$ = $4; }
	;

attribute_list:
      attrib
		{ $$ = $1; }
	| attribute_list ',' attrib
		{ $$ = chainon ($1, $3); }
	;
 
attrib:
    /* empty */
		{ $$ = NULL_TREE; }
	| any_word
		{ $$ = build_tree_list ($1, NULL_TREE); }
	| any_word '(' IDENTIFIER ')'
		{ $$ = build_tree_list ($1, build_tree_list (NULL_TREE, $3)); }
	| any_word '(' IDENTIFIER ',' nonnull_exprlist ')'
		{ $$ = build_tree_list ($1, tree_cons (NULL_TREE, $3, $5)); }
	| any_word '(' nonnull_exprlist ')'
		{ $$ = build_tree_list ($1, $3); }
	;

/* This still leaves out most reserved keywords,
   shouldn't we include them?  */

any_word:
	  identifier
	| SCSPEC
	| TYPESPEC
	| TYPE_QUAL
	;

/* A nonempty list of identifiers, including typenames.  */
identifiers_or_typenames:
	identifier
		{ $$ = build_tree_list (NULL_TREE, $1); }
	| identifiers_or_typenames ',' identifier
		{ $$ = chainon ($1, build_tree_list (NULL_TREE, $3)); }
	;

maybe_init:
	%prec EMPTY /* empty */
		{ $$ = NULL_TREE; }
	| '=' init
		{ $$ = $2; }

init:
	  expr_no_commas %prec '='
	| '{' '}'
		{ $$ = build_nt (CONSTRUCTOR, NULL_TREE, NULL_TREE);
		  TREE_HAS_CONSTRUCTOR ($$) = 1; }
	| '{' initlist '}'
		{ $$ = build_nt (CONSTRUCTOR, NULL_TREE, nreverse ($2));
		  TREE_HAS_CONSTRUCTOR ($$) = 1; }
	| '{' initlist ',' '}'
		{ $$ = build_nt (CONSTRUCTOR, NULL_TREE, nreverse ($2));
		  TREE_HAS_CONSTRUCTOR ($$) = 1; }
	| error
		{ $$ = NULL_TREE; }
	;

/* This chain is built in reverse order,
   and put in forward order where initlist is used.  */
initlist:
	  init
		{ $$ = build_tree_list (NULL_TREE, $$); }
	| initlist ',' init
		{ $$ = tree_cons (NULL_TREE, $3, $$); }
	/* These are for labeled elements.  The syntax for an array element
	   initializer conflicts with the syntax for an Objective-C message,
	   so don't include these productions in the Objective-C grammar.  
	| '[' expr_no_commas ']' init
		{ $$ = build_tree_list ($2, $4); } */
	| initlist ',' CASE expr_no_commas ':' init
		{ $$ = tree_cons ($4, $6, $$); }
	| identifier ':' init
		{ $$ = build_tree_list ($$, $3); }
	| initlist ',' identifier ':' init
		{ $$ = tree_cons ($3, $5, $$); }
	;

structsp:
	  ENUM identifier '{'
		{ $<itype>3 = suspend_momentary ();
		  $$ = start_enum ($2); }
	  enumlist maybecomma_warn '}'
		{ $$ = finish_enum ($<ttype>4, $5);
		  resume_momentary ((int) $<itype>3);
		  check_for_missing_semicolon ($<ttype>4); }
	| ENUM identifier '{' '}'
		{ $$ = finish_enum (start_enum ($2), NULL_TREE);
		  check_for_missing_semicolon ($$); }
	| ENUM '{'
		{ $<itype>2 = suspend_momentary ();
		  $$ = start_enum (make_anon_name ()); }
	  enumlist maybecomma_warn '}'
		{ $$ = finish_enum ($<ttype>3, $4);
		  resume_momentary ((int) $<itype>1);
		  check_for_missing_semicolon ($<ttype>3); }
	| ENUM '{' '}'
		{ $$ = finish_enum (start_enum (make_anon_name()), NULL_TREE);
		  check_for_missing_semicolon ($$); }
	| ENUM identifier
		{ $$ = xref_tag (enum_type_node, $2, NULL_TREE, 1); }
	| ENUM complex_type_name
		{ $$ = xref_tag (enum_type_node, $2, NULL_TREE, 1); }
	| TYPENAME_KEYWORD complex_type_name
		{ $$ = $2; }
	/* C++ extensions, merged with C to avoid shift/reduce conflicts */
	| class_head left_curly opt.component_decl_list '}'
		{
		  int semi;
		  tree id;

#if 0
		  /* Need to rework class nesting in the
		     presence of nested classes, etc.  */
		  shadow_tag (CLASSTYPE_AS_LIST ($$)); */
#endif
		  if (yychar == YYEMPTY)
		    yychar = YYLEX;
		  semi = yychar == ';';
		  /* finish_struct nukes this anyway; if
		     finish_exception does too, then it can go. */
		  if (semi)
		    note_got_semicolon ($$);

		  if (TREE_CODE ($$) == ENUMERAL_TYPE)
		    /* $$ = $1 from default rule.  */;
		  else
		    {
		      $$ = finish_struct ($$, $3, semi);
		      if (semi) note_got_semicolon ($$);
		    }

		  pop_obstacks ();

		  id = TYPE_IDENTIFIER ($$);
		  if (id && IDENTIFIER_TEMPLATE (id))
		    {
		      tree decl;

		      /* I don't know if the copying of this TYPE_DECL is
		       * really needed.  However, it's such a small per-
		       * formance penalty that the extra safety is a bargain.
		       * - niklas@appli.se
		       */
		      push_obstacks (&permanent_obstack, &permanent_obstack);
		      decl = copy_node (lookup_name (id, 0));
		      if (DECL_LANG_SPECIFIC (decl))
			copy_lang_decl (decl);
		      pop_obstacks ();
		      undo_template_name_overload (id, 0);
		      pushdecl_top_level (decl);
		    }
		  if (! semi)
		    check_for_missing_semicolon ($$); }
	| class_head  %prec EMPTY
		{
		  /* struct B: public A; is not accepted by the WP grammar.  */
		  if (TYPE_BINFO_BASETYPES ($$) && !TYPE_SIZE ($$)
		      && ! TYPE_BEING_DEFINED ($$))
		    cp_error ("base clause without member specification for `%#T'",
			      $$);
		}
	;

maybecomma:
	  /* empty */
	| ','
	;

maybecomma_warn:
	  /* empty */
	| ','
		{ if (pedantic) pedwarn ("comma at end of enumerator list"); }
	;

aggr:	  AGGR
	| aggr SCSPEC
		{ error ("storage class specifier `%s' not allowed after struct or class", IDENTIFIER_POINTER ($2)); }
	| aggr TYPESPEC
		{ error ("type specifier `%s' not allowed after struct or class", IDENTIFIER_POINTER ($2)); }
	| aggr TYPE_QUAL
		{ error ("type qualifier `%s' not allowed after struct or class", IDENTIFIER_POINTER ($2)); }
	| aggr AGGR
		{ error ("no body nor ';' separates two class, struct or union declarations"); }
	;

specialization:
	  aggr template_type_name ';'
		{ 
		  yyungetc (';', 1); current_aggr = $$; $$ = $2; 
		  if ($<ttype>0 == ridpointers[(int) RID_TEMPLATE])
		    instantiate_class_template ($$, 2);
		}
	;

named_class_head_sans_basetype:
	  aggr identifier
		{ current_aggr = $$; $$ = $2; }
	| specialization
	;

named_class_head_sans_basetype_defn:
	  aggr identifier_defn %prec EMPTY
		{ current_aggr = $$; $$ = $2; }
	| aggr template_type_name '{'
		{ yyungetc ('{', 1);
		aggr2:
		  current_aggr = $$;
		  $$ = $2;
		  overload_template_name ($$, 0); }
	| aggr template_type_name ':'
		{ yyungetc (':', 1); goto aggr2; }
	;

named_complex_class_head_sans_basetype:
	  aggr nested_name_specifier identifier
		{ current_aggr = $$; $$ = $3; }
	| aggr template_type %prec EMPTY
		{ current_aggr = $$; $$ = $2; }
	;

do_xref_defn: /* empty */ %prec EMPTY
        { $<ttype>$ = xref_tag (current_aggr, $<ttype>0, NULL_TREE, 0); }
	;

named_class_head:
	  named_class_head_sans_basetype %prec EMPTY
		{ $$ = xref_tag (current_aggr, $1, NULL_TREE, 1); }
	| named_class_head_sans_basetype_defn do_xref_defn
          maybe_base_class_list %prec EMPTY
		{ 
		  $$ = $<ttype>2;
		  if ($3)
                    xref_basetypes (current_aggr, $1, $<ttype>2, $3); 
		}
	| named_complex_class_head_sans_basetype maybe_base_class_list
		{ 
		  $$ = TREE_TYPE ($1);
		  if ($2)
		    xref_basetypes (current_aggr, $1, TREE_TYPE ($1), $2); 
		}
	;

unnamed_class_head: aggr '{'
		{ $$ = xref_tag ($$, make_anon_name (), NULL_TREE, 0);
		  yyungetc ('{', 1); }
	;

class_head: unnamed_class_head | named_class_head ;

maybe_base_class_list:
	  %prec EMPTY /* empty */
		{ $$ = NULL_TREE; }
	| ':' see_typename %prec EMPTY
		{ yyungetc(':', 1); $$ = NULL_TREE; }
	| ':' see_typename base_class_list  %prec EMPTY
		{ $$ = $3; }
	;

base_class_list:
	  base_class
	| base_class_list ',' see_typename base_class
		{ $$ = chainon ($$, $4); }
	;

base_class:
	  base_class.1
		{
		  tree type;
		  type = IDENTIFIER_TYPE_VALUE ($$);
		  if (! is_aggr_typedef ($$, 1))
		    $$ = NULL_TREE;
		  else if (current_aggr == signature_type_node
			   && (! type) && (! IS_SIGNATURE (type)))
		    {
		      error ("class name not allowed as base signature");
		      $$ = NULL_TREE;
		    }
		  else if (current_aggr == signature_type_node)
		    {
		      sorry ("signature inheritance, base type `%s' ignored",
			     IDENTIFIER_POINTER ($$));
		      $$ = build_tree_list ((tree)access_public, $$);
		    }
		  else if (type && IS_SIGNATURE (type))
		    {
		      error ("signature name not allowed as base class");
		      $$ = NULL_TREE;
		    }
		  else
		    $$ = build_tree_list ((tree)access_default, $$);
		}
	| base_class_access_list see_typename base_class.1
		{
		  tree type;
		  type = IDENTIFIER_TYPE_VALUE ($3);
		  if (current_aggr == signature_type_node)
		    error ("access and source specifiers not allowed in signature");
		  if (! is_aggr_typedef ($3, 1))
		    $$ = NULL_TREE;
		  else if (current_aggr == signature_type_node
			   && (! type) && (! IS_SIGNATURE (type)))
		    {
		      error ("class name not allowed as base signature");
		      $$ = NULL_TREE;
		    }
		  else if (current_aggr == signature_type_node)
		    {
		      sorry ("signature inheritance, base type `%s' ignored",
			     IDENTIFIER_POINTER ($$));
		      $$ = build_tree_list ((tree)access_public, $3);
		    }
		  else if (type && IS_SIGNATURE (type))
		    {
		      error ("signature name not allowed as base class");
		      $$ = NULL_TREE;
		    }
		  else
		    $$ = build_tree_list ((tree) $$, $3);
		}
	;

base_class.1:
	  complete_type_name
	| SIGOF '(' expr ')'
		{
		  if (current_aggr == signature_type_node)
		    {
		      if (IS_AGGR_TYPE (TREE_TYPE ($3)))
			{
			  sorry ("`sigof' as base signature specifier");
			  /* need to return some dummy signature identifier */
			  $$ = $3;
			}
		      else
			{
			  error ("`sigof' applied to non-aggregate expression");
			  $$ = error_mark_node;
			}
		    }
		  else
		    {
		      error ("`sigof' in struct or class declaration");
		      $$ = error_mark_node;
		    }
		}
	| SIGOF '(' type_id ')'
		{
		  if (current_aggr == signature_type_node)
		    {
		      if (IS_AGGR_TYPE (groktypename ($3)))
			{
			  sorry ("`sigof' as base signature specifier");
			  /* need to return some dummy signature identifier */
			  $$ = $3;
			}
		      else
			{
			  error ("`sigof' applied to non-aggregate expression");
			  $$ = error_mark_node;
			}
		    }
		  else
		    {
		      error ("`sigof' in struct or class declaration");
		      $$ = error_mark_node;
		    }
		}
	;

base_class_access_list:
	  VISSPEC see_typename
	| SCSPEC see_typename
		{ if ($<ttype>$ != ridpointers[(int)RID_VIRTUAL])
		    sorry ("non-virtual access");
		  $$ = access_default_virtual; }
	| base_class_access_list VISSPEC see_typename
		{ int err = 0;
		  if ($2 == access_protected)
		    {
		      warning ("`protected' access not implemented");
		      $2 = access_public;
		      err++;
		    }
		  else if ($2 == access_public)
		    {
		      if ($1 == access_private)
			{
			mixed:
			  error ("base class cannot be public and private");
			}
		      else if ($1 == access_default_virtual)
			$$ = access_public_virtual;
		    }
		  else /* $2 == access_private */
		    {
		      if ($1 == access_public)
			goto mixed;
		      else if ($1 == access_default_virtual)
			$$ = access_private_virtual;
		    }
		}
	| base_class_access_list SCSPEC see_typename
		{ if ($2 != ridpointers[(int)RID_VIRTUAL])
		    sorry ("non-virtual access");
		  if ($$ == access_public)
		    $$ = access_public_virtual;
		  else if ($$ == access_private)
		    $$ = access_private_virtual; }
	;

left_curly: '{'
		{ tree t = $<ttype>0;
		  push_obstacks_nochange ();
		  end_temporary_allocation ();

		  if (! IS_AGGR_TYPE (t))
		    {
		      t = $<ttype>0 = make_lang_type (RECORD_TYPE);
		      TYPE_NAME (t) = get_identifier ("erroneous type");
		    }
		  if (TYPE_SIZE (t))
		    duplicate_tag_error (t);
                  if (TYPE_SIZE (t) || TYPE_BEING_DEFINED (t))
                    {
                      t = make_lang_type (TREE_CODE (t));
                      pushtag (TYPE_IDENTIFIER ($<ttype>0), t, 0);
                      $<ttype>0 = t;
                    }
		  pushclass (t, 0);
		  TYPE_BEING_DEFINED (t) = 1;
		  /* Reset the interface data, at the earliest possible
		     moment, as it might have been set via a class foo;
		     before.  */
		  /* Don't change signatures.  */
		  if (! IS_SIGNATURE (t))
		    {
		      extern tree pending_vtables;
		      int needs_writing;
		      tree name = TYPE_IDENTIFIER (t);

		      if (! ANON_AGGRNAME_P (name))
			{
			  CLASSTYPE_INTERFACE_ONLY (t) = interface_only;
			  SET_CLASSTYPE_INTERFACE_UNKNOWN_X
			    (t, interface_unknown);
			}

		      /* Record how to set the access of this class's
			 virtual functions.  If write_virtuals == 2 or 3, then
			 inline virtuals are ``extern inline''.  */
		      switch (write_virtuals)
			{
			case 0:
			case 1:
			  needs_writing = 1;
			  break;
			case 2:
			  needs_writing = !! value_member (name, pending_vtables);
			  break;
			case 3:
			  needs_writing = ! CLASSTYPE_INTERFACE_ONLY (t)
			    && CLASSTYPE_INTERFACE_KNOWN (t);
			  break;
			default:
			  needs_writing = 0;
			}
		      CLASSTYPE_VTABLE_NEEDS_WRITING (t) = needs_writing;
		    }
#if 0
		  t = TYPE_IDENTIFIER ($<ttype>0);
		  if (t && IDENTIFIER_TEMPLATE (t))
		    overload_template_name (t, 1);
#endif
		}
	;

opt.component_decl_list:
	/* empty */
		{ $$ = NULL_TREE; }
	| component_decl_list
		{
		  if (current_aggr == signature_type_node)
		    $$ = build_tree_list ((tree) access_public, $$);
		  else
		    $$ = build_tree_list ((tree) access_default, $$);
		}
	| opt.component_decl_list VISSPEC ':' component_decl_list
		{
		  tree visspec = (tree) $2;

		  if (current_aggr == signature_type_node)
		    {
		      error ("access specifier not allowed in signature");
		      visspec = (tree) access_public;
		    }
		  $$ = chainon ($$, build_tree_list (visspec, $4));
		}
	| opt.component_decl_list VISSPEC ':'
		{
		  if (current_aggr == signature_type_node)
		    error ("access specifier not allowed in signature");
		}
        | DEFS '(' CLASSNAME ')' 
                {
                  tree interface = lookup_interface ($3);
                  if (interface)
                    $$ = get_class_ivars (interface);
                  else
                    {
                      error ("Cannot find interface declaration for `%s'",
                             IDENTIFIER_POINTER ($3));
                      $$ = error_mark_node;
                    }
                }
        | DEFS '(' CLASSNAME ')' ';'
                {
                  tree interface = lookup_interface ($3);
                  if (interface)
                    $$ = get_class_ivars (interface);
                  else
                    {
                      error ("Cannot find interface declaration for `%s'",
                             IDENTIFIER_POINTER ($3));
                      $$ = error_mark_node;
                    }
                }
	;

/* Note: we no longer warn about the semicolon after a component_decl_list.
   ARM $9.2 says that the semicolon is optional, and therefore allowed.  */
component_decl_list:
	  component_decl
		{ if ($$ == void_type_node) $$ = NULL_TREE; 
		}
	| component_decl_list component_decl
		{ /* In pushdecl, we created a reverse list of names
		     in this binding level.  Make sure that the chain
		     of what we're trying to add isn't the item itself
		     (which can happen with what pushdecl's doing).  */
		  if ($2 != NULL_TREE && $2 != void_type_node)
		    {
		      if (TREE_CHAIN ($2) != $$)
			$$ = chainon ($$, $2);
		      else
			$$ = $2;
		    }
		}
	;

component_decl:
	  component_decl_1 ';'
		{ }
	| component_decl_1 '}'
		{ error ("missing ';' before right brace");
		  yyungetc ('}', 0); }
	/* C++: handle constructors, destructors and inline functions */
	/* note that INLINE is like a TYPESPEC */
	| fn.def2 ':' /* base_init compstmt */
		{ $$ = finish_method ($$); }
	| fn.def2 TRY /* base_init compstmt */
		{ $$ = finish_method ($$); }
	| fn.def2 RETURN /* base_init compstmt */
		{ $$ = finish_method ($$); }
	| fn.def2 '{' /* nodecls compstmt */
		{ $$ = finish_method ($$); }
	| ';'
		{ $$ = NULL_TREE; }
	;

component_decl_1:
	/* Do not add a "typed_declspecs declarator" rule here for
	   speed; we need to call grok_x_components for enums, so the
	   speedup would be insignificant.  */
	  typed_declspecs components
		{ $$ = grok_x_components ($1, $2); }
	| declmods notype_components
		{ $$ = grok_x_components ($1, $2); }
	| notype_declarator exception_specification_opt maybeasm maybe_attribute maybe_init
		{ $$ = grokfield ($$, NULL_TREE, $2, $5, $3,
				  build_tree_list ($4, NULL_TREE)); }
	| ':' expr_no_commas
		{ $$ = grokbitfield (NULL_TREE, NULL_TREE, $2); }
	| error
		{ $$ = NULL_TREE; }

	/* These rules introduce a reduce/reduce conflict; in
		typedef int foo, bar;
		class A {
		  foo (bar);
		};
	   should "A::foo" be declared as a function or "A::bar" as a data
	   member? In other words, is "bar" an after_type_declarator or a
	   parmlist? */
	| typed_declspecs '(' parmlist ')' type_quals exception_specification_opt maybeasm maybe_attribute maybe_init
		{ tree specs, attrs;
		  split_specs_attrs ($1, &specs, &attrs);
		  $$ = build_parse_node (CALL_EXPR, TREE_VALUE (specs),
					 $3, $5);
		  $$ = grokfield ($$, TREE_CHAIN (specs), $6, $9, $7,
				  build_tree_list ($8, attrs)); }
	| typed_declspecs LEFT_RIGHT type_quals exception_specification_opt maybeasm maybe_attribute maybe_init
		{ tree specs, attrs;
		  split_specs_attrs ($1, &specs, &attrs);
		  $$ = build_parse_node (CALL_EXPR, TREE_VALUE (specs),
					 empty_parms (), $3);
		  $$ = grokfield ($$, TREE_CHAIN (specs), $4, $7, $5,
				  build_tree_list ($6, attrs)); }
	| using_decl
		{ $$ = do_class_using_decl ($1); }
	;

/* The case of exactly one component is handled directly by component_decl. */
/* ??? Huh? ^^^ */
components:
	  /* empty: possibly anonymous */
		{ $$ = NULL_TREE; }
	| component_declarator0
	| components ',' component_declarator
		{
		  /* In this context, void_type_node encodes
		     friends.  They have been recorded elsewhere.  */
		  if ($$ == void_type_node)
		    $$ = $3;
		  else
		    $$ = chainon ($$, $3);
		}
	;

notype_components:
	  /* empty: possibly anonymous */
		{ $$ = NULL_TREE; }
	| notype_component_declarator0
	| notype_components ',' notype_component_declarator
		{
		  /* In this context, void_type_node encodes
		     friends.  They have been recorded elsewhere.  */
		  if ($$ == void_type_node)
		    $$ = $3;
		  else
		    $$ = chainon ($$, $3);
		}
	;

component_declarator0:
	  after_type_component_declarator0
	| notype_component_declarator0
	;

component_declarator:
	  after_type_component_declarator
	| notype_component_declarator
	;

after_type_component_declarator0:
	  after_type_declarator exception_specification_opt maybeasm maybe_attribute maybe_init
		{ split_specs_attrs ($<ttype>0, &current_declspecs,
				     &prefix_attributes);
		  $<ttype>0 = current_declspecs;
		  $$ = grokfield ($$, current_declspecs, $2, $5, $3,
				  build_tree_list ($4, prefix_attributes)); }
	| TYPENAME ':' expr_no_commas maybe_attribute
		{ split_specs_attrs ($<ttype>0, &current_declspecs,
				     &prefix_attributes);
		  $<ttype>0 = current_declspecs;
		  $$ = grokbitfield ($$, current_declspecs, $3);
		  cplus_decl_attributes ($$, $4, prefix_attributes); }
	;

notype_component_declarator0:
	  notype_declarator exception_specification_opt maybeasm maybe_attribute maybe_init
		{ split_specs_attrs ($<ttype>0, &current_declspecs,
				     &prefix_attributes);
		  $<ttype>0 = current_declspecs;
		  $$ = grokfield ($$, current_declspecs, $2, $5, $3,
				  build_tree_list ($4, prefix_attributes)); }
	| IDENTIFIER ':' expr_no_commas maybe_attribute
		{ split_specs_attrs ($<ttype>0, &current_declspecs,
				     &prefix_attributes);
		  $<ttype>0 = current_declspecs;
		  $$ = grokbitfield ($$, current_declspecs, $3);
		  cplus_decl_attributes ($$, $4, prefix_attributes); }
	| ':' expr_no_commas maybe_attribute
		{ split_specs_attrs ($<ttype>0, &current_declspecs,
				     &prefix_attributes);
		  $<ttype>0 = current_declspecs;
		  $$ = grokbitfield (NULL_TREE, current_declspecs, $2);
		  cplus_decl_attributes ($$, $3, prefix_attributes); }
	;

after_type_component_declarator:
	  after_type_declarator exception_specification_opt maybeasm maybe_attribute maybe_init
		{ $$ = grokfield ($$, current_declspecs, $2, $5, $3,
				  build_tree_list ($4, prefix_attributes)); }
	| TYPENAME ':' expr_no_commas maybe_attribute
		{ $$ = grokbitfield ($$, current_declspecs, $3);
		  cplus_decl_attributes ($$, $4, prefix_attributes); }
	;

notype_component_declarator:
	  notype_declarator exception_specification_opt maybeasm maybe_attribute maybe_init
		{ $$ = grokfield ($$, current_declspecs, $2, $5, $3,
				  build_tree_list ($4, prefix_attributes)); }
	| IDENTIFIER ':' expr_no_commas maybe_attribute
		{ $$ = grokbitfield ($$, current_declspecs, $3);
		  cplus_decl_attributes ($$, $4, prefix_attributes); }
	| ':' expr_no_commas maybe_attribute
		{ $$ = grokbitfield (NULL_TREE, current_declspecs, $2);
		  cplus_decl_attributes ($$, $3, prefix_attributes); }
	;

/* We chain the enumerators in reverse order.
   Because of the way enums are built, the order is
   insignificant.  Take advantage of this fact.  */

enumlist:
	  enumerator
	| enumlist ',' enumerator
		{ TREE_CHAIN ($3) = $$; $$ = $3; }
	;

enumerator:
	  identifier
		{ $$ = build_enumerator ($$, NULL_TREE); }
	| identifier '=' expr_no_commas
		{ $$ = build_enumerator ($$, $3); }
	;

/* ANSI new-type-id (5.3.4) */
new_type_id:
	  type_specifier_seq new_declarator
		{ $$ = build_decl_list ($$, $2); }
	| type_specifier_seq %prec EMPTY
		{ $$ = build_decl_list ($$, NULL_TREE); }
	/* GNU extension to allow arrays of arbitrary types with
	   non-constant dimension.  */
	| '(' type_id ')' '[' expr ']'
		{
		  if (pedantic)
		    pedwarn ("ANSI C++ forbids array dimensions with parenthesized type in new");
		  $$ = build_parse_node (ARRAY_REF, TREE_VALUE ($2), $5);
		  $$ = build_decl_list (TREE_PURPOSE ($2), $$);
		}
	;

type_quals:
	  /* empty */ %prec EMPTY
		{ $$ = NULL_TREE; }
	| type_quals TYPE_QUAL
		{ $$ = decl_tree_cons (NULL_TREE, $2, $$); }
	;

nonempty_type_quals:
	  TYPE_QUAL
		{ $$ = IDENTIFIER_AS_LIST ($$); }
	| nonempty_type_quals TYPE_QUAL
		{ $$ = decl_tree_cons (NULL_TREE, $2, $$); }
	;

/* These rules must follow the rules for function declarations
   and component declarations.  That way, longer rules are preferred.  */

suspend_mom:
	{ $<itype>$ = suspend_momentary (); } 

/* An expression which will not live on the momentary obstack.  */
nonmomentary_expr:
	suspend_mom expr
	{ resume_momentary ((int) $<itype>1); $$ = $2; }
/* This rule needs to be before after_type_declarator so that we resolve
   the reduce/reduce conflict in state 111 correctly.  We need to resolve it
   the same way that vanilla C++ resolves the reduce/reduce conflict in state
   105.  That is we must assume that we are processing a typespec rather than
   an after_type_declarator when we see a TYPENAME and aren't sure.  For the
   OBJECTNAME case this means we must read any protocolrefs, and then resume
   processing the typespec. */
protocolrefs:
          /* empty */
		{
                  $$ = NULL_TREE;
                }
	| '<' identifier_list '>'
		{
                    $$ = $2;
		}
	;

/* An expression which will not live on the momentary obstack.  */
maybe_parmlist:
	  suspend_mom '(' nonnull_exprlist ')'
		{ resume_momentary ((int) $<itype>1); $$ = $3; }
	| suspend_mom '(' parmlist ')'
		{ resume_momentary ((int) $<itype>1); $$ = $3; }
	| suspend_mom OBJECTNAME LEFT_RIGHT
		{ resume_momentary ((int) $<itype>1); $$ = empty_parms (); }
	| suspend_mom LEFT_RIGHT
		{ resume_momentary ((int) $<itype>1); $$ = empty_parms (); }
	| suspend_mom '(' error ')'
		{ resume_momentary ((int) $<itype>1); $$ = NULL_TREE; }
	;

/* A declarator that is allowed only after an explicit typespec.  */
/* may all be followed by prec '.' */
after_type_declarator:
	  '*' nonempty_type_quals after_type_declarator  %prec UNARY
		{ $$ = make_pointer_declarator ($2, $3); }
	| '&' nonempty_type_quals after_type_declarator  %prec UNARY
		{ $$ = make_reference_declarator ($2, $3); }
	| '*' after_type_declarator  %prec UNARY
		{ $$ = make_pointer_declarator (NULL_TREE, $2); }
	| '&' after_type_declarator  %prec UNARY
		{ $$ = make_reference_declarator (NULL_TREE, $2); }
	| ptr_to_mem type_quals after_type_declarator
		{ tree arg = make_pointer_declarator ($2, $3);
		  $$ = build_parse_node (SCOPE_REF, $1, arg);
		}
	| direct_after_type_declarator
	;

qualified_type_name:
	  type_name %prec EMPTY
		{
		  /* Remember that this name has been used in the class
		     definition, as per [class.scope0] */
		  if (current_class_type
		      && TYPE_BEING_DEFINED (current_class_type)
		      && ! IDENTIFIER_CLASS_VALUE ($$))
		    {
		      tree t = lookup_name ($$, -2);
		      if (t)
			pushdecl_class_level (t);
		    }
		}
	| nested_type
	;

nested_type:
	nested_name_specifier type_name %prec EMPTY
		{ $$ = $2; }
	;

direct_after_type_declarator:
	  direct_after_type_declarator maybe_parmlist type_quals %prec '.'
		{ $$ = build_parse_node (CALL_EXPR, $$, $2, $3); }
	| direct_after_type_declarator '[' nonmomentary_expr ']'
		{ $$ = build_parse_node (ARRAY_REF, $$, $3); }
	| direct_after_type_declarator '[' ']'
		{ $$ = build_parse_node (ARRAY_REF, $$, NULL_TREE); }
	| '(' after_type_declarator ')'
		{ $$ = $2; }
	| nested_name_specifier type_name %prec EMPTY
		{ push_nested_class (TREE_TYPE ($$), 3);
		  $$ = build_parse_node (SCOPE_REF, $$, $2);
		  TREE_COMPLEXITY ($$) = current_class_depth; }
	| type_name %prec EMPTY
	| OBJECTNAME %prec EMPTY
	;

/* A declarator allowed whether or not there has been
   an explicit typespec.  These cannot redeclare a typedef-name.  */

notype_declarator:
	  '*' nonempty_type_quals notype_declarator  %prec UNARY
		{ $$ = make_pointer_declarator ($2, $3); }
	| '&' nonempty_type_quals notype_declarator  %prec UNARY
		{ $$ = make_reference_declarator ($2, $3); }
	| '*' notype_declarator  %prec UNARY
		{ $$ = make_pointer_declarator (NULL_TREE, $2); }
	| '&' notype_declarator  %prec UNARY
		{ $$ = make_reference_declarator (NULL_TREE, $2); }
	| ptr_to_mem type_quals notype_declarator
		{ tree arg = make_pointer_declarator ($2, $3);
		  $$ = build_parse_node (SCOPE_REF, $1, arg);
		}
	| direct_notype_declarator
	;

complex_notype_declarator:
	  '*' nonempty_type_quals notype_declarator  %prec UNARY
		{ $$ = make_pointer_declarator ($2, $3); }
	| '&' nonempty_type_quals notype_declarator  %prec UNARY
		{ $$ = make_reference_declarator ($2, $3); }
	| '*' complex_notype_declarator  %prec UNARY
		{ $$ = make_pointer_declarator (NULL_TREE, $2); }
	| '&' complex_notype_declarator  %prec UNARY
		{ $$ = make_reference_declarator (NULL_TREE, $2); }
	| ptr_to_mem type_quals notype_declarator
		{ tree arg = make_pointer_declarator ($2, $3);
		  $$ = build_parse_node (SCOPE_REF, $1, arg);
		}
	| complex_direct_notype_declarator
	;

complex_direct_notype_declarator:
	  direct_notype_declarator maybe_parmlist type_quals  %prec '.'
		{ $$ = build_parse_node (CALL_EXPR, $$, $2, $3); }
	| '(' complex_notype_declarator ')'
		{ $$ = $2; }
	| direct_notype_declarator '[' nonmomentary_expr ']'
		{ $$ = build_parse_node (ARRAY_REF, $$, $3); }
	| direct_notype_declarator '[' ']'
		{ $$ = build_parse_node (ARRAY_REF, $$, NULL_TREE); }
	| notype_qualified_id
		{ if (TREE_TYPE (OP0 ($$)) != current_class_type)
		    {
		      push_nested_class (TREE_TYPE (OP0 ($$)), 3);
		      TREE_COMPLEXITY ($$) = current_class_depth;
		    }
		}
	;

qualified_id:
	nested_name_specifier unqualified_id
		{ got_scope = NULL_TREE;
		  $$ = build_parse_node (SCOPE_REF, $$, $2); }
	;

notype_qualified_id:
	nested_name_specifier notype_unqualified_id
		{ got_scope = NULL_TREE;
		  $$ = build_parse_node (SCOPE_REF, $$, $2); }
	;

overqualified_id:
	  notype_qualified_id
	| global_scope notype_qualified_id
		{ $$ = $2; }
	;

functional_cast:
	  typespec '(' nonnull_exprlist ')'
		{ $$ = build_functional_cast ($$, $3); }
	| typespec '(' expr_or_declarator ')'
		{ $$ = reparse_decl_as_expr ($$, $3); }
	| typespec fcast_or_absdcl %prec EMPTY
		{ $$ = reparse_absdcl_as_expr ($$, $2); }
	;

type_name:
	  TYPENAME
	| template_type %prec EMPTY
	;

nested_name_specifier:
	  nested_name_specifier_1
	| nested_name_specifier nested_name_specifier_1
		{ $$ = $2; }
	;

/* Why the @#$%^& do type_name and notype_identifier need to be expanded
   inline here?!?  (jason) */
nested_name_specifier_1:
	  TYPENAME SCOPE
		{ got_scope = TREE_TYPE ($$); }
	| NSNAME SCOPE
		{ got_scope = $$; }
	| template_type SCOPE
		{ got_scope = TREE_TYPE ($$); }
/* 	These break 'const i;'
	| IDENTIFIER SCOPE
		{
		 failed_scope:
		  cp_error ("`%D' is not an aggregate typedef", 
			    lastiddecl ? lastiddecl : $$);
		  $$ = error_mark_node;
		}
	| PTYPENAME SCOPE
		{ goto failed_scope; } */
	;

complete_type_name:
	  qualified_type_name
	| global_scope qualified_type_name
		{ $$ = $2; }
	;

complex_type_name:
	  nested_type
	| global_scope qualified_type_name
		{ $$ = $2; }
	;

ptr_to_mem:
	  nested_name_specifier '*'
		{ got_scope = NULL_TREE; }
	| global_scope nested_name_specifier '*'
		{ $$ = $2; got_scope = NULL_TREE; }
	;

/* All uses of explicit global scope must go through this nonterminal so
   that got_scope will be set before yylex is called to get the next token. */
global_scope:
	  SCOPE
		{ got_scope = void_type_node; }
	;

/* ANSI new-declarator (5.3.4) */
new_declarator:
	  '*' type_quals new_declarator
		{ $$ = make_pointer_declarator ($2, $3); }
	| '*' type_quals  %prec EMPTY
		{ $$ = make_pointer_declarator ($2, NULL_TREE); }
	| '&' type_quals new_declarator %prec EMPTY
		{ $$ = make_reference_declarator ($2, $3); }
	| '&' type_quals %prec EMPTY
		{ $$ = make_reference_declarator ($2, NULL_TREE); }
	| ptr_to_mem type_quals %prec EMPTY
		{ tree arg = make_pointer_declarator ($2, NULL_TREE);
		  $$ = build_parse_node (SCOPE_REF, $1, arg);
		}
	| ptr_to_mem type_quals new_declarator
		{ tree arg = make_pointer_declarator ($2, $3);
		  $$ = build_parse_node (SCOPE_REF, $1, arg);
		}
	| direct_new_declarator %prec EMPTY
	;

/* ANSI direct-new-declarator (5.3.4) */
direct_new_declarator:
	  '[' expr ']'
		{ $$ = build_parse_node (ARRAY_REF, NULL_TREE, $2); }
	| direct_new_declarator '[' nonmomentary_expr ']'
		{ $$ = build_parse_node (ARRAY_REF, $$, $3); }
	;

/* ANSI abstract-declarator (8.1) */
absdcl:
	  '*' nonempty_type_quals absdcl
		{ $$ = make_pointer_declarator ($2, $3); }
	| '*' absdcl
		{ $$ = make_pointer_declarator (NULL_TREE, $2); }
	| '*' nonempty_type_quals  %prec EMPTY
		{ $$ = make_pointer_declarator ($2, NULL_TREE); }
	| '*' %prec EMPTY
		{ $$ = make_pointer_declarator (NULL_TREE, NULL_TREE); }
	| '&' nonempty_type_quals absdcl
		{ $$ = make_reference_declarator ($2, $3); }
	| '&' absdcl
		{ $$ = make_reference_declarator (NULL_TREE, $2); }
	| '&' nonempty_type_quals %prec EMPTY
		{ $$ = make_reference_declarator ($2, NULL_TREE); }
	| '&' %prec EMPTY
		{ $$ = make_reference_declarator (NULL_TREE, NULL_TREE); }
	| ptr_to_mem type_quals %prec EMPTY
		{ tree arg = make_pointer_declarator ($2, NULL_TREE);
		  $$ = build_parse_node (SCOPE_REF, $1, arg);
		}
	| ptr_to_mem type_quals absdcl
		{ tree arg = make_pointer_declarator ($2, $3);
		  $$ = build_parse_node (SCOPE_REF, $1, arg);
		}
	| direct_abstract_declarator %prec EMPTY
	;

/* ANSI direct-abstract-declarator (8.1) */
direct_abstract_declarator:
	  '(' absdcl ')'
		{ $$ = $2; }
	  /* `(typedef)1' is `int'.  */
	| PAREN_STAR_PAREN
	| direct_abstract_declarator '(' parmlist ')' type_quals  %prec '.'
		{ $$ = build_parse_node (CALL_EXPR, $$, $3, $5); }
	| direct_abstract_declarator LEFT_RIGHT type_quals  %prec '.'
		{ $$ = build_parse_node (CALL_EXPR, $$, empty_parms (), $3); }
	| direct_abstract_declarator '[' nonmomentary_expr ']'  %prec '.'
		{ $$ = build_parse_node (ARRAY_REF, $$, $3); }
	| direct_abstract_declarator '[' ']'  %prec '.'
		{ $$ = build_parse_node (ARRAY_REF, $$, NULL_TREE); }
	| '(' complex_parmlist ')' type_quals  %prec '.'
		{ $$ = build_parse_node (CALL_EXPR, NULL_TREE, $2, $4); }
	| regcast_or_absdcl type_quals %prec '.'
		{ TREE_OPERAND ($$, 2) = $2; }
	| fcast_or_absdcl type_quals %prec '.'
		{ TREE_OPERAND ($$, 2) = $2; }
	| '[' nonmomentary_expr ']'  %prec '.'
		{ $$ = build_parse_node (ARRAY_REF, NULL_TREE, $2); }
	| '[' ']'  %prec '.'
		{ $$ = build_parse_node (ARRAY_REF, NULL_TREE, NULL_TREE); }
	;

/* For C++, decls and stmts can be intermixed, so we don't need to
   have a special rule that won't start parsing the stmt section
   until we have a stmt that parses without errors.  */

stmts:
	  stmt
	| errstmt
	| stmts stmt
	| stmts errstmt
	;

errstmt:  error ';'
	;

/* build the LET_STMT node before parsing its contents,
  so that any LET_STMTs within the context can have their display pointers
  set up to point at this one.  */

.pushlevel:  /* empty */
		{ emit_line_note (input_filename, lineno);
		  pushlevel (0);
		  clear_last_expr ();
		  push_momentary ();
		  expand_start_bindings (0);
		  if (objc_method_context)
		    add_objc_decls(); }
	;

.poplevel:   /* empty */
		{ expand_end_bindings (getdecls (), kept_level_p (), 1);
		  $$ = poplevel (kept_level_p (), 1, 0);
		  pop_momentary (); }
	;

/* Read zero or more forward-declarations for labels
   that nested functions can jump to.  */
maybe_label_decls:
	  /* empty */
	| label_decls
		{ if (pedantic)
		    pedwarn ("ANSI C++ forbids label declarations"); }
	;

label_decls:
	  label_decl
	| label_decls label_decl
	;

label_decl:
	  LABEL identifiers_or_typenames ';'
		{ tree link;
		  for (link = $2; link; link = TREE_CHAIN (link))
		    {
		      tree label = shadow_label (TREE_VALUE (link));
		      C_DECLARED_LABEL_FLAG (label) = 1;
		      declare_nonlocal_label (label);
		    }
		}
	;

/* This is the body of a function definition.
   It causes syntax errors to ignore to the next openbrace.  */
compstmt_or_error:
	  compstmt
		{}
	| error compstmt
	;

compstmt: '{' .pushlevel compstmtend .poplevel
		{ $$ = $4; }
	;

simple_if:
	  IF
		{ cond_stmt_keyword = "if"; }
	  .pushlevel paren_cond_or_null
		{ emit_line_note (input_filename, lineno);
		  expand_start_cond ($4, 0); }
	  implicitly_scoped_stmt
	;

implicitly_scoped_stmt:
	  compstmt
		{ finish_stmt (); }
	| .pushlevel simple_stmt .poplevel
		{ $$ = $3; }
	;

stmt:
	  compstmt
		{ finish_stmt (); }
	| simple_stmt
	;

simple_stmt:
	  decl
		{ finish_stmt (); }
	| expr ';'
		{
		  tree expr = $1;
		  emit_line_note (input_filename, lineno);
		  /* Do default conversion if safe and possibly important,
		     in case within ({...}).  */
		  if ((TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE
		       && lvalue_p (expr))
		      || TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE)
		    expr = default_conversion (expr);
		  cplus_expand_expr_stmt (expr);
		  clear_momentary ();
		  finish_stmt (); }
	| simple_if ELSE
		{ expand_start_else (); }
	  implicitly_scoped_stmt
		{ expand_end_cond (); }
	  .poplevel
		{ finish_stmt (); }
	| simple_if %prec IF
		{ expand_end_cond ();
		  expand_end_bindings (getdecls (), kept_level_p (), 1);
		  poplevel (kept_level_p (), 1, 0);
		  pop_momentary ();
		  finish_stmt (); }
	| WHILE
		{ emit_nop ();
		  emit_line_note (input_filename, lineno);
		  expand_start_loop (1);
		  cond_stmt_keyword = "while"; }
	  .pushlevel paren_cond_or_null
		{ expand_exit_loop_if_false (0, $4); }
	  already_scoped_stmt .poplevel
		{ expand_end_loop ();
		  finish_stmt (); }
	| DO
		{ emit_nop ();
		  emit_line_note (input_filename, lineno);
		  expand_start_loop_continue_elsewhere (1); }
	  implicitly_scoped_stmt WHILE
		{ expand_loop_continue_here ();
		  cond_stmt_keyword = "do"; }
	  paren_expr_or_null ';'
		{ emit_line_note (input_filename, lineno);
		  expand_exit_loop_if_false (0, $6);
		  expand_end_loop ();
		  clear_momentary ();
		  finish_stmt (); }
	| FOR
		{ emit_line_note (input_filename, lineno);
		  if (flag_new_for_scope > 0)
		    {
		      /* Conditionalize .pushlevel */
		      pushlevel (0);
		      note_level_for_for ();
		      clear_last_expr ();
		      push_momentary ();
		      expand_start_bindings (0);
		    }
		}
	  '(' for.init.statement
		{ emit_nop ();
		  emit_line_note (input_filename, lineno);
		  expand_start_loop_continue_elsewhere (1); }
	  .pushlevel xcond ';'
		{ emit_line_note (input_filename, lineno);
		  if ($7) expand_exit_loop_if_false (0, $7); }
	  xexpr ')'
		/* Don't let the tree nodes for $10 be discarded
		   by clear_momentary during the parsing of the next stmt.  */
		{ push_momentary (); }
	  already_scoped_stmt .poplevel
		{ emit_line_note (input_filename, lineno);
		  expand_loop_continue_here ();
		  if ($10) cplus_expand_expr_stmt ($10);
		  pop_momentary ();
		  expand_end_loop ();
		  if (flag_new_for_scope > 0)
		    {
		      expand_end_bindings (getdecls (), kept_level_p (), 1);
		      poplevel (kept_level_p (), 1, 0);
		      pop_momentary ();
		    }
		  finish_stmt (); }
	| SWITCH .pushlevel '(' condition ')'
		{ emit_line_note (input_filename, lineno);
		  c_expand_start_case ($4);
		  push_switch ();
		  /* Don't let the tree nodes for $4 be discarded by
		     clear_momentary during the parsing of the next stmt.  */
		  push_momentary (); }
	  implicitly_scoped_stmt
		{ expand_end_case ($4);
		  pop_momentary ();
		  pop_switch (); }
	  .poplevel
		{ finish_stmt (); }
	| CASE expr_no_commas ':'
		{ register tree value = check_cp_case_value ($2);
		  register tree label
		    = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);

		  if (value != error_mark_node)
		    {
		      tree duplicate;
		      int success = pushcase (value, convert_and_check,
					      label, &duplicate);
		      if (success == 1)
			cp_error ("case label `%E' not within a switch statement", $2);
		      else if (success == 2)
			{
			  cp_error ("duplicate case value `%E'", $2);
			  cp_error_at ("previously used here", duplicate);
			}
		      else if (success == 3)
			warning ("case value out of range");
		      else if (success == 5)
			cp_error ("case label `%E' within scope of cleanup or variable array", $2);
		    }
		  define_case_label (label);
		}
	  stmt
	| CASE expr_no_commas ELLIPSIS expr_no_commas ':'
		{ register tree value1 = check_cp_case_value ($2);
		  register tree value2 = check_cp_case_value ($4);
		  register tree label
		    = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);

		  if (pedantic)
		    pedwarn ("ANSI C++ forbids range expressions in switch statement");
		  if (value1 != error_mark_node
		      && value2 != error_mark_node)
		    {
		      tree duplicate;
		      int success = pushcase_range (value1, value2,
						    convert_and_check, label,
						    &duplicate);
		      if (success == 1)
			error ("case label not within a switch statement");
		      else if (success == 2)
			{
			  error ("duplicate (or overlapping) case value");
			  error_with_decl (duplicate, "this is the first entry overlapping that value");
			}
		      else if (success == 3)
			warning ("case value out of range");
		      else if (success == 4)
			warning ("empty range specified");
		      else if (success == 5)
			error ("case label within scope of cleanup or variable array");
		    }
		  define_case_label (label);
		}
	  stmt
	| DEFAULT ':'
		{
		  tree duplicate;
		  register tree label
		    = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
		  int success = pushcase (NULL_TREE, 0, label, &duplicate);
		  if (success == 1)
		    error ("default label not within a switch statement");
		  else if (success == 2)
		    {
		      error ("multiple default labels in one switch");
		      error_with_decl (duplicate, "this is the first default label");
		    }
		  define_case_label (NULL_TREE);
		}
	  stmt
	| BREAK ';'
		{ emit_line_note (input_filename, lineno);
		  if ( ! expand_exit_something ())
		    error ("break statement not within loop or switch"); }
	| CONTINUE ';'
		{ emit_line_note (input_filename, lineno);
		  if (! expand_continue_loop (0))
		    error ("continue statement not within a loop"); }
	| RETURN ';'
		{ emit_line_note (input_filename, lineno);
		  c_expand_return (NULL_TREE); }
	| RETURN expr ';'
		{ emit_line_note (input_filename, lineno);
		  c_expand_return ($2);
		  finish_stmt ();
		}
	| asm_keyword maybe_type_qual '(' string ')' ';'
		{ if (TREE_CHAIN ($4)) $4 = combine_strings ($4);
		  emit_line_note (input_filename, lineno);
		  expand_asm ($4);
		  finish_stmt ();
		}
	/* This is the case with just output operands.  */
	| asm_keyword maybe_type_qual '(' string ':' asm_operands ')' ';'
		{ if (TREE_CHAIN ($4)) $4 = combine_strings ($4);
		  emit_line_note (input_filename, lineno);
		  c_expand_asm_operands ($4, $6, NULL_TREE, NULL_TREE,
					 $2 == ridpointers[(int)RID_VOLATILE],
					 input_filename, lineno);
		  finish_stmt ();
		}
	/* This is the case with input operands as well.  */
	| asm_keyword maybe_type_qual '(' string ':' asm_operands ':' asm_operands ')' ';'
		{ if (TREE_CHAIN ($4)) $4 = combine_strings ($4);
		  emit_line_note (input_filename, lineno);
		  c_expand_asm_operands ($4, $6, $8, NULL_TREE,
					 $2 == ridpointers[(int)RID_VOLATILE],
					 input_filename, lineno);
		  finish_stmt ();
		}
	/* This is the case with clobbered registers as well.  */
	| asm_keyword maybe_type_qual '(' string ':' asm_operands ':'
	  asm_operands ':' asm_clobbers ')' ';'
		{ if (TREE_CHAIN ($4)) $4 = combine_strings ($4);
		  emit_line_note (input_filename, lineno);
		  c_expand_asm_operands ($4, $6, $8, $10,
					 $2 == ridpointers[(int)RID_VOLATILE],
					 input_filename, lineno);
		  finish_stmt ();
		}
	| GOTO '*' expr ';'
		{ emit_line_note (input_filename, lineno);
		  expand_computed_goto ($3); }
	| GOTO identifier ';'
		{ tree decl;
		  emit_line_note (input_filename, lineno);
		  decl = lookup_label ($2);
		  TREE_USED (decl) = 1;
		  expand_goto (decl); }
	| label_colon stmt
		{ finish_stmt (); }
	| label_colon '}'
		{ error ("label must be followed by statement");
		  yyungetc ('}', 0);
		  finish_stmt (); }
	| ';'
		{ finish_stmt (); }
	| try_block
	;

function_try_block:
	  TRY
		{
		  if (! current_function_parms_stored)
		    store_parm_decls ();
		  expand_start_early_try_stmts ();
		}
	  ctor_initializer_opt compstmt_or_error
		{ expand_end_try_stmts ();
		  expand_start_all_catch (); }
	  handler_seq
		{
		  expand_end_all_catch ();
		  finish_function (lineno, (int)$3, 0);
		}
	;

try_block:
	  TRY
		{ expand_start_try_stmts (); }
	  compstmt
		{ expand_end_try_stmts ();
		  expand_start_all_catch (); }
	  handler_seq
		{ expand_end_all_catch (); }
	;

handler_seq:
	  /* empty */
	| handler_seq CATCH .pushlevel
		{ dont_allow_type_definitions = "inside exception declarations"; }
	  handler_args
		{ dont_allow_type_definitions = 0; }
	  compstmt
		{ expand_end_catch_block (); }
	  .poplevel
	;

type_specifier_seq:
	  typed_typespecs %prec EMPTY
	| nonempty_type_quals %prec EMPTY
	;

handler_args:
	  '(' ELLIPSIS ')'
		{ expand_start_catch_block (NULL_TREE, NULL_TREE); }
	/* This doesn't allow reference parameters, the below does.
	| '(' type_specifier_seq absdcl ')'
		{ expand_start_catch_block ($2, $3); }
	| '(' type_specifier_seq ')'
		{ expand_start_catch_block ($2, NULL_TREE); }
	| '(' type_specifier_seq notype_declarator ')'
		{ expand_start_catch_block ($2, $3); }
	| '(' typed_typespecs after_type_declarator ')'
		{ expand_start_catch_block ($2, $3); }
	This allows reference parameters... */
	| '(' parm ')'
		{ expand_start_catch_block (TREE_PURPOSE ($2),
					    TREE_VALUE ($2)); }
	;

label_colon:
	  IDENTIFIER ':'
		{ tree label;
		do_label:
		  label = define_label (input_filename, lineno, $1);
		  if (label)
		    expand_label (label);
		}
	| PTYPENAME ':'
		{ goto do_label; }
	| TYPENAME ':'
		{ goto do_label; }
	;

for.init.statement:
	  xexpr ';'
		{ if ($1) cplus_expand_expr_stmt ($1); }
	| decl
	| '{' compstmtend
	;

/* Either a type-qualifier or nothing.  First thing in an `asm' statement.  */

maybe_type_qual:
	/* empty */
		{ emit_line_note (input_filename, lineno);
		  $$ = NULL_TREE; }
	| TYPE_QUAL
		{ emit_line_note (input_filename, lineno); }
	;

xexpr:
	/* empty */
		{ $$ = NULL_TREE; }
	| expr
	| error
		{ $$ = NULL_TREE; }
	;

/* These are the operands other than the first string and colon
   in  asm ("addextend %2,%1": "=dm" (x), "0" (y), "g" (*x))  */
asm_operands: /* empty */
		{ $$ = NULL_TREE; }
	| nonnull_asm_operands
	;

nonnull_asm_operands:
	  asm_operand
	| nonnull_asm_operands ',' asm_operand
		{ $$ = chainon ($$, $3); }
	;

asm_operand:
	  STRING '(' expr ')'
		{ $$ = build_tree_list ($$, $3); }
	;

asm_clobbers:
	  STRING
		{ $$ = tree_cons (NULL_TREE, $$, NULL_TREE); }
	| asm_clobbers ',' STRING
		{ $$ = tree_cons (NULL_TREE, $3, $$); }
	;

/* This is what appears inside the parens in a function declarator.
   Its value is represented in the format that grokdeclarator expects.

   In C++, declaring a function with no parameters
   means that that function takes *no* parameters.  */

parmlist:  /* empty */
		{
		  if (strict_prototype)
		    $$ = void_list_node;
		  else
		    $$ = NULL_TREE;
		}
	| complex_parmlist
	| type_id
		{ $$ = tree_cons (NULL_TREE, $$, void_list_node);
		  TREE_PARMLIST ($$) = 1; }
	;

/* This nonterminal does not include the common sequence '(' type_id ')',
   as it is ambiguous and must be disambiguated elsewhere.  */
complex_parmlist:
	  parms
		{
		  $$ = chainon ($$, void_list_node);
		  TREE_PARMLIST ($$) = 1;
		}
	| parms_comma ELLIPSIS
		{
		  TREE_PARMLIST ($$) = 1;
		}
	/* C++ allows an ellipsis without a separating ',' */
	| parms ELLIPSIS
		{
		  TREE_PARMLIST ($$) = 1;
		}
	| type_id ELLIPSIS
		{
		  $$ = build_tree_list (NULL_TREE, $$); 
		  TREE_PARMLIST ($$) = 1;
		}
	| ELLIPSIS
		{
		  /* ARM $8.2.5 has this as a boxed-off comment.  */
		  if (pedantic)
		    warning ("use of `...' without a first argument is non-portable");
		  $$ = NULL_TREE;
		}
	| TYPENAME_ELLIPSIS
		{
		  TREE_PARMLIST ($$) = 1;
		}
	| parms TYPENAME_ELLIPSIS
		{
		  TREE_PARMLIST ($$) = 1;
		}
	| type_id TYPENAME_ELLIPSIS
		{
		  $$ = build_tree_list (NULL_TREE, $$);
		  TREE_PARMLIST ($$) = 1;
		}
	| parms ':'
		{
		  /* This helps us recover from really nasty
		     parse errors, for example, a missing right
		     parenthesis.  */
		  yyerror ("possibly missing ')'");
		  $$ = chainon ($$, void_list_node);
		  TREE_PARMLIST ($$) = 1;
		  yyungetc (':', 0);
		  yychar = ')';
		}
	| type_id ':'
		{
		  /* This helps us recover from really nasty
		     parse errors, for example, a missing right
		     parenthesis.  */
		  yyerror ("possibly missing ')'");
		  $$ = tree_cons (NULL_TREE, $$, void_list_node);
		  TREE_PARMLIST ($$) = 1;
		  yyungetc (':', 0);
		  yychar = ')';
		}
	;

/* A nonempty list of parameter declarations or type names.  */
parms:
	  named_parm
		{ $$ = build_tree_list (NULL_TREE, $$); }
	| parm '=' init
		{ $$ = build_tree_list ($3, $$); }
	| parms_comma full_parm
		{ $$ = chainon ($$, $2); }
	| parms_comma bad_parm
		{ $$ = chainon ($$, build_tree_list (NULL_TREE, $2)); }
	| parms_comma bad_parm '=' init
		{ $$ = chainon ($$, build_tree_list ($4, $2)); }
	;

parms_comma:
	  parms ','
	| type_id ','
		{ $$ = build_tree_list (NULL_TREE, $$); }
	;

/* A single parameter declaration or parameter type name,
   as found in a parmlist.  The first four cases make up for 10%
   of the time spent parsing C++.  We cannot use them because
   of `int id[]' which won't get parsed properly.  */
named_parm:
	/* Here we expand typed_declspecs inline to avoid mis-parsing of
	   TYPESPEC IDENTIFIER.  */
	  typed_declspecs1 declarator
		{ tree specs = strip_attrs ($1);
		  $$ = build_tree_list (specs, $2); }
	| typed_typespecs declarator
		{ $$ = build_tree_list ($$, $2); }
	| typespec declarator
		{ $$ = build_tree_list (get_decl_list ($$), $2); }
	| typed_declspecs1 absdcl
		{ tree specs = strip_attrs ($1);
		  $$ = build_tree_list (specs, $2); }
	| typed_declspecs1 %prec EMPTY
		{ tree specs = strip_attrs ($1);
		  $$ = build_tree_list (specs, NULL_TREE); }
	| declmods notype_declarator
		{ tree specs = strip_attrs ($1);
		  $$ = build_tree_list (specs, $2); }
	;

full_parm:
	  parm maybe_init
		{ $$ = build_tree_list ($2, $$); }
	;

parm:
	named_parm
	| type_id
	;

see_typename: %prec EMPTY
	{ see_typename (); }
	;

bad_parm:
	  /* empty */ %prec EMPTY
		{
		  error ("type specifier omitted for parameter");
		  $$ = build_tree_list (integer_type_node, NULL_TREE);
		}
	| notype_declarator
		{
		  error ("type specifier omitted for parameter");
		  $$ = build_tree_list (integer_type_node, $$);
		}
	;

exception_specification_opt:
	  %prec EMPTY /* empty */
		{ $$ = NULL_TREE; }
	| THROW '(' ansi_raise_identifiers  ')' %prec EMPTY
		{ $$ = $3; }
	| THROW LEFT_RIGHT %prec EMPTY
		{ $$ = build_decl_list (NULL_TREE, NULL_TREE); }
	;

ansi_raise_identifier:
	  type_id
		{ $$ = build_decl_list (NULL_TREE, groktypename($$)); }
	;

ansi_raise_identifiers:
	  ansi_raise_identifier
	| ansi_raise_identifiers ',' ansi_raise_identifier
		{
		  TREE_CHAIN ($3) = $$;
		  $$ = $3;
		}
	;

conversion_declarator:
	  /* empty */ %prec EMPTY
		{ $$ = NULL_TREE; }
	| '*' type_quals conversion_declarator
		{ $$ = make_pointer_declarator ($2, $3); }
	| '&' type_quals conversion_declarator
		{ $$ = make_reference_declarator ($2, $3); }
	| ptr_to_mem type_quals conversion_declarator
		{ tree arg = make_pointer_declarator ($2, $3);
		  $$ = build_parse_node (SCOPE_REF, $1, arg);
		}
	;

operator: OPERATOR
		{ got_scope = NULL_TREE; }
	;

operator_name:
	  operator '*'
		{ $$ = ansi_opname[MULT_EXPR]; }
	| operator '/'
		{ $$ = ansi_opname[TRUNC_DIV_EXPR]; }
	| operator '%'
		{ $$ = ansi_opname[TRUNC_MOD_EXPR]; }
	| operator '+'
		{ $$ = ansi_opname[PLUS_EXPR]; }
	| operator '-'
		{ $$ = ansi_opname[MINUS_EXPR]; }
	| operator '&'
		{ $$ = ansi_opname[BIT_AND_EXPR]; }
	| operator '|'
		{ $$ = ansi_opname[BIT_IOR_EXPR]; }
	| operator '^'
		{ $$ = ansi_opname[BIT_XOR_EXPR]; }
	| operator '~'
		{ $$ = ansi_opname[BIT_NOT_EXPR]; }
	| operator ','
		{ $$ = ansi_opname[COMPOUND_EXPR]; }
	| operator ARITHCOMPARE
		{ $$ = ansi_opname[$2]; }
	| operator '<'
		{ $$ = ansi_opname[LT_EXPR]; }
	| operator '>'
		{ $$ = ansi_opname[GT_EXPR]; }
	| operator EQCOMPARE
		{ $$ = ansi_opname[$2]; }
	| operator ASSIGN
		{ $$ = ansi_assopname[$2]; }
	| operator '='
		{ $$ = ansi_opname [MODIFY_EXPR]; }
	| operator LSHIFT
		{ $$ = ansi_opname[$2]; }
	| operator RSHIFT
		{ $$ = ansi_opname[$2]; }
	| operator PLUSPLUS
		{ $$ = ansi_opname[POSTINCREMENT_EXPR]; }
	| operator MINUSMINUS
		{ $$ = ansi_opname[PREDECREMENT_EXPR]; }
	| operator ANDAND
		{ $$ = ansi_opname[TRUTH_ANDIF_EXPR]; }
	| operator OROR
		{ $$ = ansi_opname[TRUTH_ORIF_EXPR]; }
	| operator '!'
		{ $$ = ansi_opname[TRUTH_NOT_EXPR]; }
	| operator '?' ':'
		{ $$ = ansi_opname[COND_EXPR]; }
	| operator MIN_MAX
		{ $$ = ansi_opname[$2]; }
	| operator POINTSAT  %prec EMPTY
		{ $$ = ansi_opname[COMPONENT_REF]; }
	| operator POINTSAT_STAR  %prec EMPTY
		{ $$ = ansi_opname[MEMBER_REF]; }
	| operator LEFT_RIGHT
		{ $$ = ansi_opname[CALL_EXPR]; }
	| operator '[' ']'
		{ $$ = ansi_opname[ARRAY_REF]; }
	| operator NEW %prec EMPTY
		{ $$ = ansi_opname[NEW_EXPR]; }
	| operator DELETE %prec EMPTY
		{ $$ = ansi_opname[DELETE_EXPR]; }
	| operator NEW '[' ']'
		{ $$ = ansi_opname[VEC_NEW_EXPR]; }
	| operator DELETE '[' ']'
		{ $$ = ansi_opname[VEC_DELETE_EXPR]; }
	/* Names here should be looked up in class scope ALSO.  */
	| operator type_specifier_seq conversion_declarator
		{ $$ = grokoptypename ($2, $3); }
	| operator error
		{ $$ = ansi_opname[ERROR_MARK]; }
	;
/*
 *	Objective-C productions.
 */
 
/* records the type and storage class specs to use for processing
   the declarators that follow */
   
.setspecs: /* empty */
		{ current_declspecs = $<ttype>0;
		  $<itype>$ = suspend_momentary (); }
	;

objcdef:
	  classdef
	| classdecl
	| protocoldecl
	| aliasdecl
	| protocoldef
	| methoddef
	| END
		{
		  if (objc_implementation_context)
                    {
		    finish_class(objc_implementation_context);    
		    objc_ivar_chain = NULL_TREE;
		    objc_implementation_context = NULL_TREE;
		    }
		  else
		    warning("`@end' must appear in an implementation context");
		}
	;

/* A nonempty list of identifiers.  */
identifier_list:
	  identifier
		{ $$ = build_tree_list (NULL_TREE, $1); }
	| identifier_list ',' identifier
		{ $$ = chainon ($1, build_tree_list (NULL_TREE, $3)); }
	;

classdecl:
	  CLASS	identifier_list ';'
		{
		  objc_declare_class ($2);
		}

protocoldecl:
	  PROTOCOL identifier_list ';'
		{
		  objc_declare_protocols ($2);
		}

aliasdecl:
	  ALIAS identifier identifier ';'
		{
		  objc_declare_alias ($2, $3);
		}

/* This is necessary for living in this c++ parser */
identifier_colon:
	  identifier ':'
		{ $$ = $1 }
	| PTYPENAME
	;

classdef:
	  INTERFACE identifier protocolrefs '{'
		{
		  objc_interface_context = objc_ivar_context
		    = start_class (CLASS_INTERFACE_TYPE, $2, NULL_TREE, $3);
                  objc_public_flag = 0;
		}
	  ivar_decl_list '}'
		{
                  continue_class (objc_interface_context);
		}
	  methodprotolist
	  END
		{
		  finish_class (objc_interface_context);
		  objc_interface_context = NULL_TREE;
		}

	| INTERFACE identifier protocolrefs
		{
		  objc_interface_context
		    = start_class (CLASS_INTERFACE_TYPE, $2, NULL_TREE, $3);
                  continue_class (objc_interface_context);
		}
	  methodprotolist
	  END
		{ 
		  finish_class(objc_interface_context);    
		  objc_interface_context = NULL_TREE; 
		}

	| INTERFACE identifier_colon identifier protocolrefs '{'
		{
		  objc_interface_context = objc_ivar_context
		    = start_class (CLASS_INTERFACE_TYPE, $2, $3, $4);
                  objc_public_flag = 0;
		}
	  ivar_decl_list '}'
		{
                  continue_class (objc_interface_context);
		}
	  methodprotolist
	  END
		{ 
		  finish_class(objc_interface_context);    
		  objc_interface_context = NULL_TREE; 
		}

	| INTERFACE identifier_colon identifier protocolrefs
		{
		  objc_interface_context
		    = start_class (CLASS_INTERFACE_TYPE, $2, $3, $4);
                  continue_class (objc_interface_context);
		}
	  methodprotolist
	  END
		{
		  finish_class (objc_interface_context);
		  objc_interface_context = NULL_TREE;
		}

	| IMPLEMENTATION identifier '{'
		{
		  objc_implementation_context = objc_ivar_context
		    = start_class (CLASS_IMPLEMENTATION_TYPE, $2, NULL_TREE, NULL_TREE);
                  objc_public_flag = 0;
		}
	  ivar_decl_list '}'
		{
                  objc_ivar_chain
		    = continue_class (objc_implementation_context);
		}

	| IMPLEMENTATION identifier
		{
		  objc_implementation_context
		    = start_class (CLASS_IMPLEMENTATION_TYPE, $2, NULL_TREE, NULL_TREE);
                  objc_ivar_chain
		    = continue_class (objc_implementation_context);
		}

	| IMPLEMENTATION identifier_colon identifier '{'
		{
		  objc_implementation_context = objc_ivar_context
		    = start_class (CLASS_IMPLEMENTATION_TYPE, $2, $3, NULL_TREE);
                  objc_public_flag = 0;
		}
	  ivar_decl_list '}'
		{
                  objc_ivar_chain
		    = continue_class (objc_implementation_context);
		}

	| IMPLEMENTATION identifier_colon identifier
		{
		  objc_implementation_context
		    = start_class (CLASS_IMPLEMENTATION_TYPE, $2, $3, NULL_TREE);
                  objc_ivar_chain
		    = continue_class (objc_implementation_context);
		}

	| INTERFACE identifier '(' identifier ')' protocolrefs
		{
		  objc_interface_context
		    = start_class (CATEGORY_INTERFACE_TYPE, $2, $4, $6);
                  continue_class (objc_interface_context);
		}
	  methodprotolist
	  END
		{
		  finish_class (objc_interface_context);
		  objc_interface_context = NULL_TREE;
		}

	| IMPLEMENTATION identifier '(' identifier ')'
		{
		  objc_implementation_context
		    = start_class (CATEGORY_IMPLEMENTATION_TYPE, $2, $4, NULL_TREE);
                  objc_ivar_chain
		    = continue_class (objc_implementation_context);
		}
	;

protocoldef:
	  PROTOCOL identifier protocolrefs
		{
		objc_interface_context = 
			start_protocol(PROTOCOL_INTERFACE_TYPE, $2, $3);
		}
	  methodprotolist
	  END
		{
		finish_protocol(objc_interface_context);
		objc_interface_context = NULL_TREE;
		}
	;

ivar_decl_list:   
          ivar_decl_list visibility_spec ivar_decls
        | ivar_decls
        ;

visibility_spec:
	  PUBLIC     { objc_public_flag = 1; }
	| PRIVATE    { objc_public_flag = 2; }
	| PROTECTED  { objc_public_flag = 0; }
	;

ivar_decls:
          /* empty */
		{ 
                  $$ = NULL_TREE; 
                }
	| ivar_decls ivar_decl ';'
	| ivar_decls ';'
		{ 
                  if (pedantic) 
		    warning ("extra semicolon in struct or union specified"); 
                }
	;

ivar_decl:
	typed_typespecs .setspecs ivars
	        { 
                  $$ = $3;
		  resume_momentary ($<itype>2);
                }
	| nonempty_type_quals .setspecs ivars
	        { 
                  $$ = $3;
		  resume_momentary ($<itype>2);
                }
	| error
		{ $$ = NULL_TREE; }
	;

ivars:
	  /* empty */
		{ $$ = NULL_TREE; }
	| ivar_declarator
	| ivars ',' ivar_declarator
	;

ivar_declarator:
	  declarator
		{ 
                $$ = add_instance_variable(objc_ivar_context, objc_public_flag,
                           $1, current_declspecs, NULL_TREE); 
                }
	| declarator ':' expr_no_commas
		{ 
                $$ = add_instance_variable(objc_ivar_context, objc_public_flag,
                           $1, current_declspecs, $3); 
                }
	| ':' expr_no_commas
		{ 
                $$ = add_instance_variable(objc_ivar_context, objc_public_flag,
                           NULL_TREE, current_declspecs, $2); 
                }
	;

methoddef:
	  '+' 
		{
		  if (objc_implementation_context)
		    objc_inherit_code = CLASS_METHOD_DECL;
                  else
		    fatal("Illegal method definition - must be in a class context.");
		}
	  methoddecl 
		{ 
		  add_class_method(objc_implementation_context,$3);
		  start_method_def ($3);
		  objc_method_context = $3;
		}
	  optarglist
		{
		  continue_method_def();
		}
	  compstmt_or_error
		{ 
		  finish_method_def (); 
		  objc_method_context = NULL_TREE; 
		}

	| '-' 
		{
		  if (objc_implementation_context)
		    objc_inherit_code = INSTANCE_METHOD_DECL;
                  else
		    fatal("Illegal method definition - must be in a class context.");
		}
	  methoddecl 
		{ 
		  add_instance_method(objc_implementation_context,$3);
		  start_method_def ($3); 
		  objc_method_context = $3;
		}
	  optarglist
		{
		  continue_method_def();
		}
	  compstmt_or_error
		{ 
		  finish_method_def (); 
		  objc_method_context = NULL_TREE; 
		}
	;

/* the reason for the strange actions in this rule
 is so that notype_initdecls when reached via datadef
 can find a valid list of type and sc specs in $0. */

methodprotolist:
	  /* empty  */
	| {$<ttype>$ = NULL_TREE; } methodprotolist2
	;

methodprotolist2:		 /* eliminates a shift/reduce conflict */
	  methodproto 
	| datadef
	| methodprotolist2 methodproto
	| methodprotolist2 {$<ttype>$ = NULL_TREE; } datadef
	;

semi_or_error:
	  ';'
	| error
	;

methodproto:
	  '+' 
		{
		  objc_inherit_code = CLASS_METHOD_DECL;
		}
	  methoddecl 
		{ 
		  add_class_method(objc_interface_context,$3);
		}
	  semi_or_error

	| '-' 
		{
		  objc_inherit_code = INSTANCE_METHOD_DECL;
		}
	  methoddecl 
		{
		  add_instance_method(objc_interface_context,$3);
		}
	  semi_or_error
	;

start_method_typename:
	'('	{ remember_protocol_qualifiers (); }

end_method_typename:
	')'	{ forget_protocol_qualifiers (); }

methodtype:
	start_method_typename type_id end_method_typename
		{ $$ = $2; }

objc_return_type_mods:
	SCSPEC
		{ $$ = tree_cons (NULL_TREE, $1, NULL_TREE); }
	| objc_return_type_mods SCSPEC
		{ $$ = tree_cons (NULL_TREE, $2, $1); }
	;

methoddecl:
	start_method_typename type_id end_method_typename unaryselector
		{ 
		$$ = build_method_decl (objc_inherit_code, $2, $4, NULL_TREE, NULL_TREE); 
		}

	| start_method_typename objc_return_type_mods type_id
		end_method_typename unaryselector 
		{ 
		$$ = build_method_decl (objc_inherit_code, $2, $5, NULL_TREE, NULL_TREE); 
		}

	| unaryselector 
		{ 
		$$ = build_method_decl (objc_inherit_code, NULL_TREE, $1, NULL_TREE, NULL_TREE); 
		}

	| start_method_typename objc_return_type_mods type_id
		end_method_typename keywordselector optparmlist
		{ 
		$$ = build_method_decl (objc_inherit_code, $2, $3, $5, $6); 
		}

	| start_method_typename type_id end_method_typename keywordselector optparmlist
		{ 
		$$ = build_method_decl (objc_inherit_code, $2, $4, $5, NULL_TREE); 
		}

	| keywordselector optparmlist
		{ 
		$$ = build_method_decl (objc_inherit_code, NULL_TREE, $1, $2, NULL_TREE); 
		}
	;


/* "optarglist" assumes that start_method_def() has already been called...
   if it is not, the "xdecls" will not be placed in the proper scope */

optarglist:
	  /* empty */
	| ';' myxdecls
	;

/* to get around the following situation: "int foo(int a) int b; {}" that
   is synthesized when parsing "- a:a b:b; id c; id d; { ... }" */

myxdecls:
	  /* empty */
	| mydecls
	;

mydecls:
	mydecl
	| errstmt
	| mydecls mydecl
	| mydecl errstmt
	;

mydecl:
	typed_declspecs .setspecs myparms ';'
		{ resume_momentary ($<itype>2); }
	| typed_declspecs ';'
		{ shadow_tag ($1); }
	| declmods ';'
		{ warning ("empty declaration"); }
	;

/* this must be converted to live in the g++ world...snaroff */

myparms:	
	myparm
		{ objcplus_push_parm_decl ($1); }
	| myparms ',' myparm
		{ objcplus_push_parm_decl ($3); }
	;

/* A single parameter declaration or parameter type name,
   as found in a parmlist. DOES NOT ALLOW AN INITIALIZER OR ASMSPEC */

myparm:
	 notype_declarator
		{ $$ = build_tree_list (current_declspecs, $1)	; }
	| absdcl
		{ $$ = build_tree_list (current_declspecs, $1)	; }
	;

optparmlist:
	  /* empty */
		{ 
	    	  $$ = NULL_TREE; 
		}
	| ',' ELLIPSIS
		{
		  /* oh what a kludge! */
		  $$ = (tree)1;	
		}
	| ',' 
		{ 
		  pushlevel (0); 
		} 
	  parmlist	
		{ 
	  	  /* returns a tree list node generated by `get_parm_info()' */
		  $$ = $3; 
		  poplevel(0,0,0);
		}
	;

unaryselector:
	  selector
	;

keywordselector:
	  keyworddecl
	| keywordselector keyworddecl
		{ 
		  $$ = chainon($1, $2);
		}
	;

selector:
          IDENTIFIER
        | TYPENAME
	| OBJECTNAME
	| reservedword
	;

reservedword:
	  ENUM { $$ = get_identifier("enum"); }
	| AGGR 
          {
            if (yylval.ttype == class_type_node)
              $$ = get_identifier("class");
            else if (yylval.ttype == record_type_node)
              $$ = get_identifier("struct");
            else if (yylval.ttype == union_type_node)
              $$ = get_identifier("union");
	    else if (yylval.ttype == enum_type_node)
              $$ = get_identifier("enum");
            else
              abort ();
          }
	| IF { $$ = get_identifier("if"); }
	| ELSE { $$ = get_identifier("else"); }
	| WHILE { $$ = get_identifier("while"); }
	| DO { $$ = get_identifier("do"); }
	| FOR { $$ = get_identifier("for"); }
	| SWITCH { $$ = get_identifier("switch"); }
	| CASE { $$ = get_identifier("case"); }
	| DEFAULT { $$ = get_identifier("default"); }
	| BREAK { $$ = get_identifier("break"); }
	| CONTINUE { $$ = get_identifier("continue"); }
	| RETURN  { $$ = get_identifier("return"); }
	| GOTO { $$ = get_identifier("goto"); }
	| ASM_KEYWORD { $$ = get_identifier("asm"); }
        | SIZEOF { $$ = get_identifier("sizeof"); } 
	| TYPEOF { $$ = get_identifier("typeof"); }
	| ALIGNOF { $$ = get_identifier("alignof"); }
	| NEW { $$ = get_identifier("new"); }
	| DELETE { $$ = get_identifier("delete"); }
	| OPERATOR { $$ = get_identifier("operator"); }
	| VISSPEC {
		    if ($1 == access_private)
		      $$ = get_identifier ("private");
		    else if ($1 == access_public)
		      $$ = get_identifier ("public");
		    else if ($1 == access_protected)
		      $$ = get_identifier ("protected");
		    else
		      abort ();
		  }
        | SCSPEC   { $$ = yylval.ttype; }
        | TYPESPEC { $$ = yylval.ttype; }
	| OVERLOAD { $$ = get_identifier("overload"); }
	;

keyworddecl:
	  selector ':' methodtype identifier
		{ 
		  $$ = build_keyword_decl($1, $3, $4);
		}

	| selector ':' identifier
		{ 
		  $$ = build_keyword_decl($1, NULL_TREE, $3);
		}

	| ':' methodtype identifier
		{ 
		  $$ = build_keyword_decl(NULL_TREE, $2, $3);
		}

	| ':' identifier
		{ 
		  $$ = build_keyword_decl(NULL_TREE, NULL_TREE, $2);
		}
	;

messageargs:
	  selector
        | keywordarglist
	;

keywordarglist:
	  keywordarg
	| keywordarglist keywordarg
		{ 
		  $$ = chainon($1, $2);
		}
	;


keywordexpr:	
	  nonnull_exprlist
		{ 
		  if (TREE_CHAIN($1) == NULL_TREE)
		    /* just return the expr., remove a level of indirection */
		    $$ = TREE_VALUE($1);
                  else
		    /* we have a comma expr., we will collapse later */
		    $$ = $1;
		}
	;

keywordarg:
	  selector ':' keywordexpr
		{
		  $$ = build_tree_list($1, $3);
		}
	| ':' keywordexpr
		{
		  $$ = build_tree_list(NULL_TREE,$2);
		}
	;

receiver:
	  nonnull_exprlist
		{
		  $$ = build_x_compound_expr ($1);
		}
	| CLASSNAME
		{
		  $$ = get_class_reference($1);
		}
	;

objc_openbracket.expr:
	  '[' 
		{ objc_receiver_context = 1; objc_msg_context += 1; }
	  receiver
		{ objc_receiver_context = 0; $$ = $3; }
	;	

objc_closebracket:
	  ']'
		{ objc_msg_context -= 1; }
	;

objcmessageexpr:
	  objc_openbracket.expr
 	    messageargs 
	  objc_closebracket
		{
		  $$ = build_tree_list($1,$2);
                  TREE_TYPE ($$) = NULL_TREE;
		}
   /*
	| CLASSNAME SCOPE '[' 
		{ objc_receiver_context = 1; }
	  receiver
		{ objc_receiver_context = 0; }
	  messageargs ']'
		{
		  $$ = build_tree_list($5,$7);
                  TREE_TYPE ($$) = get_class_reference ($1);
		}
    */
	;

selectorarg:
	  selector
        | keywordnamelist
	;

keywordnamelist:
	  keywordname
	| keywordnamelist keywordname
		{ 
		  $$ = chainon ($1, $2);
		}
	;

keywordname:
	  selector ':' 
		{
		  $$ = build_tree_list ($1, NULL_TREE);
		}
	| ':'
		{
		  $$ = build_tree_list (NULL_TREE,NULL_TREE);
		}
	| SCOPE
	  	{
		  $$ = chainon (build_tree_list (NULL_TREE,NULL_TREE),
		       	        build_tree_list (NULL_TREE,NULL_TREE));
		}
	;

objcselectorexpr:
	  SELECTOR '(' selectorarg ')'
		{
		  $$ = $3;
		}
	;

objcprotocolexpr:
	  PROTOCOL '(' identifier ')'
		{
		  $$ = $3;
		}
	;

/* extension to support C-structures in the archiver */

objcencodeexpr:
	  ENCODE '(' type_id ')'
		{
		  $$ = groktypename($3);
		}
	;

%%

#ifdef SPEW_DEBUG
const char *
debug_yytranslate (value)
    int value;
{
  return yytname[YYTRANSLATE (value)];
}

#endif