[BACK]Return to cgram.y CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / usr.bin / xlint / lint1

File: [cvs.NetBSD.org] / src / usr.bin / xlint / lint1 / cgram.y (download)

Revision 1.160, Sun Jan 31 12:44:34 2021 UTC (3 years, 2 months ago) by rillig
Branch: MAIN
Changes since 1.159: +4 -4 lines

lint: don't warn about constant condition in 'do { } while (0)'

%{
/* $NetBSD: cgram.y,v 1.160 2021/01/31 12:44:34 rillig Exp $ */

/*
 * Copyright (c) 1996 Christopher G. Demetriou.  All Rights Reserved.
 * Copyright (c) 1994, 1995 Jochen Pohl
 * All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Jochen Pohl for
 *	The NetBSD Project.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
#if defined(__RCSID) && !defined(lint)
__RCSID("$NetBSD: cgram.y,v 1.160 2021/01/31 12:44:34 rillig Exp $");
#endif

#include <limits.h>
#include <stdlib.h>
#include <string.h>

#include "lint1.h"

extern char *yytext;

/*
 * Contains the level of current declaration, used for symbol table entries.
 * 0 is the top-level, > 0 is inside a function body.
 */
int	blklev;

/*
 * level for memory allocation. Normally the same as blklev.
 * An exception is the declaration of arguments in prototypes. Memory
 * for these can't be freed after the declaration, but symbols must
 * be removed from the symbol table after the declaration.
 */
int	mblklev;

/*
 * Save the no-warns state and restore it to avoid the problem where
 * if (expr) { stmt } / * NOLINT * / stmt;
 */
static int olwarn = LWARN_BAD;

static	int	toicon(tnode_t *, int);
static	void	idecl(sym_t *, int, sbuf_t *);
static	void	ignore_up_to_rparen(void);
static	sym_t	*symbolrename(sym_t *, sbuf_t *);


#ifdef DEBUG
static void
CLEAR_WARN_FLAGS(const char *file, size_t line)
{
	printf("%s, %d: clear flags %s %zu\n", curr_pos.p_file,
	    curr_pos.p_line, file, line);
	clear_warn_flags();
	olwarn = LWARN_BAD;
}

static void
SAVE_WARN_FLAGS(const char *file, size_t line)
{
	lint_assert(olwarn == LWARN_BAD);
	printf("%s, %d: save flags %s %zu = %d\n", curr_pos.p_file,
	    curr_pos.p_line, file, line, lwarn);
	olwarn = lwarn;
}

static void
RESTORE_WARN_FLAGS(const char *file, size_t line)
{
	if (olwarn != LWARN_BAD) {
		lwarn = olwarn;
		printf("%s, %d: restore flags %s %zu = %d\n", curr_pos.p_file,
		    curr_pos.p_line, file, line, lwarn);
		olwarn = LWARN_BAD;
	} else
		CLEAR_WARN_FLAGS(file, line);
}
#define cgram_debug(fmt, args...) printf("cgram_debug: " fmt "\n", ##args)
#else
#define CLEAR_WARN_FLAGS(f, l) clear_warn_flags(), olwarn = LWARN_BAD
#define SAVE_WARN_FLAGS(f, l)	olwarn = lwarn
#define RESTORE_WARN_FLAGS(f, l) \
	(void)(olwarn == LWARN_BAD ? (clear_warn_flags(), 0) : (lwarn = olwarn))
#define cgram_debug(fmt, args...) (void)0
#endif

/* unbind the anonymous struct members from the struct */
static void
anonymize(sym_t *s)
{
	for ( ; s; s = s->s_next)
		s->s_styp = NULL;
}
%}

%expect 138

%union {
	int	y_int;
	val_t	*y_val;
	sbuf_t	*y_sb;
	sym_t	*y_sym;
	op_t	y_op;
	scl_t	y_scl;
	tspec_t	y_tspec;
	tqual_t	y_tqual;
	type_t	*y_type;
	tnode_t	*y_tnode;
	range_t	y_range;
	strg_t	*y_string;
	pqinf_t	*y_pqinf;
};

%token			T_LBRACE T_RBRACE T_LBRACK T_RBRACK T_LPAREN T_RPAREN
%token	<y_op>		T_MEMBACC
%token	<y_op>		T_UNARY
%token	<y_op>		T_INCDEC
%token			T_SIZEOF
%token			T_BUILTIN_OFFSETOF
%token			T_TYPEOF
%token			T_EXTENSION
%token			T_ALIGNOF
%token	<y_op>		T_ASTERISK
%token	<y_op>		T_MULTIPLICATIVE
%token	<y_op>		T_ADDITIVE
%token	<y_op>		T_SHIFT
%token	<y_op>		T_RELATIONAL
%token	<y_op>		T_EQUALITY
%token	<y_op>		T_AMPER
%token	<y_op>		T_XOR
%token	<y_op>		T_BITOR
%token	<y_op>		T_LOGAND
%token	<y_op>		T_LOGOR
%token			T_QUEST
%token			T_COLON
%token	<y_op>		T_ASSIGN
%token	<y_op>		T_OPASSIGN
%token			T_COMMA
%token			T_SEMI
%token			T_ELLIPSIS
%token			T_REAL
%token			T_IMAG
%token			T_GENERIC
%token			T_NORETURN

/* storage classes (extern, static, auto, register and typedef) */
%token	<y_scl>		T_SCLASS

/*
 * predefined type keywords (char, int, short, long, unsigned, signed,
 * float, double, void); see T_TYPENAME
 */
%token	<y_tspec>	T_TYPE

/* qualifiers (const, volatile, restrict, _Thread_local) */
%token	<y_tqual>	T_QUAL

/* struct or union */
%token	<y_tspec>	T_STRUCT_OR_UNION

/* remaining keywords */
%token			T_ASM
%token			T_BREAK
%token			T_CASE
%token			T_CONTINUE
%token			T_DEFAULT
%token			T_DO
%token			T_ELSE
%token			T_ENUM
%token			T_FOR
%token			T_GOTO
%token			T_IF
%token			T_PACKED
%token			T_RETURN
%token			T_SWITCH
%token			T_SYMBOLRENAME
%token			T_WHILE
/* Type Attributes */
%token <y_type>		T_ATTRIBUTE
%token <y_type>		T_AT_ALIAS
%token <y_type>		T_AT_ALIGNED
%token <y_type>		T_AT_ALLOC_SIZE
%token <y_type>		T_AT_ALWAYS_INLINE
%token <y_type>		T_AT_BOUNDED
%token <y_type>		T_AT_BUFFER
%token <y_type>		T_AT_COLD
%token <y_type>		T_AT_CONSTRUCTOR
%token <y_type>		T_AT_DEPRECATED
%token <y_type>		T_AT_DESTRUCTOR
%token <y_type>		T_AT_FORMAT
%token <y_type>		T_AT_FORMAT_ARG
%token <y_type>		T_AT_FORMAT_GNU_PRINTF
%token <y_type>		T_AT_FORMAT_PRINTF
%token <y_type>		T_AT_FORMAT_SCANF
%token <y_type>		T_AT_FORMAT_STRFMON
%token <y_type>		T_AT_FORMAT_STRFTIME
%token <y_type>		T_AT_FORMAT_SYSLOG
%token <y_type>		T_AT_GNU_INLINE
%token <y_type>		T_AT_MALLOC
%token <y_type>		T_AT_MAY_ALIAS
%token <y_type>		T_AT_MINBYTES
%token <y_type>		T_AT_MODE
%token <y_type>		T_AT_NOINLINE
%token <y_type>		T_AT_NONNULL
%token <y_type>		T_AT_NORETURN
%token <y_type>		T_AT_NOTHROW
%token <y_type>		T_AT_NO_INSTRUMENT_FUNCTION
%token <y_type>		T_AT_OPTIMIZE
%token <y_type>		T_AT_PACKED
%token <y_type>		T_AT_PCS
%token <y_type>		T_AT_PURE
%token <y_type>		T_AT_RETURNS_TWICE
%token <y_type>		T_AT_SECTION
%token <y_type>		T_AT_SENTINEL
%token <y_type>		T_AT_STRING
%token <y_type>		T_AT_TLS_MODEL
%token <y_type>		T_AT_TUNION
%token <y_type>		T_AT_UNUSED
%token <y_type>		T_AT_USED
%token <y_type>		T_AT_VISIBILITY
%token <y_type>		T_AT_WARN_UNUSED_RESULT
%token <y_type>		T_AT_WEAK

%left	T_COMMA
%right	T_ASSIGN T_OPASSIGN
%right	T_QUEST T_COLON
%left	T_LOGOR
%left	T_LOGAND
%left	T_BITOR
%left	T_XOR
%left	T_AMPER
%left	T_EQUALITY
%left	T_RELATIONAL
%left	T_SHIFT
%left	T_ADDITIVE
%left	T_ASTERISK T_MULTIPLICATIVE
%right	T_UNARY T_INCDEC T_SIZEOF T_BUILTIN_OFFSETOF T_ALIGNOF T_REAL T_IMAG
%left	T_LPAREN T_LBRACK T_MEMBACC

%token	<y_sb>		T_NAME
%token	<y_sb>		T_TYPENAME
%token	<y_val>		T_CON
%token	<y_string>	T_STRING

%type	<y_sym>		func_decl
%type	<y_sym>		notype_decl
%type	<y_sym>		type_decl
%type	<y_type>	typespec
%type	<y_type>	clrtyp_typespec
%type	<y_type>	notype_typespec
%type	<y_type>	struct_spec
%type	<y_type>	enum_spec
%type	<y_type>	type_attribute
%type	<y_sym>		struct_tag
%type	<y_sym>		enum_tag
%type	<y_tspec>	struct
%type	<y_sym>		struct_declaration
%type	<y_sb>		identifier
%type	<y_sym>		member_declaration_list_with_rbrace
%type	<y_sym>		member_declaration_list
%type	<y_sym>		member_declaration
%type	<y_sym>		notype_member_decls
%type	<y_sym>		type_member_decls
%type	<y_sym>		notype_member_decl
%type	<y_sym>		type_member_decl
%type	<y_tnode>	constant
%type	<y_sym>		enum_declaration
%type	<y_sym>		enums_with_opt_comma
%type	<y_sym>		enums
%type	<y_sym>		enumerator
%type	<y_sym>		enumeration_constant
%type	<y_sym>		notype_direct_decl
%type	<y_sym>		type_direct_decl
%type	<y_pqinf>	pointer
%type	<y_pqinf>	asterisk
%type	<y_sym>		param_decl
%type	<y_sym>		param_list
%type	<y_sym>		abstract_decl_param_list
%type	<y_sym>		direct_param_decl
%type	<y_sym>		notype_param_decl
%type	<y_sym>		direct_notype_param_decl
%type	<y_pqinf>	type_qualifier_list
%type	<y_pqinf>	type_qualifier
%type	<y_sym>		identifier_list
%type	<y_sym>		abstract_decl
%type	<y_sym>		direct_abstract_decl
%type	<y_sym>		vararg_parameter_type_list
%type	<y_sym>		parameter_type_list
%type	<y_sym>		parameter_declaration
%type	<y_tnode>	expr
%type	<y_tnode>	expr_statement_val
%type	<y_tnode>	expr_statement_list
%type	<y_tnode>	term
%type	<y_tnode>	generic_expr
%type	<y_tnode>	func_arg_list
%type	<y_op>		point_or_arrow
%type	<y_type>	type_name
%type	<y_sym>		abstract_declaration
%type	<y_tnode>	do_while_expr
%type	<y_tnode>	opt_expr
%type	<y_string>	string
%type	<y_string>	string2
%type	<y_sb>		opt_asm_or_symbolrename
%type	<y_range>	range
%type	<y_range>	low_range


%%

program:
	  /* empty */ {
		if (sflag) {
			/* empty translation unit */
			error(272);
		} else if (!tflag) {
			/* empty translation unit */
			warning(272);
		}
	  }
	| translation_unit
	;

translation_unit:		/* C99 6.9 */
	  external_declaration
	| translation_unit external_declaration
	;

external_declaration:		/* C99 6.9 */
	  asm_statement
	| function_definition {
		global_clean_up_decl(0);
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
	  }
	| data_def {
		global_clean_up_decl(0);
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
	  }
	;

data_def:
	  T_SEMI {
		if (sflag) {
			/* empty declaration */
			error(0);
		} else if (!tflag) {
			/* empty declaration */
			warning(0);
		}
	  }
	| clrtyp deftyp notype_init_decls T_SEMI {
		if (sflag) {
			/* old style declaration; add 'int' */
			error(1);
		} else if (!tflag) {
			/* old style declaration; add 'int' */
			warning(1);
		}
	  }
	| declmods deftyp T_SEMI {
		if (dcs->d_scl == TYPEDEF) {
			/* typedef declares no type name */
			warning(72);
		} else {
			/* empty declaration */
			warning(2);
		}
	  }
	| declmods deftyp notype_init_decls T_SEMI
	| declspecs deftyp T_SEMI {
		if (dcs->d_scl == TYPEDEF) {
			/* typedef declares no type name */
			warning(72);
		} else if (!dcs->d_nedecl) {
			/* empty declaration */
			warning(2);
		}
	  }
	| declspecs deftyp type_init_decls T_SEMI
	| error T_SEMI {
		global_clean_up();
	  }
	| error T_RBRACE {
		global_clean_up();
	  }
	;

function_definition:		/* C99 6.9.1 */
	  func_decl {
		if ($1->s_type->t_tspec != FUNC) {
			/* syntax error '%s' */
			error(249, yytext);
			YYERROR;
		}
		if ($1->s_type->t_typedef) {
			/* ()-less function definition */
			error(64);
			YYERROR;
		}
		funcdef($1);
		blklev++;
		pushdecl(ARG);
		if (lwarn == LWARN_NONE)
			$1->s_used = true;
	  } arg_declaration_list_opt {
		popdecl();
		blklev--;
		check_func_lint_directives();
		check_func_old_style_arguments();
		pushctrl(0);
	  } compound_statement {
		funcend();
		popctrl(0);
	  }
	;

func_decl:
	  clrtyp deftyp notype_decl {
		$$ = $3;
	  }
	| declmods deftyp notype_decl {
		$$ = $3;
	  }
	| declspecs deftyp type_decl {
		$$ = $3;
	  }
	;

arg_declaration_list_opt:
	  /* empty */
	| arg_declaration_list
	;

arg_declaration_list:
	  arg_declaration
	| arg_declaration_list arg_declaration
	/* XXX or better "arg_declaration error" ? */
	| error
	;

/*
 * "arg_declaration" is separated from "declaration" because it
 * needs other error handling.
 */

arg_declaration:
	  declmods deftyp T_SEMI {
		/* empty declaration */
		warning(2);
	  }
	| declmods deftyp notype_init_decls T_SEMI
	| declspecs deftyp T_SEMI {
		if (!dcs->d_nedecl) {
			/* empty declaration */
			warning(2);
		} else {
			/* '%s' declared in argument declaration list */
			warning(3, type_name(dcs->d_type));
		}
	  }
	| declspecs deftyp type_init_decls T_SEMI {
		if (dcs->d_nedecl) {
			/* '%s' declared in argument declaration list */
			warning(3, type_name(dcs->d_type));
		}
	  }
	| declmods error
	| declspecs error
	;

declaration:
	  declmods deftyp T_SEMI {
		if (dcs->d_scl == TYPEDEF) {
			/* typedef declares no type name */
			warning(72);
		} else {
			/* empty declaration */
			warning(2);
		}
	  }
	| declmods deftyp notype_init_decls T_SEMI
	| declspecs deftyp T_SEMI {
		if (dcs->d_scl == TYPEDEF) {
			/* typedef declares no type name */
			warning(72);
		} else if (!dcs->d_nedecl) {
			/* empty declaration */
			warning(2);
		}
	  }
	| declspecs deftyp type_init_decls T_SEMI
	| error T_SEMI
	;

type_attribute_format_type:
	  T_AT_FORMAT_GNU_PRINTF
	| T_AT_FORMAT_PRINTF
	| T_AT_FORMAT_SCANF
	| T_AT_FORMAT_STRFMON
	| T_AT_FORMAT_STRFTIME
	| T_AT_FORMAT_SYSLOG
	;

type_attribute_bounded_type:
	  T_AT_MINBYTES
	| T_AT_STRING
	| T_AT_BUFFER
	;


type_attribute_spec:
	  /* empty */
	| T_AT_DEPRECATED T_LPAREN string T_RPAREN
	| T_AT_DEPRECATED
	| T_AT_ALIGNED T_LPAREN constant T_RPAREN
	| T_AT_ALLOC_SIZE T_LPAREN constant T_COMMA constant T_RPAREN
	| T_AT_ALLOC_SIZE T_LPAREN constant T_RPAREN
	| T_AT_BOUNDED T_LPAREN type_attribute_bounded_type
	  T_COMMA constant T_COMMA constant T_RPAREN
	| T_AT_SENTINEL T_LPAREN constant T_RPAREN
	| T_AT_SENTINEL
	| T_AT_FORMAT_ARG T_LPAREN constant T_RPAREN
	| T_AT_NONNULL T_LPAREN constant T_RPAREN
	| T_AT_MODE T_LPAREN T_NAME T_RPAREN
	| T_AT_ALIAS T_LPAREN string T_RPAREN
	| T_AT_OPTIMIZE T_LPAREN string T_RPAREN
	| T_AT_PCS T_LPAREN string T_RPAREN
	| T_AT_SECTION T_LPAREN string T_RPAREN
	| T_AT_TLS_MODEL T_LPAREN string T_RPAREN
	| T_AT_ALIGNED
	| T_AT_CONSTRUCTOR
	| T_AT_DESTRUCTOR
	| T_AT_MALLOC
	| T_AT_MAY_ALIAS
	| T_AT_NO_INSTRUMENT_FUNCTION
	| T_AT_NOINLINE
	| T_AT_NORETURN
	| T_AT_NOTHROW
	| T_AT_COLD
	| T_AT_RETURNS_TWICE
	| T_AT_PACKED {
		addpacked();
	  }
	| T_AT_PURE
	| T_AT_TUNION
	| T_AT_GNU_INLINE
	| T_AT_ALWAYS_INLINE
	| T_AT_FORMAT T_LPAREN type_attribute_format_type T_COMMA
	    constant T_COMMA constant T_RPAREN
	| T_AT_USED {
		add_attr_used();
	  }
	| T_AT_UNUSED {
		add_attr_used();
	  }
	| T_AT_WARN_UNUSED_RESULT
	| T_AT_WEAK
	| T_AT_VISIBILITY T_LPAREN constant T_RPAREN
	| T_QUAL {
		if ($1 != CONST)
			yyerror("Bad attribute");
	  }
	;

type_attribute_spec_list:
	  type_attribute_spec
	| type_attribute_spec_list T_COMMA type_attribute_spec
	;

type_attribute:
	  T_ATTRIBUTE T_LPAREN T_LPAREN {
	    attron = true;
	  } type_attribute_spec_list {
	    attron = false;
	  } T_RPAREN T_RPAREN
	| T_PACKED {
		addpacked();
	  }
	| T_NORETURN {
	  }
	;

type_attribute_list:
	  type_attribute
	| type_attribute_list type_attribute
	;

clrtyp:
	  /* empty */ {
		clrtyp();
	  }
	;

deftyp:
	  /* empty */ {
		deftyp();
	  }
	;

declspecs:
	  clrtyp_typespec {
		add_type($1);
	  }
	| declmods typespec {
		add_type($2);
	  }
	| type_attribute declspecs
	| declspecs declmod
	| declspecs notype_typespec {
		add_type($2);
	  }
	;

declmods:
	  clrtyp T_QUAL {
		add_qualifier($2);
	  }
	| clrtyp T_SCLASS {
		add_storage_class($2);
	  }
	| declmods declmod
	;

declmod:
	  T_QUAL {
		add_qualifier($1);
	  }
	| T_SCLASS {
		add_storage_class($1);
	  }
	| type_attribute_list
	;

clrtyp_typespec:
	  clrtyp notype_typespec {
		$$ = $2;
	  }
	| T_TYPENAME clrtyp {
		$$ = getsym($1)->s_type;
	  }
	;

typespec:
	  notype_typespec {
		$$ = $1;
	  }
	| T_TYPENAME {
		$$ = getsym($1)->s_type;
	  }
	;

notype_typespec:
	  T_TYPE {
		$$ = gettyp($1);
	  }
	| T_TYPEOF term {
		$$ = $2->tn_type;
	  }
	| struct_spec {
		popdecl();
		$$ = $1;
	  }
	| enum_spec {
		popdecl();
		$$ = $1;
	  }
	;

struct_spec:
	  struct struct_tag {
		/*
		 * STDC requires that "struct a;" always introduces
		 * a new tag if "a" is not declared at current level
		 *
		 * yychar is valid because otherwise the parser would not
		 * have been able to decide if it must shift or reduce
		 */
		$$ = mktag($2, $1, 0, yychar == T_SEMI);
	  }
	| struct struct_tag {
		dcs->d_tagtyp = mktag($2, $1, 1, 0);
	  } struct_declaration {
		$$ = complete_tag_struct_or_union(dcs->d_tagtyp, $4);
	  }
	| struct {
		dcs->d_tagtyp = mktag(NULL, $1, 1, 0);
	  } struct_declaration {
		$$ = complete_tag_struct_or_union(dcs->d_tagtyp, $3);
	  }
	| struct error {
		symtyp = FVFT;
		$$ = gettyp(INT);
	  }
	;

struct:
	  struct type_attribute
	| T_STRUCT_OR_UNION {
		symtyp = FTAG;
		pushdecl($1 == STRUCT ? MOS : MOU);
		dcs->d_offset = 0;
		dcs->d_stralign = CHAR_SIZE;
		$$ = $1;
	  }
	;

struct_tag:
	  identifier {
		$$ = getsym($1);
	  }
	;

struct_declaration:
	  struct_decl_lbrace member_declaration_list_with_rbrace {
		$$ = $2;
	  }
	;

struct_decl_lbrace:
	  T_LBRACE {
		symtyp = FVFT;
	  }
	;

member_declaration_list_with_rbrace:
	  member_declaration_list T_SEMI T_RBRACE {
		$$ = $1;
	  }
	| member_declaration_list T_RBRACE {
		if (sflag) {
			/* syntax req. ';' after last struct/union member */
			error(66);
		} else {
			/* syntax req. ';' after last struct/union member */
			warning(66);
		}
		$$ = $1;
	  }
	| T_RBRACE {
		$$ = NULL;
	  }
	;

opt_type_attribute:
	  /* empty */
	| type_attribute
	;

member_declaration_list:
	  member_declaration {
		$$ = $1;
	  }
	| member_declaration_list T_SEMI member_declaration {
		$$ = lnklst($1, $3);
	  }
	;

member_declaration:
	  noclass_declmods deftyp {
		/* too late, i know, but getsym() compensates it */
		symtyp = FMEMBER;
	  } notype_member_decls opt_type_attribute {
		symtyp = FVFT;
		$$ = $4;
	  }
	| noclass_declspecs deftyp {
		symtyp = FMEMBER;
	  } type_member_decls opt_type_attribute {
		symtyp = FVFT;
		$$ = $4;
	  }
	| noclass_declmods deftyp opt_type_attribute {
		symtyp = FVFT;
		/* struct or union member must be named */
		if (!Sflag)
			/* anonymous struct/union members is a C9X feature */
			warning(49);
		/* add all the members of the anonymous struct/union */
		$$ = dcs->d_type->t_str->memb;
		anonymize($$);
	  }
	| noclass_declspecs deftyp opt_type_attribute {
		symtyp = FVFT;
		/* struct or union member must be named */
		if (!Sflag)
			/* anonymous struct/union members is a C9X feature */
			warning(49);
		$$ = dcs->d_type->t_str->memb;
		/* add all the members of the anonymous struct/union */
		anonymize($$);
	  }
	| error {
		symtyp = FVFT;
		$$ = NULL;
	  }
	;

noclass_declspecs:
	  clrtyp_typespec {
		add_type($1);
	  }
	| type_attribute noclass_declspecs
	| noclass_declmods typespec {
		add_type($2);
	  }
	| noclass_declspecs T_QUAL {
		add_qualifier($2);
	  }
	| noclass_declspecs notype_typespec {
		add_type($2);
	  }
	| noclass_declspecs type_attribute
	;

noclass_declmods:
	  clrtyp T_QUAL {
		add_qualifier($2);
	  }
	| noclass_declmods T_QUAL {
		add_qualifier($2);
	  }
	;

notype_member_decls:
	  notype_member_decl {
		$$ = declarator_1_struct_union($1);
	  }
	| notype_member_decls {
		symtyp = FMEMBER;
	  } T_COMMA type_member_decl {
		$$ = lnklst($1, declarator_1_struct_union($4));
	  }
	;

type_member_decls:
	  type_member_decl {
		$$ = declarator_1_struct_union($1);
	  }
	| type_member_decls {
		symtyp = FMEMBER;
	  } T_COMMA type_member_decl {
		$$ = lnklst($1, declarator_1_struct_union($4));
	  }
	;

notype_member_decl:
	  notype_decl {
		$$ = $1;
	  }
	| notype_decl T_COLON constant {
		$$ = bitfield($1, toicon($3, 1));
	  }
	| {
		symtyp = FVFT;
	  } T_COLON constant {
		$$ = bitfield(NULL, toicon($3, 1));
	  }
	;

type_member_decl:
	  type_decl {
		$$ = $1;
	  }
	| type_decl T_COLON constant {
		$$ = bitfield($1, toicon($3, 1));
	  }
	| {
		symtyp = FVFT;
	  } T_COLON constant {
		$$ = bitfield(NULL, toicon($3, 1));
	  }
	;

enum_spec:
	  enum enum_tag {
		$$ = mktag($2, ENUM, 0, 0);
	  }
	| enum enum_tag {
		dcs->d_tagtyp = mktag($2, ENUM, 1, 0);
	  } enum_declaration {
		$$ = complete_tag_enum(dcs->d_tagtyp, $4);
	  }
	| enum {
		dcs->d_tagtyp = mktag(NULL, ENUM, 1, 0);
	  } enum_declaration {
		$$ = complete_tag_enum(dcs->d_tagtyp, $3);
	  }
	| enum error {
		symtyp = FVFT;
		$$ = gettyp(INT);
	  }
	;

enum:
	  T_ENUM {
		symtyp = FTAG;
		pushdecl(CTCONST);
	  }
	;

enum_tag:
	  identifier {
		$$ = getsym($1);
	  }
	;

enum_declaration:
	  enum_decl_lbrace enums_with_opt_comma T_RBRACE {
		$$ = $2;
	  }
	;

enum_decl_lbrace:
	  T_LBRACE {
		symtyp = FVFT;
		enumval = 0;
	  }
	;

enums_with_opt_comma:
	  enums {
		$$ = $1;
	  }
	| enums T_COMMA {
		if (sflag) {
			/* trailing ',' prohibited in enum declaration */
			error(54);
		} else {
			/* trailing ',' prohibited in enum declaration */
			c99ism(54);
		}
		$$ = $1;
	  }
	;

enums:
	  enumerator {
		$$ = $1;
	  }
	| enums T_COMMA enumerator {
		$$ = lnklst($1, $3);
	  }
	| error {
		$$ = NULL;
	  }
	;

enumerator:
	  enumeration_constant {
		$$ = enumeration_constant($1, enumval, 1);
	  }
	| enumeration_constant T_ASSIGN constant {
		$$ = enumeration_constant($1, toicon($3, 1), 0);
	  }
	;

enumeration_constant:		/* C99 6.4.4.3 */
	  identifier {
		$$ = getsym($1);
	  }
	;


notype_init_decls:
	  notype_init_decl
	| notype_init_decls T_COMMA type_init_decl
	;

type_init_decls:
	  type_init_decl
	| type_init_decls T_COMMA type_init_decl
	;

notype_init_decl:
	  notype_decl opt_asm_or_symbolrename {
		idecl($1, 0, $2);
		check_size($1);
	  }
	| notype_decl opt_asm_or_symbolrename {
		idecl($1, 1, $2);
	  } T_ASSIGN initializer {
		check_size($1);
	  }
	;

type_init_decl:
	  type_decl opt_asm_or_symbolrename {
		idecl($1, 0, $2);
		check_size($1);
	  }
	| type_decl opt_asm_or_symbolrename {
		idecl($1, 1, $2);
	  } T_ASSIGN initializer {
		check_size($1);
	  }
	;

notype_decl:
	  notype_direct_decl {
		$$ = $1;
	  }
	| pointer notype_direct_decl {
		$$ = add_pointer($2, $1);
	  }
	;

notype_direct_decl:
	  T_NAME {
		$$ = declarator_name(getsym($1));
	  }
	| T_LPAREN type_decl T_RPAREN {
		$$ = $2;
	  }
	| type_attribute notype_direct_decl {
		$$ = $2;
	  }
	| notype_direct_decl T_LBRACK T_RBRACK {
		$$ = add_array($1, 0, 0);
	  }
	| notype_direct_decl T_LBRACK constant T_RBRACK {
		$$ = add_array($1, 1, toicon($3, 0));
	  }
	| notype_direct_decl param_list opt_asm_or_symbolrename {
		$$ = add_function(symbolrename($1, $3), $2);
		popdecl();
		blklev--;
	  }
	| notype_direct_decl type_attribute_list
	;

type_decl:
	  type_direct_decl {
		$$ = $1;
	  }
	| pointer type_direct_decl {
		$$ = add_pointer($2, $1);
	  }
	;

type_direct_decl:
	  identifier {
		$$ = declarator_name(getsym($1));
	  }
	| T_LPAREN type_decl T_RPAREN {
		$$ = $2;
	  }
	| type_attribute type_direct_decl {
		$$ = $2;
	  }
	| type_direct_decl T_LBRACK T_RBRACK {
		$$ = add_array($1, 0, 0);
	  }
	| type_direct_decl T_LBRACK constant T_RBRACK {
		$$ = add_array($1, 1, toicon($3, 0));
	  }
	| type_direct_decl param_list opt_asm_or_symbolrename {
		$$ = add_function(symbolrename($1, $3), $2);
		popdecl();
		blklev--;
	  }
	| type_direct_decl type_attribute_list
	;

/*
 * param_decl and notype_param_decl exist to avoid a conflict in
 * argument lists. A typename enclosed in parens should always be
 * treated as a typename, not an argument.
 * "typedef int a; f(int (a));" is  "typedef int a; f(int foo(a));"
 *				not "typedef int a; f(int a);"
 */
param_decl:
	  direct_param_decl {
		$$ = $1;
	  }
	| pointer direct_param_decl {
		$$ = add_pointer($2, $1);
	  }
	;

direct_param_decl:
	  identifier type_attribute_list {
		$$ = declarator_name(getsym($1));
	  }
	| identifier {
		$$ = declarator_name(getsym($1));
	  }
	| T_LPAREN notype_param_decl T_RPAREN {
		$$ = $2;
	  }
	| direct_param_decl T_LBRACK T_RBRACK {
		$$ = add_array($1, 0, 0);
	  }
	| direct_param_decl T_LBRACK constant T_RBRACK {
		$$ = add_array($1, 1, toicon($3, 0));
	  }
	| direct_param_decl param_list opt_asm_or_symbolrename {
		$$ = add_function(symbolrename($1, $3), $2);
		popdecl();
		blklev--;
	  }
	;

notype_param_decl:
	  direct_notype_param_decl {
		$$ = $1;
	  }
	| pointer direct_notype_param_decl {
		$$ = add_pointer($2, $1);
	  }
	;

direct_notype_param_decl:
	  identifier {
		$$ = declarator_name(getsym($1));
	  }
	| T_LPAREN notype_param_decl T_RPAREN {
		$$ = $2;
	  }
	| direct_notype_param_decl T_LBRACK T_RBRACK {
		$$ = add_array($1, 0, 0);
	  }
	| direct_notype_param_decl T_LBRACK constant T_RBRACK {
		$$ = add_array($1, 1, toicon($3, 0));
	  }
	| direct_notype_param_decl param_list opt_asm_or_symbolrename {
		$$ = add_function(symbolrename($1, $3), $2);
		popdecl();
		blklev--;
	  }
	;

pointer:
	  asterisk {
		$$ = $1;
	  }
	| asterisk type_qualifier_list {
		$$ = merge_pointers_and_qualifiers($1, $2);
	  }
	| asterisk pointer {
		$$ = merge_pointers_and_qualifiers($1, $2);
	  }
	| asterisk type_qualifier_list pointer {
		$$ = merge_pointers_and_qualifiers($1, $2);
		$$ = merge_pointers_and_qualifiers($$, $3);
	  }
	;

asterisk:
	  T_ASTERISK {
		$$ = xcalloc(1, sizeof (pqinf_t));
		$$->p_pcnt = 1;
	  }
	;

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

type_qualifier:
	  T_QUAL {
		$$ = xcalloc(1, sizeof (pqinf_t));
		if ($1 == CONST) {
			$$->p_const = true;
		} else if ($1 == VOLATILE) {
			$$->p_volatile = true;
		} else {
			lint_assert($1 == RESTRICT || $1 == THREAD);
		}
	  }
	;

param_list:
	  id_list_lparn identifier_list T_RPAREN {
		$$ = $2;
	  }
	| abstract_decl_param_list {
		$$ = $1;
	  }
	;

id_list_lparn:
	  T_LPAREN {
		blklev++;
		pushdecl(PARG);
	  }
	;

identifier_list:
	  T_NAME {
		$$ = old_style_function_name(getsym($1));
	  }
	| identifier_list T_COMMA T_NAME {
		$$ = lnklst($1, old_style_function_name(getsym($3)));
	  }
	| identifier_list error {
		$$ = $1;
	  }
	;

abstract_decl_param_list:
	  abstract_decl_lparn T_RPAREN {
		$$ = NULL;
	  }
	| abstract_decl_lparn vararg_parameter_type_list T_RPAREN {
		dcs->d_proto = true;
		$$ = $2;
	  }
	| abstract_decl_lparn error T_RPAREN {
		$$ = NULL;
	  }
	;

abstract_decl_lparn:
	  T_LPAREN {
		blklev++;
		pushdecl(PARG);
	  }
	;

vararg_parameter_type_list:
	  parameter_type_list {
		$$ = $1;
	  }
	| parameter_type_list T_COMMA T_ELLIPSIS {
		dcs->d_vararg = true;
		$$ = $1;
	  }
	| T_ELLIPSIS {
		if (sflag) {
			/* ANSI C requires formal parameter before '...' */
			error(84);
		} else if (!tflag) {
			/* ANSI C requires formal parameter before '...' */
			warning(84);
		}
		dcs->d_vararg = true;
		$$ = NULL;
	  }
	;

parameter_type_list:
	  parameter_declaration {
		$$ = $1;
	  }
	| parameter_type_list T_COMMA parameter_declaration {
		$$ = lnklst($1, $3);
	  }
	;

parameter_declaration:
	  declmods deftyp {
		$$ = declare_argument(abstract_name(), 0);
	  }
	| declspecs deftyp {
		$$ = declare_argument(abstract_name(), 0);
	  }
	| declmods deftyp notype_param_decl {
		$$ = declare_argument($3, 0);
	  }
	/*
	 * param_decl is needed because of following conflict:
	 * "typedef int a; f(int (a));" could be parsed as
	 * "function with argument a of type int", or
	 * "function with an abstract argument of type function".
	 * This grammar realizes the second case.
	 */
	| declspecs deftyp param_decl {
		$$ = declare_argument($3, 0);
	  }
	| declmods deftyp abstract_decl {
		$$ = declare_argument($3, 0);
	  }
	| declspecs deftyp abstract_decl {
		$$ = declare_argument($3, 0);
	  }
	;

opt_asm_or_symbolrename:		/* expect only one */
	  /* empty */ {
		$$ = NULL;
	  }
	| T_ASM T_LPAREN T_STRING T_RPAREN {
		freeyyv(&$3, T_STRING);
		$$ = NULL;
	  }
	| T_SYMBOLRENAME T_LPAREN T_NAME T_RPAREN {
		$$ = $3;
	  }
	;

initializer:
	  {
		cgram_debug("begin initializer");
	  } init_assign_expr {
		cgram_debug("end initializer");
	  }
	;

init_assign_expr:
	| init_by_name init_base_expr	%prec T_COMMA
	| init_base_expr

init_base_expr:
	  expr				%prec T_COMMA {
		mkinit($1);
	  }
	| init_lbrace init_rbrace
	| init_lbrace init_expr_list init_rbrace
	| init_lbrace init_expr_list T_COMMA init_rbrace
	| error
	;

init_expr_list:
	  init_assign_expr		%prec T_COMMA
	| init_expr_list T_COMMA init_assign_expr
	;

low_range:
	  constant T_ELLIPSIS {
		$$.lo = toicon($1, 1);
	  }
	;
range:
	  constant {
		$$.lo = toicon($1, 1);
		$$.hi = $$.lo + 1;
	  }
	| low_range constant {
		$$.lo = $1.lo;
		$$.hi = toicon($2, 1);
	  }
	;

init_field:
	  T_LBRACK range T_RBRACK {
		if (!Sflag)
			/* array initializer with des.s is a C9X feature */
			warning(321);
	  }
	| point identifier {
		if (!Sflag)
			/* struct or union member name in initializer is ... */
			warning(313);
		push_member($2);
	  }
	;

init_field_list:
	  init_field
	| init_field_list init_field
	;

init_by_name:
	  init_field_list T_ASSIGN
	| identifier T_COLON {
		/* GCC style struct or union member name in initializer */
		gnuism(315);
		push_member($1);
	  }
	;

init_lbrace:
	  T_LBRACE {
		init_lbrace();
	  }
	;

init_rbrace:
	  T_RBRACE {
		init_rbrace();
	  }
	;

type_name:
	  {
		pushdecl(ABSTRACT);
	  } abstract_declaration {
		popdecl();
		$$ = $2->s_type;
	  }
	;

abstract_declaration:
	  noclass_declmods deftyp {
		$$ = declare_1_abstract(abstract_name());
	  }
	| noclass_declspecs deftyp {
		$$ = declare_1_abstract(abstract_name());
	  }
	| noclass_declmods deftyp abstract_decl {
		$$ = declare_1_abstract($3);
	  }
	| noclass_declspecs deftyp abstract_decl {
		$$ = declare_1_abstract($3);
	  }
	;

abstract_decl:
	  pointer {
		$$ = add_pointer(abstract_name(), $1);
	  }
	| direct_abstract_decl {
		$$ = $1;
	  }
	| pointer direct_abstract_decl {
		$$ = add_pointer($2, $1);
	  }
	| T_TYPEOF term {
		$$ = mktempsym($2->tn_type);
	  }
	;

direct_abstract_decl:
	  T_LPAREN abstract_decl T_RPAREN {
		$$ = $2;
	  }
	| T_LBRACK T_RBRACK {
		$$ = add_array(abstract_name(), 0, 0);
	  }
	| T_LBRACK constant T_RBRACK {
		$$ = add_array(abstract_name(), 1, toicon($2, 0));
	  }
	| type_attribute direct_abstract_decl {
		$$ = $2;
	  }
	| direct_abstract_decl T_LBRACK T_RBRACK {
		$$ = add_array($1, 0, 0);
	  }
	| direct_abstract_decl T_LBRACK constant T_RBRACK {
		$$ = add_array($1, 1, toicon($3, 0));
	  }
	| abstract_decl_param_list opt_asm_or_symbolrename {
		$$ = add_function(symbolrename(abstract_name(), $2), $1);
		popdecl();
		blklev--;
	  }
	| direct_abstract_decl abstract_decl_param_list opt_asm_or_symbolrename {
		$$ = add_function(symbolrename($1, $3), $2);
		popdecl();
		blklev--;
	  }
	| direct_abstract_decl type_attribute_list
	;

non_expr_statement:
	  labeled_statement
	| compound_statement
	| selection_statement
	| iteration_statement
	| jump_statement {
		ftflg = false;
	  }
	| asm_statement

statement:			/* C99 6.8 */
	  expr_statement
	| non_expr_statement
	;

labeled_statement:		/* C99 6.8.1 */
	  label statement
	;

label:
	  T_NAME T_COLON {
		symtyp = FLABEL;
		named_label(getsym($1));
	  }
	| T_CASE constant T_COLON {
		case_label($2);
		ftflg = true;
	  }
	| T_CASE constant T_ELLIPSIS constant T_COLON {
		/* XXX: We don't fill all cases */
		case_label($2);
		ftflg = true;
	  }
	| T_DEFAULT T_COLON {
		default_label();
		ftflg = true;
	  }
	;

statement_d_list:
	  statement_list
	| statement_d_list declaration_list statement_list {
		if (!Sflag)
			/* declarations after statements is a C9X feature */
			c99ism(327);
	  }
	;

compound_statement:		/* C99 6.8.2 */
	  compound_statement_lbrace compound_statement_rbrace
	| compound_statement_lbrace statement_d_list compound_statement_rbrace
	| compound_statement_lbrace declaration_list compound_statement_rbrace
	| compound_statement_lbrace declaration_list statement_d_list
	    compound_statement_rbrace
	;

compound_statement_lbrace:
	  T_LBRACE {
		blklev++;
		mblklev++;
		pushdecl(AUTO);
	  }
	;

compound_statement_rbrace:
	  T_RBRACE {
		popdecl();
		freeblk();
		mblklev--;
		blklev--;
		ftflg = false;
	  }
	;

statement_list:
	  statement
	| statement_list statement {
		RESTORE_WARN_FLAGS(__FILE__, __LINE__);
	  }
	| statement_list error T_SEMI
	;

expr_statement:
	  expr T_SEMI {
		expr($1, false, false, false, false);
		ftflg = false;
	  }
	| T_SEMI {
		ftflg = false;
	  }
	;

/*
 * The following two productions are used to implement
 * ({ [[decl-list] stmt-list] }).
 * XXX: This is not well tested.
 */
expr_statement_val:
	  expr T_SEMI {
		/* XXX: We should really do that only on the last name */
		if ($1->tn_op == NAME)
			$1->tn_sym->s_used = true;
		$$ = $1;
		expr($1, false, false, false, false);
		ftflg = false;
	  }
	| non_expr_statement {
		$$ = getnode();
		$$->tn_type = gettyp(VOID);
	  }
	;

expr_statement_list:
	  expr_statement_val
	| expr_statement_list expr_statement_val {
		$$ = $2;
	  }
	;

selection_statement:		/* C99 6.8.4 */
	  if_without_else {
		SAVE_WARN_FLAGS(__FILE__, __LINE__);
		if2();
		if3(0);
	  }
	| if_without_else T_ELSE {
		SAVE_WARN_FLAGS(__FILE__, __LINE__);
		if2();
	  } statement {
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
		if3(1);
	  }
	| if_without_else T_ELSE error {
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
		if3(0);
	  }
	| switch_expr statement {
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
		switch2();
	  }
	| switch_expr error {
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
		switch2();
	  }
	;

if_without_else:
	  if_expr statement
	| if_expr error
	;

if_expr:
	  T_IF T_LPAREN expr T_RPAREN {
		if1($3);
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
	  }
	;

switch_expr:
	  T_SWITCH T_LPAREN expr T_RPAREN {
		switch1($3);
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
	  }
	;

association:
	  type_name T_COLON expr
	| T_DEFAULT T_COLON expr
	;

association_list:
	  association
	| association_list T_COMMA association
	;

generic_expr:
	  T_GENERIC T_LPAREN expr T_COMMA association_list T_RPAREN {
		$$ = $3;
	  }
	;

do_statement:
	  do statement {
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
	  }
	;

iteration_statement:		/* C99 6.8.5 */
	  while_expr statement {
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
		while2();
	  }
	| while_expr error {
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
		while2();
	  }
	| do_statement do_while_expr {
		do2($2);
		ftflg = false;
	  }
	| do error {
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
		do2(NULL);
	  }
	| for_exprs statement {
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
		for2();
		popdecl();
		blklev--;
	  }
	| for_exprs error {
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
		for2();
		popdecl();
		blklev--;
	  }
	;

while_expr:
	  T_WHILE T_LPAREN expr T_RPAREN {
		while1($3);
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
	  }
	;

do:
	  T_DO {
		do1();
	  }
	;

do_while_expr:
	  T_WHILE T_LPAREN expr T_RPAREN T_SEMI {
		$$ = $3;
	  }
	;

for_start:
	  T_FOR T_LPAREN {
		pushdecl(AUTO);
		blklev++;
	  }
	;
for_exprs:
	  for_start declspecs deftyp notype_init_decls T_SEMI opt_expr
	  T_SEMI opt_expr T_RPAREN {
		/* variable declaration in for loop */
		c99ism(325);
		for1(NULL, $6, $8);
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
	    }
	  | for_start opt_expr T_SEMI opt_expr T_SEMI opt_expr T_RPAREN {
		for1($2, $4, $6);
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
	  }
	;

opt_expr:
	  /* empty */ {
		$$ = NULL;
	  }
	| expr {
		$$ = $1;
	  }
	;

jump_statement:			/* C99 6.8.6 */
	  goto identifier T_SEMI {
		dogoto(getsym($2));
	  }
	| goto error T_SEMI {
		symtyp = FVFT;
	  }
	| T_CONTINUE T_SEMI {
		docont();
	  }
	| T_BREAK T_SEMI {
		dobreak();
	  }
	| T_RETURN T_SEMI {
		doreturn(NULL);
	  }
	| T_RETURN expr T_SEMI {
		doreturn($2);
	  }
	;

goto:
	  T_GOTO {
		symtyp = FLABEL;
	  }
	;

asm_statement:
	  T_ASM T_LPAREN read_until_rparn T_SEMI {
		setasm();
	  }
	| T_ASM T_QUAL T_LPAREN read_until_rparn T_SEMI {
		setasm();
	  }
	| T_ASM error
	;

read_until_rparn:
	  /* empty */ {
		ignore_up_to_rparen();
	  }
	;

declaration_list:
	  declaration {
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
	  }
	| declaration_list declaration {
		CLEAR_WARN_FLAGS(__FILE__, __LINE__);
	  }
	;

constant:
	  expr				%prec T_COMMA {
		  $$ = $1;
	  }
	;

expr:
	  expr T_ASTERISK expr {
		$$ = build(MULT, $1, $3);
	  }
	| expr T_MULTIPLICATIVE expr {
		$$ = build($2, $1, $3);
	  }
	| expr T_ADDITIVE expr {
		$$ = build($2, $1, $3);
	  }
	| expr T_SHIFT expr {
		$$ = build($2, $1, $3);
	  }
	| expr T_RELATIONAL expr {
		$$ = build($2, $1, $3);
	  }
	| expr T_EQUALITY expr {
		$$ = build($2, $1, $3);
	  }
	| expr T_AMPER expr {
		$$ = build(BITAND, $1, $3);
	  }
	| expr T_XOR expr {
		$$ = build(BITXOR, $1, $3);
	  }
	| expr T_BITOR expr {
		$$ = build(BITOR, $1, $3);
	  }
	| expr T_LOGAND expr {
		$$ = build(LOGAND, $1, $3);
	  }
	| expr T_LOGOR expr {
		$$ = build(LOGOR, $1, $3);
	  }
	| expr T_QUEST expr T_COLON expr {
		$$ = build(QUEST, $1, build(COLON, $3, $5));
	  }
	| expr T_ASSIGN expr {
		$$ = build(ASSIGN, $1, $3);
	  }
	| expr T_OPASSIGN expr {
		$$ = build($2, $1, $3);
	  }
	| expr T_COMMA expr {
		$$ = build(COMMA, $1, $3);
	  }
	| term {
		$$ = $1;
	  }
	| generic_expr {
		$$ = $1;
	  }
	;

term:
	  T_NAME {
		/* XXX really necessary? */
		if (yychar < 0)
			yychar = yylex();
		$$ = new_name_node(getsym($1), yychar);
	  }
	| string {
		$$ = new_string_node($1);
	  }
	| T_CON {
		$$ = new_constant_node(gettyp($1->v_tspec), $1);
	  }
	| T_LPAREN expr T_RPAREN {
		if ($2 != NULL)
			$2->tn_parenthesized = true;
		$$ = $2;
	  }
	| T_LPAREN compound_statement_lbrace declaration_list
	    expr_statement_list {
		blklev--;
		mblklev--;
		initsym = mktempsym(duptyp($4->tn_type));
		mblklev++;
		blklev++;
		/* ({ }) is a GCC extension */
		gnuism(320);
	 } compound_statement_rbrace T_RPAREN {
		$$ = new_name_node(initsym, 0);
	 }
	| T_LPAREN compound_statement_lbrace expr_statement_list {
		blklev--;
		mblklev--;
		initsym = mktempsym($3->tn_type);
		mblklev++;
		blklev++;
		/* ({ }) is a GCC extension */
		gnuism(320);
	 } compound_statement_rbrace T_RPAREN {
		$$ = new_name_node(initsym, 0);
	 }
	| term T_INCDEC {
		$$ = build($2 == INC ? INCAFT : DECAFT, $1, NULL);
	  }
	| T_INCDEC term {
		$$ = build($1 == INC ? INCBEF : DECBEF, $2, NULL);
	  }
	| T_ASTERISK term {
		$$ = build(INDIR, $2, NULL);
	  }
	| T_AMPER term {
		$$ = build(ADDR, $2, NULL);
	  }
	| T_UNARY term {
		$$ = build($1, $2, NULL);
	  }
	| T_ADDITIVE term {
		if (tflag && $1 == PLUS) {
			/* unary + is illegal in traditional C */
			warning(100);
		}
		$$ = build($1 == PLUS ? UPLUS : UMINUS, $2, NULL);
	  }
	| term T_LBRACK expr T_RBRACK {
		$$ = build(INDIR, build(PLUS, $1, $3), NULL);
	  }
	| term T_LPAREN T_RPAREN {
		$$ = new_function_call_node($1, NULL);
	  }
	| term T_LPAREN func_arg_list T_RPAREN {
		$$ = new_function_call_node($1, $3);
	  }
	| term point_or_arrow T_NAME {
		if ($1 != NULL) {
			sym_t	*msym;
			/*
			 * XXX struct_or_union_member should be integrated
			 * in build()
			 */
			if ($2 == ARROW) {
				/*
				 * must do this before struct_or_union_member
				 * is called
				 */
				$1 = cconv($1);
			}
			msym = struct_or_union_member($1, $2, getsym($3));
			$$ = build($2, $1, new_name_node(msym, 0));
		} else {
			$$ = NULL;
		}
	  }
	| T_REAL term {
		$$ = build(REAL, $2, NULL);
	  }
	| T_IMAG term {
		$$ = build(IMAG, $2, NULL);
	  }
	| T_EXTENSION term {
		$$ = $2;
	  }
	| T_REAL T_LPAREN term T_RPAREN {
		$$ = build(REAL, $3, NULL);
	  }
	| T_IMAG T_LPAREN term T_RPAREN {
		$$ = build(IMAG, $3, NULL);
	  }
	| T_BUILTIN_OFFSETOF T_LPAREN type_name T_COMMA identifier T_RPAREN
						    %prec T_BUILTIN_OFFSETOF {
		symtyp = FMEMBER;
		$$ = build_offsetof($3, getsym($5));
	  }
	| T_SIZEOF term					%prec T_SIZEOF {
		$$ = $2 == NULL ? NULL : build_sizeof($2->tn_type);
		if ($$ != NULL)
			check_expr_misc($2, 0, 0, 0, 0, 0, 1);
	  }
	| T_SIZEOF T_LPAREN type_name T_RPAREN		%prec T_SIZEOF {
		$$ = build_sizeof($3);
	  }
	| T_ALIGNOF T_LPAREN type_name T_RPAREN		%prec T_ALIGNOF {
		$$ = build_alignof($3);
	  }
	| T_LPAREN type_name T_RPAREN term		%prec T_UNARY {
		$$ = cast($4, $2);
	  }
	| T_LPAREN type_name T_RPAREN			%prec T_UNARY {
		sym_t *tmp = mktempsym($2);
		idecl(tmp, 1, NULL);
	  } init_lbrace init_expr_list init_rbrace {
		if (!Sflag)
			 /* compound literals are a C9X/GCC extension */
			 gnuism(319);
		$$ = new_name_node(initsym, 0);
	  }
	;

string:
	  T_STRING {
		$$ = $1;
	  }
	| T_STRING string2 {
		$$ = cat_strings($1, $2);
	  }
	;

string2:
	  T_STRING {
		if (tflag) {
			/* concatenated strings are illegal in traditional C */
			warning(219);
		}
		$$ = $1;
	  }
	| string2 T_STRING {
		$$ = cat_strings($1, $2);
	  }
	;

func_arg_list:
	  expr						%prec T_COMMA {
		$$ = new_function_argument_node(NULL, $1);
	  }
	| func_arg_list T_COMMA expr {
		$$ = new_function_argument_node($1, $3);
	  }
	;

point_or_arrow:
	  T_MEMBACC {
		symtyp = FMEMBER;
		$$ = $1;
	  }
	;

point:
	  T_MEMBACC {
		if ($1 != POINT) {
			/* syntax error '%s' */
			error(249, yytext);
		}
	  }
	;

identifier:
	  T_NAME {
		$$ = $1;
		cgram_debug("name '%s'", $$->sb_name);
	  }
	| T_TYPENAME {
		$$ = $1;
		cgram_debug("typename '%s'", $$->sb_name);
	  }
	;

%%

/* ARGSUSED */
int
yyerror(const char *msg)
{
	/* syntax error '%s' */
	error(249, yytext);
	if (++sytxerr >= 5)
		norecover();
	return 0;
}

static __inline int uq_gt(uint64_t, uint64_t);
static __inline int q_gt(int64_t, int64_t);

static __inline int
uq_gt(uint64_t a, uint64_t b)
{

	return a > b;
}

static __inline int
q_gt(int64_t a, int64_t b)
{

	return a > b;
}

#define	q_lt(a, b)	q_gt(b, a)

/*
 * Gets a node for a constant and returns the value of this constant
 * as integer.
 *
 * If the node is not constant or too large for int or of type float,
 * a warning will be printed.
 *
 * toicon() should be used only inside declarations. If it is used in
 * expressions, it frees the memory used for the expression.
 */
static int
toicon(tnode_t *tn, int required)
{
	int	i;
	tspec_t	t;
	val_t	*v;

	v = constant(tn, required);

	if (tn == NULL) {
		i = 1;
		goto done;
	}

	/*
	 * Abstract declarations are used inside expression. To free
	 * the memory would be a fatal error.
	 * We don't free blocks that are inside casts because these
	 * will be used later to match types.
	 */
	if (tn->tn_op != CON && dcs->d_ctx != ABSTRACT)
		tfreeblk();

	if ((t = v->v_tspec) == FLOAT || t == DOUBLE || t == LDOUBLE) {
		i = (int)v->v_ldbl;
		/* integral constant expression expected */
		error(55);
	} else {
		i = (int)v->v_quad;
		if (is_uinteger(t)) {
			if (uq_gt((uint64_t)v->v_quad,
				  (uint64_t)TARG_INT_MAX)) {
				/* integral constant too large */
				warning(56);
			}
		} else {
			if (q_gt(v->v_quad, (int64_t)TARG_INT_MAX) ||
			    q_lt(v->v_quad, (int64_t)TARG_INT_MIN)) {
				/* integral constant too large */
				warning(56);
			}
		}
	}

done:
	free(v);
	return i;
}

static void
idecl(sym_t *decl, int initflg, sbuf_t *renaming)
{
	char *s;

	initerr = false;
	initsym = decl;

	switch (dcs->d_ctx) {
	case EXTERN:
		if (renaming != NULL) {
			lint_assert(decl->s_rename == NULL);

			s = getlblk(1, renaming->sb_len + 1);
	                (void)memcpy(s, renaming->sb_name, renaming->sb_len + 1);
			decl->s_rename = s;
			freeyyv(&renaming, T_NAME);
		}
		decl1ext(decl, initflg);
		break;
	case ARG:
		if (renaming != NULL) {
			/* symbol renaming can't be used on function arguments */
			error(310);
			freeyyv(&renaming, T_NAME);
			break;
		}
		(void)declare_argument(decl, initflg);
		break;
	case AUTO:
		if (renaming != NULL) {
			/* symbol renaming can't be used on automatic variables */
			error(311);
			freeyyv(&renaming, T_NAME);
			break;
		}
		declare_local(decl, initflg);
		break;
	default:
		LERROR("idecl(%d)", dcs->d_ctx);
	}

	if (initflg && !initerr)
		initstack_init();
}

/*
 * Discard all input tokens up to and including the next
 * unmatched right paren
 */
static void
ignore_up_to_rparen(void)
{
	int	level;

	if (yychar < 0)
		yychar = yylex();
	freeyyv(&yylval, yychar);

	level = 1;
	while (yychar != T_RPAREN || --level > 0) {
		if (yychar == T_LPAREN) {
			level++;
		} else if (yychar <= 0) {
			break;
		}
		freeyyv(&yylval, yychar = yylex());
	}

	yyclearin;
}

static	sym_t *
symbolrename(sym_t *s, sbuf_t *sb)
{
	if (sb)
		s->s_rename = sb->sb_name;
	return s;
}