Module Name:    src
Committed By:   rillig
Date:           Mon Jul 12 21:43:44 UTC 2021

Modified Files:
        src/usr.bin/xlint/lint1: cgram.y

Log Message:
lint: reorder grammar rules in the same way as in C99

The code coverage before and after this change is exactly the same,
except of course for cgram.y and cgram.c.

No functional change.


To generate a diff of this commit:
cvs rdiff -u -r1.318 -r1.319 src/usr.bin/xlint/lint1/cgram.y

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.bin/xlint/lint1/cgram.y
diff -u src/usr.bin/xlint/lint1/cgram.y:1.318 src/usr.bin/xlint/lint1/cgram.y:1.319
--- src/usr.bin/xlint/lint1/cgram.y:1.318	Sun Jul 11 21:07:44 2021
+++ src/usr.bin/xlint/lint1/cgram.y	Mon Jul 12 21:43:44 2021
@@ -1,5 +1,5 @@
 %{
-/* $NetBSD: cgram.y,v 1.318 2021/07/11 21:07:44 rillig Exp $ */
+/* $NetBSD: cgram.y,v 1.319 2021/07/12 21:43:44 rillig Exp $ */
 
 /*
  * Copyright (c) 1996 Christopher G. Demetriou.  All Rights Reserved.
@@ -35,7 +35,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID) && !defined(lint)
-__RCSID("$NetBSD: cgram.y,v 1.318 2021/07/11 21:07:44 rillig Exp $");
+__RCSID("$NetBSD: cgram.y,v 1.319 2021/07/12 21:43:44 rillig Exp $");
 #endif
 
 #include <limits.h>
@@ -363,159 +363,373 @@ program:
 	| translation_unit
 	;
 
-translation_unit:		/* C99 6.9 */
-	  external_declaration
-	| translation_unit external_declaration
+identifier_sym:			/* helper for struct/union/enum */
+	  identifier {
+		$$ = getsym($1);
+	  }
 	;
 
-external_declaration:		/* C99 6.9 */
-	  asm_statement
-	| function_definition {
-		global_clean_up_decl(false);
-		clear_warning_flags();
+/* K&R ???, C90 ???, C99 6.4.2.1, C11 ???, C18 ??? */
+identifier:
+	  T_NAME {
+		$$ = $1;
+		cgram_debug("name '%s'", $$->sb_name);
 	  }
-	| top_level_declaration {
-		global_clean_up_decl(false);
-		clear_warning_flags();
+	| T_TYPENAME {
+		$$ = $1;
+		cgram_debug("typename '%s'", $$->sb_name);
+	  }
+	;
+
+/* see C99 6.4.5, string literals are joined by 5.1.1.2 */
+string:
+	  T_STRING
+	| T_STRING string2 {
+		$$ = cat_strings($1, $2);
+	  }
+	;
+
+/* see C99 6.4.5, string literals are joined by 5.1.1.2 */
+string2:
+	  T_STRING {
+		if (tflag) {
+			/* concatenated strings are illegal in traditional C */
+			warning(219);
+		}
+		$$ = $1;
+	  }
+	| string2 T_STRING {
+		$$ = cat_strings($1, $2);
+	  }
+	;
+
+/* K&R 7.1, C90 ???, C99 6.5.1, C11 6.5.1, C18 6.5.1 */
+primary_expression:
+	  T_NAME {
+		/* XXX really necessary? */
+		if (yychar < 0)
+			yychar = yylex();
+		$$ = new_name_node(getsym($1), yychar);
+	  }
+	| T_CON {
+		$$ = expr_new_constant(gettyp($1->v_tspec), $1);
+	  }
+	| string {
+		$$ = new_string_node($1);
+	  }
+	| T_LPAREN expr T_RPAREN {
+		if ($2 != NULL)
+			$2->tn_parenthesized = true;
+		$$ = $2;
+	  }
+	| generic_selection
+	/* GCC primary-expression, see c_parser_postfix_expression */
+	| T_BUILTIN_OFFSETOF T_LPAREN type_name T_COMMA identifier T_RPAREN {
+		symtyp = FMEMBER;
+		$$ = build_offsetof($3, getsym($5));
+	  }
+	;
+
+/* K&R ---, C90 ---, C99 ---, C11 6.5.1.1, C18 6.5.1.1 */
+generic_selection:
+	  T_GENERIC T_LPAREN assignment_expression T_COMMA
+	    generic_assoc_list T_RPAREN {
+	  	/* generic selection requires C11 or later */
+	  	c11ism(345);
+		$$ = build_generic_selection($3, $5);
+	  }
+	;
+
+/* K&R ---, C90 ---, C99 ---, C11 6.5.1.1, C18 6.5.1.1 */
+generic_assoc_list:
+	  generic_association
+	| generic_assoc_list T_COMMA generic_association {
+		$3->ga_prev = $1;
+		$$ = $3;
+	  }
+	;
+
+/* K&R ---, C90 ---, C99 ---, C11 6.5.1.1, C18 6.5.1.1 */
+generic_association:
+	  type_name T_COLON assignment_expression {
+		$$ = getblk(sizeof(*$$));
+		$$->ga_arg = $1;
+		$$->ga_result = $3;
+	  }
+	| T_DEFAULT T_COLON assignment_expression {
+		$$ = getblk(sizeof(*$$));
+		$$->ga_arg = NULL;
+		$$->ga_result = $3;
+	  }
+	;
+
+/* K&R 7.1, C90 ???, C99 6.5.2, C11 6.5.2, C18 6.5.2 */
+postfix_expression:
+	  primary_expression
+	| postfix_expression T_LBRACK expr T_RBRACK {
+		$$ = build(INDIR, build(PLUS, $1, $3), NULL);
+	  }
+	| postfix_expression T_LPAREN T_RPAREN {
+		$$ = new_function_call_node($1, NULL);
+	  }
+	| postfix_expression T_LPAREN argument_expression_list T_RPAREN {
+		$$ = new_function_call_node($1, $3);
+	  }
+	| postfix_expression 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;
+		}
+	  }
+	| postfix_expression T_INCDEC {
+		$$ = build($2 == INC ? INCAFT : DECAFT, $1, NULL);
+	  }
+	| T_LPAREN type_name T_RPAREN {	/* C99 6.5.2.5 "Compound literals" */
+		sym_t *tmp = mktempsym($2);
+		begin_initialization(tmp);
+		cgram_declare(tmp, true, NULL);
+	  } init_lbrace initializer_list comma_opt init_rbrace {
+		if (!Sflag)
+			 /* compound literals are a C9X/GCC extension */
+			 gnuism(319);
+		$$ = new_name_node(*current_initsym(), 0);
+		end_initialization();
+	  }
+	| T_LPAREN compound_statement_lbrace gcc_statement_expr_list {
+		block_level--;
+		mem_block_level--;
+		begin_initialization(mktempsym(dup_type($3->tn_type)));
+		mem_block_level++;
+		block_level++;
+		/* ({ }) is a GCC extension */
+		gnuism(320);
+	  } compound_statement_rbrace T_RPAREN {
+		$$ = new_name_node(*current_initsym(), 0);
+		end_initialization();
 	  }
 	;
 
+comma_opt:			/* helper for 'postfix_expression' */
+	  /* empty */
+	| T_COMMA
+	;
+
 /*
- * On the top level, lint allows several forms of declarations that it doesn't
- * allow in functions.  For example, a single ';' is an empty declaration and
- * is supported by some compilers, but in a function it would be an empty
- * statement, not a declaration.  This makes a difference in C90 mode, where
- * a statement must not be followed by a declaration.
+ * The inner part of a GCC statement-expression of the form ({ ... }).
  *
- * See 'declaration' for all other declarations.
+ * https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
  */
-top_level_declaration:		/* C99 6.9 calls this 'declaration' */
-	  T_SEMI {
-		if (sflag) {
-			/* empty declaration */
-			error(0);
-		} else if (!tflag) {
-			/* empty declaration */
-			warning(0);
-		}
+gcc_statement_expr_list:
+	  gcc_statement_expr_item
+	| gcc_statement_expr_list gcc_statement_expr_item {
+		$$ = $2;
 	  }
-	| begin_type end_type notype_init_decls T_SEMI {
-		if (sflag) {
-			/* old style declaration; add 'int' */
-			error(1);
-		} else if (!tflag) {
-			/* old style declaration; add 'int' */
-			warning(1);
-		}
+	;
+
+gcc_statement_expr_item:
+	  declaration {
+		clear_warning_flags();
+		$$ = NULL;
 	  }
-	| begin_type_declmods end_type T_SEMI {
-		if (dcs->d_scl == TYPEDEF) {
-			/* typedef declares no type name */
-			warning(72);
+	| non_expr_statement {
+		$$ = expr_zalloc_tnode();
+		$$->tn_type = gettyp(VOID);
+	  }
+	| expr T_SEMI {
+		if ($1 == NULL) {	/* in case of syntax errors */
+			$$ = expr_zalloc_tnode();
+			$$->tn_type = gettyp(VOID);
 		} else {
-			/* empty declaration */
-			warning(2);
+			/* XXX: 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);
+			seen_fallthrough = false;
 		}
 	  }
-	| begin_type_declmods end_type notype_init_decls T_SEMI
-	| begin_type_declaration_specifiers end_type T_SEMI {
-		if (dcs->d_scl == TYPEDEF) {
-			/* typedef declares no type name */
-			warning(72);
-		} else if (!dcs->d_nonempty_decl) {
-			/* empty declaration */
-			warning(2);
+	;
+
+point_or_arrow:			/* helper for 'postfix_expression' */
+	  T_POINT {
+		symtyp = FMEMBER;
+		$$ = POINT;
+	  }
+	| T_ARROW {
+		symtyp = FMEMBER;
+		$$ = ARROW;
+	  }
+	;
+
+/* K&R 7.1, C90 ???, C99 6.5.2, C11 6.5.2, C18 6.5.2 */
+argument_expression_list:
+	  expr %prec T_COMMA {
+		$$ = new_function_argument_node(NULL, $1);
+	  }
+	| argument_expression_list T_COMMA expr {
+		$$ = new_function_argument_node($1, $3);
+	  }
+	;
+
+/* K&R 7.2, C90 ???, C99 6.5.3, C11 6.5.3, C18 6.5.3 */
+unary_expression:
+	  postfix_expression
+	| T_INCDEC unary_expression {
+		$$ = build($1 == INC ? INCBEF : DECBEF, $2, NULL);
+	  }
+	| T_AMPER cast_expression {
+		$$ = build(ADDR, $2, NULL);
+	  }
+	| T_ASTERISK cast_expression {
+		$$ = build(INDIR, $2, NULL);
+	  }
+	| T_ADDITIVE cast_expression {
+		if (tflag && $1 == PLUS) {
+			/* unary + is illegal in traditional C */
+			warning(100);
 		}
+		$$ = build($1 == PLUS ? UPLUS : UMINUS, $2, NULL);
 	  }
-	| begin_type_declaration_specifiers end_type type_init_decls T_SEMI
-	| error T_SEMI {
-		global_clean_up();
+	| T_COMPLEMENT cast_expression {
+		$$ = build(COMPL, $2, NULL);
 	  }
-	| error T_RBRACE {
-		global_clean_up();
+	| T_LOGNOT cast_expression {
+		$$ = build(NOT, $2, NULL);
+	  }
+	| T_REAL cast_expression {	/* GCC c_parser_unary_expression */
+		$$ = build(REAL, $2, NULL);
+	  }
+	| T_IMAG cast_expression {	/* GCC c_parser_unary_expression */
+		$$ = build(IMAG, $2, NULL);
+	  }
+	| T_EXTENSION cast_expression {	/* GCC c_parser_unary_expression */
+		$$ = $2;
+	  }
+	| T_SIZEOF unary_expression {
+		$$ = $2 == NULL ? NULL : build_sizeof($2->tn_type);
+		if ($$ != NULL)
+			check_expr_misc($2, false, false, false, false, false, true);
+	  }
+	| T_SIZEOF T_LPAREN type_name T_RPAREN {
+		$$ = build_sizeof($3);
+	  }
+	/* K&R ---, C90 ---, C99 ---, C11 6.5.3, C18 6.5.3 */
+	| T_ALIGNOF T_LPAREN type_name T_RPAREN {
+		$$ = build_alignof($3);
+	  }
+	;
+
+/* The rule 'unary_operator' is inlined into unary_expression. */
+
+/* K&R 7.2, C90 ???, C99 6.5.4, C11 6.5.4, C18 6.5.4 */
+cast_expression:
+	  unary_expression
+	| T_LPAREN type_name T_RPAREN cast_expression {
+		$$ = cast($4, $2);
 	  }
 	;
 
-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);
-		block_level++;
-		begin_declaration_level(ARG);
-		if (lwarn == LWARN_NONE)
-			$1->s_used = true;
-	  } arg_declaration_list_opt {
-		end_declaration_level();
-		block_level--;
-		check_func_lint_directives();
-		check_func_old_style_arguments();
-		begin_control_statement(CS_FUNCTION_BODY);
-	  } compound_statement {
-		funcend();
-		end_control_statement(CS_FUNCTION_BODY);
+expr_opt:
+	  /* empty */ {
+		$$ = NULL;
 	  }
+	| expr
 	;
 
-func_decl:
-	  begin_type end_type notype_decl {
-		$$ = $3;
+/* 'expression' also implements 'multiplicative_expression'. */
+/* 'expression' also implements 'additive_expression'. */
+/* 'expression' also implements 'shift_expression'. */
+/* 'expression' also implements 'relational_expression'. */
+/* 'expression' also implements 'equality_expression'. */
+/* 'expression' also implements 'AND_expression'. */
+/* 'expression' also implements 'exclusive_OR_expression'. */
+/* 'expression' also implements 'inclusive_OR_expression'. */
+/* 'expression' also implements 'logical_AND_expression'. */
+/* 'expression' also implements 'logical_OR_expression'. */
+/* 'expression' also implements 'conditional_expression'. */
+/* 'expression' also implements 'assignment_expression'. */
+/* TODO: rename to 'expression' */
+/* K&R ???, C90 ???, C99 6.5.5 to 6.5.17, C11 ???, C18 ??? */
+expr:
+	  expr T_ASTERISK expr {
+		$$ = build(MULT, $1, $3);
 	  }
-	| begin_type_declmods end_type notype_decl {
-		$$ = $3;
+	| expr T_MULTIPLICATIVE expr {
+		$$ = build($2, $1, $3);
 	  }
-	| begin_type_declaration_specifiers end_type type_decl {
-		$$ = $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_BITXOR 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);
 	  }
+	| cast_expression
 	;
 
-arg_declaration_list_opt:	/* C99 6.9.1p13 example 1 */
+/* K&R ???, C90 ???, C99 6.5.16, C11 ???, C18 ??? */
+assignment_expression:
+	  expr %prec T_ASSIGN
+	;
+
+constant_expr_list_opt:		/* helper for gcc_attribute */
 	  /* empty */
-	| arg_declaration_list
+	| constant_expr_list
 	;
 
-arg_declaration_list:		/* C99 6.9.1p13 example 1 */
-	  arg_declaration
-	| arg_declaration_list arg_declaration
-	/* XXX or better "arg_declaration error" ? */
-	| error
+constant_expr_list:		/* helper for gcc_attribute */
+	  constant_expr
+	| constant_expr_list T_COMMA constant_expr
 	;
 
-/*
- * "arg_declaration" is separated from "declaration" because it
- * needs other error handling.
- */
-arg_declaration:
-	  begin_type_declmods end_type T_SEMI {
-		/* empty declaration */
-		warning(2);
-	  }
-	| begin_type_declmods end_type notype_init_decls T_SEMI
-	| begin_type_declaration_specifiers end_type T_SEMI {
-		if (!dcs->d_nonempty_decl) {
-			/* empty declaration */
-			warning(2);
-		} else {
-			/* '%s' declared in argument declaration list */
-			warning(3, type_name(dcs->d_type));
-		}
-	  }
-	| begin_type_declaration_specifiers end_type type_init_decls T_SEMI {
-		if (dcs->d_nonempty_decl) {
-			/* '%s' declared in argument declaration list */
-			warning(3, type_name(dcs->d_type));
-		}
-	  }
-	| begin_type_declmods error
-	| begin_type_declaration_specifiers error
+constant_expr:			/* C99 6.6 */
+	  expr %prec T_ASSIGN
 	;
 
 declaration:			/* C99 6.7 */
@@ -542,18 +756,6 @@ declaration:			/* C99 6.7 */
 	| error T_SEMI
 	;
 
-begin_type:
-	  /* empty */ {
-		begin_type();
-	  }
-	;
-
-end_type:
-	  /* empty */ {
-		end_type();
-	  }
-	;
-
 begin_type_declaration_specifiers:	/* see C99 6.7 */
 	  begin_type_typespec {
 		add_type($1);
@@ -568,7 +770,7 @@ begin_type_declaration_specifiers:	/* se
 	  }
 	;
 
-begin_type_declmods:
+begin_type_declmods:		/* see C99 6.7 */
 	  begin_type T_QUAL {
 		add_qualifier($2);
 	  }
@@ -578,14 +780,21 @@ begin_type_declmods:
 	| begin_type_declmods declmod
 	;
 
-declmod:
-	  T_QUAL {
-		add_qualifier($1);
+begin_type_noclass_declspecs:
+	  begin_type_typespec {
+		add_type($1);
 	  }
-	| T_SCLASS {
-		add_storage_class($1);
+	| type_attribute begin_type_noclass_declspecs
+	| begin_type_noclass_declmods type_specifier {
+		add_type($2);
 	  }
-	| type_attribute_list
+	| begin_type_noclass_declspecs T_QUAL {
+		add_qualifier($2);
+	  }
+	| begin_type_noclass_declspecs notype_type_specifier {
+		add_type($2);
+	  }
+	| begin_type_noclass_declspecs type_attribute
 	;
 
 begin_type_typespec:
@@ -597,6 +806,25 @@ begin_type_typespec:
 	  }
 	;
 
+begin_type_noclass_declmods:
+	  begin_type T_QUAL {
+		add_qualifier($2);
+	  }
+	| begin_type_noclass_declmods T_QUAL {
+		add_qualifier($2);
+	  }
+	;
+
+declmod:
+	  T_QUAL {
+		add_qualifier($1);
+	  }
+	| T_SCLASS {
+		add_storage_class($1);
+	  }
+	| type_attribute_list
+	;
+
 type_attribute_list:
 	  type_attribute
 	| type_attribute_list type_attribute
@@ -620,6 +848,23 @@ type_attribute:			/* See C11 6.7 declara
 	| T_NORETURN
 	;
 
+align_as:			/* See alignment-specifier in C11 6.7.5 */
+	  type_specifier
+	| constant_expr
+	;
+
+begin_type:
+	  /* empty */ {
+		begin_type();
+	  }
+	;
+
+end_type:
+	  /* empty */ {
+		end_type();
+	  }
+	;
+
 type_specifier:			/* C99 6.7.2 */
 	  notype_type_specifier
 	| T_TYPENAME {
@@ -627,7 +872,7 @@ type_specifier:			/* C99 6.7.2 */
 	  }
 	;
 
-notype_type_specifier:
+notype_type_specifier:		/* see C99 6.7.2 */
 	  T_TYPE {
 		$$ = gettyp($1);
 	  }
@@ -682,19 +927,19 @@ struct_or_union:		/* C99 6.7.2.1 */
 	  }
 	;
 
-braced_struct_declaration_list:
+braced_struct_declaration_list:	/* see C99 6.7.2.1 */
 	  struct_declaration_lbrace struct_declaration_list_with_rbrace {
 		$$ = $2;
 	  }
 	;
 
-struct_declaration_lbrace:
+struct_declaration_lbrace:	/* see C99 6.7.2.1 */
 	  T_LBRACE {
 		symtyp = FVFT;
 	  }
 	;
 
-struct_declaration_list_with_rbrace:
+struct_declaration_list_with_rbrace:	/* see C99 6.7.2.1 */
 	  struct_declaration_list T_SEMI T_RBRACE
 	| struct_declaration_list T_RBRACE {
 		if (sflag) {
@@ -711,14 +956,14 @@ struct_declaration_list_with_rbrace:
 	  }
 	;
 
-struct_declaration_list:
+struct_declaration_list:	/* C99 6.7.2.1 */
 	  struct_declaration
 	| struct_declaration_list T_SEMI struct_declaration {
 		$$ = lnklst($1, $3);
 	  }
 	;
 
-struct_declaration:
+struct_declaration:		/* C99 6.7.2.1 */
 	  begin_type_noclass_declmods end_type {
 		/* too late, i know, but getsym() compensates it */
 		symtyp = FMEMBER;
@@ -758,32 +1003,7 @@ struct_declaration:
 	  }
 	;
 
-begin_type_noclass_declspecs:
-	  begin_type_typespec {
-		add_type($1);
-	  }
-	| type_attribute begin_type_noclass_declspecs
-	| begin_type_noclass_declmods type_specifier {
-		add_type($2);
-	  }
-	| begin_type_noclass_declspecs T_QUAL {
-		add_qualifier($2);
-	  }
-	| begin_type_noclass_declspecs notype_type_specifier {
-		add_type($2);
-	  }
-	| begin_type_noclass_declspecs type_attribute
-	;
-
-begin_type_noclass_declmods:
-	  begin_type T_QUAL {
-		add_qualifier($2);
-	  }
-	| begin_type_noclass_declmods T_QUAL {
-		add_qualifier($2);
-	  }
-	;
-
+/* TODO: rename 'decls' to 'declarators', everywhere. */
 notype_member_decls:
 	  notype_member_decl {
 		$$ = declarator_1_struct_union($1);
@@ -830,7 +1050,7 @@ type_member_decl:
 	  }
 	;
 
-enum_specifier:		/* C99 6.7.2.2 */
+enum_specifier:			/* C99 6.7.2.2 */
 	  enum identifier_sym {
 		$$ = mktag($2, ENUM, false, false);
 	  }
@@ -850,27 +1070,27 @@ enum_specifier:		/* C99 6.7.2.2 */
 	  }
 	;
 
-enum:
+enum:				/* helper for C99 6.7.2.2 */
 	  T_ENUM {
 		symtyp = FTAG;
 		begin_declaration_level(CTCONST);
 	  }
 	;
 
-enum_declaration:
+enum_declaration:		/* helper for C99 6.7.2.2 */
 	  enum_decl_lbrace enums_with_opt_comma T_RBRACE {
 		$$ = $2;
 	  }
 	;
 
-enum_decl_lbrace:
+enum_decl_lbrace:		/* helper for C99 6.7.2.2 */
 	  T_LBRACE {
 		symtyp = FVFT;
 		enumval = 0;
 	  }
 	;
 
-enums_with_opt_comma:
+enums_with_opt_comma:		/* helper for C99 6.7.2.2 */
 	  enumerator_list
 	| enumerator_list T_COMMA {
 		if (sflag) {
@@ -884,26 +1104,69 @@ enums_with_opt_comma:
 	  }
 	;
 
-enumerator_list:
-	  enumerator
-	| enumerator_list T_COMMA enumerator {
-		$$ = lnklst($1, $3);
+enumerator_list:		/* C99 6.7.2.2 */
+	  enumerator
+	| enumerator_list T_COMMA enumerator {
+		$$ = lnklst($1, $3);
+	  }
+	| error {
+		$$ = NULL;
+	  }
+	;
+
+enumerator:			/* C99 6.7.2.2 */
+	  identifier_sym {
+		$$ = enumeration_constant($1, enumval, true);
+	  }
+	| identifier_sym T_ASSIGN constant_expr {
+		$$ = enumeration_constant($1, to_int_constant($3, true), false);
+	  }
+	;
+
+type_qualifier:			/* C99 6.7.3 */
+	  T_QUAL {
+		$$ = xcalloc(1, sizeof(*$$));
+		if ($1 == CONST) {
+			$$->p_const = true;
+		} else if ($1 == VOLATILE) {
+			$$->p_volatile = true;
+		} else {
+			lint_assert($1 == RESTRICT || $1 == THREAD);
+		}
+	  }
+	;
+
+pointer:			/* C99 6.7.5 */
+	  asterisk type_qualifier_list_opt {
+		$$ = merge_qualified_pointer($1, $2);
+	  }
+	| asterisk type_qualifier_list_opt pointer {
+		$$ = merge_qualified_pointer($1, $2);
+		$$ = merge_qualified_pointer($$, $3);
+	  }
+	;
+
+asterisk:			/* helper for 'pointer' */
+	  T_ASTERISK {
+		$$ = xcalloc(1, sizeof(*$$));
+		$$->p_pointer = true;
 	  }
-	| error {
+	;
+
+type_qualifier_list_opt:	/* see C99 6.7.5 */
+	  /* empty */ {
 		$$ = NULL;
 	  }
+	| type_qualifier_list
 	;
 
-enumerator:			/* C99 6.7.2.2 */
-	  identifier_sym {
-		$$ = enumeration_constant($1, enumval, true);
-	  }
-	| identifier_sym T_ASSIGN constant_expr {
-		$$ = enumeration_constant($1, to_int_constant($3, true), false);
+type_qualifier_list:		/* C99 6.7.5 */
+	  type_qualifier
+	| type_qualifier_list type_qualifier {
+		$$ = merge_qualified_pointer($1, $2);
 	  }
 	;
 
-
 /*
  * For an explanation of 'notype' in the following rules, see the Bison
  * manual, section 7.1 "Semantic Info in Token Kinds".
@@ -1023,16 +1286,11 @@ type_param_decl:
 	  }
 	;
 
-array_size:
-	  type_qualifier_list_opt T_SCLASS constant_expr {
-		/* C11 6.7.6.3p7 */
-		if ($2 != STATIC)
-			yyerror("Bad attribute");
-		/* static array size is a C11 extension */
-		c11ism(343);
-		$$ = $3;
+notype_param_decl:
+	  direct_notype_param_decl
+	| pointer direct_notype_param_decl {
+		$$ = add_pointer($2, $1);
 	  }
-	| constant_expr
 	;
 
 direct_param_decl:
@@ -1058,13 +1316,6 @@ direct_param_decl:
 	  }
 	;
 
-notype_param_decl:
-	  direct_notype_param_decl
-	| pointer direct_notype_param_decl {
-		$$ = add_pointer($2, $1);
-	  }
-	;
-
 direct_notype_param_decl:
 	  identifier {
 		$$ = declarator_name(getsym($1));
@@ -1085,77 +1336,113 @@ direct_notype_param_decl:
 	  }
 	;
 
-pointer:			/* C99 6.7.5 */
-	  asterisk type_qualifier_list_opt {
-		$$ = merge_qualified_pointer($1, $2);
-	  }
-	| asterisk type_qualifier_list_opt pointer {
-		$$ = merge_qualified_pointer($1, $2);
-		$$ = merge_qualified_pointer($$, $3);
+param_list:
+	  id_list_lparen identifier_list T_RPAREN {
+		$$ = $2;
 	  }
+	| abstract_decl_param_list
 	;
 
-asterisk:
-	  T_ASTERISK {
-		$$ = xcalloc(1, sizeof(*$$));
-		$$->p_pointer = true;
+id_list_lparen:
+	  T_LPAREN {
+		block_level++;
+		begin_declaration_level(PROTO_ARG);
 	  }
 	;
 
-type_qualifier_list_opt:
-	  /* empty */ {
-		$$ = NULL;
+array_size:
+	  type_qualifier_list_opt T_SCLASS constant_expr {
+		/* C11 6.7.6.3p7 */
+		if ($2 != STATIC)
+			yyerror("Bad attribute");
+		/* static array size is a C11 extension */
+		c11ism(343);
+		$$ = $3;
 	  }
-	| type_qualifier_list
+	| constant_expr
 	;
 
-type_qualifier_list:		/* C99 6.7.5 */
-	  type_qualifier
-	| type_qualifier_list type_qualifier {
-		$$ = merge_qualified_pointer($1, $2);
+identifier_list:		/* C99 6.7.5 */
+	  T_NAME {
+		$$ = old_style_function_name(getsym($1));
 	  }
-	;
-
-type_qualifier:
-	  T_QUAL {
-		$$ = xcalloc(1, sizeof(*$$));
-		if ($1 == CONST) {
-			$$->p_const = true;
-		} else if ($1 == VOLATILE) {
-			$$->p_volatile = true;
-		} else {
-			lint_assert($1 == RESTRICT || $1 == THREAD);
-		}
+	| identifier_list T_COMMA T_NAME {
+		$$ = lnklst($1, old_style_function_name(getsym($3)));
 	  }
+	| identifier_list error
 	;
 
-align_as:			/* See alignment-specifier in C11 6.7.5 */
-	  type_specifier
-	| constant_expr
+/* XXX: C99 requires an additional specifier-qualifier-list. */
+type_name:			/* C99 6.7.6 */
+	  {
+		begin_declaration_level(ABSTRACT);
+	  } abstract_declaration {
+		end_declaration_level();
+		$$ = $2->s_type;
+	  }
 	;
 
-param_list:
-	  id_list_lparen identifier_list T_RPAREN {
-		$$ = $2;
+abstract_declaration:
+	  begin_type_noclass_declmods end_type {
+		$$ = declare_1_abstract(abstract_name());
+	  }
+	| begin_type_noclass_declspecs end_type {
+		$$ = declare_1_abstract(abstract_name());
+	  }
+	| begin_type_noclass_declmods end_type abstract_declarator {
+		$$ = declare_1_abstract($3);
+	  }
+	| begin_type_noclass_declspecs end_type abstract_declarator {
+		$$ = declare_1_abstract($3);
 	  }
-	| abstract_decl_param_list
 	;
 
-id_list_lparen:
-	  T_LPAREN {
-		block_level++;
-		begin_declaration_level(PROTO_ARG);
+abstract_declarator:		/* C99 6.7.6 */
+	  pointer {
+		$$ = add_pointer(abstract_name(), $1);
+	  }
+	| direct_abstract_declarator
+	| pointer direct_abstract_declarator {
+		$$ = add_pointer($2, $1);
+	  }
+	| T_TYPEOF cast_expression {	/* GCC extension */
+		$$ = mktempsym($2->tn_type);
 	  }
 	;
 
-identifier_list:
-	  T_NAME {
-		$$ = old_style_function_name(getsym($1));
+direct_abstract_declarator:	/* C99 6.7.6 */
+	  T_LPAREN abstract_declarator T_RPAREN {
+		$$ = $2;
 	  }
-	| identifier_list T_COMMA T_NAME {
-		$$ = lnklst($1, old_style_function_name(getsym($3)));
+	| T_LBRACK T_RBRACK {
+		$$ = add_array(abstract_name(), false, 0);
 	  }
-	| identifier_list error
+	| T_LBRACK array_size T_RBRACK {
+		$$ = add_array(abstract_name(), true, to_int_constant($2, false));
+	  }
+	| type_attribute direct_abstract_declarator {
+		$$ = $2;
+	  }
+	| direct_abstract_declarator T_LBRACK T_RBRACK {
+		$$ = add_array($1, false, 0);
+	  }
+	| direct_abstract_declarator T_LBRACK T_ASTERISK T_RBRACK { /* C99 */
+		$$ = add_array($1, false, 0);
+	  }
+	| direct_abstract_declarator T_LBRACK array_size T_RBRACK {
+		$$ = add_array($1, true, to_int_constant($3, false));
+	  }
+	| abstract_decl_param_list asm_or_symbolrename_opt {
+		$$ = add_function(symbolrename(abstract_name(), $2), $1);
+		end_declaration_level();
+		block_level--;
+	  }
+	| direct_abstract_declarator abstract_decl_param_list asm_or_symbolrename_opt {
+		$$ = add_function(symbolrename($1, $3), $2);
+		end_declaration_level();
+		block_level--;
+	  }
+	| direct_abstract_declarator type_attribute_list
 	;
 
 abstract_decl_param_list:
@@ -1197,6 +1484,7 @@ vararg_parameter_type_list:
 	  }
 	;
 
+/* XXX: C99 6.7.5 defines the same name, but it looks different. */
 parameter_type_list:
 	  parameter_declaration
 	| parameter_type_list T_COMMA parameter_declaration {
@@ -1233,19 +1521,6 @@ parameter_declaration:
 	  }
 	;
 
-asm_or_symbolrename_opt:		/* 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:			/* C99 6.7.8 "Initialization" */
 	  expr %prec T_COMMA {
 		init_expr($1);
@@ -1262,7 +1537,7 @@ initializer_list:		/* C99 6.7.8 "Initial
 	| initializer_list T_COMMA initializer_list_item
 	;
 
-initializer_list_item:
+initializer_list_item:		/* helper */
 	  designation initializer
 	| initializer
 	;
@@ -1309,91 +1584,37 @@ range:
 	  }
 	;
 
-init_lbrace:
+init_lbrace:			/* helper */
 	  T_LBRACE {
 		init_lbrace();
 	  }
 	;
 
-init_rbrace:
+init_rbrace:			/* helper */
 	  T_RBRACE {
 		init_rbrace();
 	  }
 	;
 
-type_name:			/* C99 6.7.6 */
-	  {
-		begin_declaration_level(ABSTRACT);
-	  } abstract_declaration {
-		end_declaration_level();
-		$$ = $2->s_type;
-	  }
-	;
-
-abstract_declaration:
-	  begin_type_noclass_declmods end_type {
-		$$ = declare_1_abstract(abstract_name());
-	  }
-	| begin_type_noclass_declspecs end_type {
-		$$ = declare_1_abstract(abstract_name());
-	  }
-	| begin_type_noclass_declmods end_type abstract_declarator {
-		$$ = declare_1_abstract($3);
-	  }
-	| begin_type_noclass_declspecs end_type abstract_declarator {
-		$$ = declare_1_abstract($3);
-	  }
-	;
-
-abstract_declarator:		/* C99 6.7.6 */
-	  pointer {
-		$$ = add_pointer(abstract_name(), $1);
+asm_or_symbolrename_opt:	/* GCC extensions */
+	  /* empty */ {
+		$$ = NULL;
 	  }
-	| direct_abstract_declarator
-	| pointer direct_abstract_declarator {
-		$$ = add_pointer($2, $1);
+	| T_ASM T_LPAREN T_STRING T_RPAREN {
+		freeyyv(&$3, T_STRING);
+		$$ = NULL;
 	  }
-	| T_TYPEOF cast_expression {	/* GCC extension */
-		$$ = mktempsym($2->tn_type);
+	| T_SYMBOLRENAME T_LPAREN T_NAME T_RPAREN {
+		$$ = $3;
 	  }
 	;
 
-direct_abstract_declarator:		/* C99 6.7.6 */
-	  T_LPAREN abstract_declarator T_RPAREN {
-		$$ = $2;
-	  }
-	| T_LBRACK T_RBRACK {
-		$$ = add_array(abstract_name(), false, 0);
-	  }
-	| T_LBRACK array_size T_RBRACK {
-		$$ = add_array(abstract_name(), true, to_int_constant($2, false));
-	  }
-	| type_attribute direct_abstract_declarator {
-		$$ = $2;
-	  }
-	| direct_abstract_declarator T_LBRACK T_RBRACK {
-		$$ = add_array($1, false, 0);
-	  }
-	| direct_abstract_declarator T_LBRACK T_ASTERISK T_RBRACK { /* C99 */
-		$$ = add_array($1, false, 0);
-	  }
-	| direct_abstract_declarator T_LBRACK array_size T_RBRACK {
-		$$ = add_array($1, true, to_int_constant($3, false));
-	  }
-	| abstract_decl_param_list asm_or_symbolrename_opt {
-		$$ = add_function(symbolrename(abstract_name(), $2), $1);
-		end_declaration_level();
-		block_level--;
-	  }
-	| direct_abstract_declarator abstract_decl_param_list asm_or_symbolrename_opt {
-		$$ = add_function(symbolrename($1, $3), $2);
-		end_declaration_level();
-		block_level--;
-	  }
-	| direct_abstract_declarator type_attribute_list
+statement:			/* C99 6.8 */
+	  expression_statement
+	| non_expr_statement
 	;
 
-non_expr_statement:
+non_expr_statement:		/* helper for C99 6.8 */
 	  type_attribute T_SEMI
 	| labeled_statement
 	| compound_statement
@@ -1405,11 +1626,6 @@ non_expr_statement:
 	| asm_statement
 	;
 
-statement:			/* C99 6.8 */
-	  expression_statement
-	| non_expr_statement
-	;
-
 labeled_statement:		/* C99 6.8.1 */
 	  label type_attribute_opt statement
 	;
@@ -1534,12 +1750,6 @@ switch_expr:			/* see C99 6.8.4 */
 	  }
 	;
 
-do_statement:			/* C99 6.8.5 */
-	  do statement {
-		clear_warning_flags();
-	  }
-	;
-
 iteration_statement:		/* C99 6.8.5 */
 	  while_expr statement {
 		clear_warning_flags();
@@ -1571,20 +1781,26 @@ iteration_statement:		/* C99 6.8.5 */
 	  }
 	;
 
-while_expr:
+while_expr:			/* see C99 6.8.5 */
 	  T_WHILE T_LPAREN expr T_RPAREN {
 		while1($3);
 		clear_warning_flags();
 	  }
 	;
 
+do_statement:			/* see C99 6.8.5 */
+	  do statement {
+		clear_warning_flags();
+	  }
+	;
+
 do:				/* see C99 6.8.5 */
 	  T_DO {
 		do1();
 	  }
 	;
 
-do_while_expr:
+do_while_expr:			/* see C99 6.8.5 */
 	  T_WHILE T_LPAREN expr T_RPAREN T_SEMI {
 		$$ = $3;
 	  }
@@ -1611,396 +1827,202 @@ for_exprs:			/* see C99 6.8.5 */
 	  }
 	;
 
-jump_statement:			/* C99 6.8.6 */
-	  goto identifier T_SEMI {
-		do_goto(getsym($2));
-	  }
-	| goto error T_SEMI {
-		symtyp = FVFT;
-	  }
-	| T_CONTINUE T_SEMI {
-		do_continue();
-	  }
-	| T_BREAK T_SEMI {
-		do_break();
-	  }
-	| T_RETURN T_SEMI {
-		do_return(NULL);
-	  }
-	| T_RETURN expr T_SEMI {
-		do_return($2);
-	  }
-	;
-
-goto:
-	  T_GOTO {
-		symtyp = FLABEL;
-	  }
-	;
-
-asm_statement:
-	  T_ASM T_LPAREN read_until_rparen T_SEMI {
-		setasm();
-	  }
-	| T_ASM T_QUAL T_LPAREN read_until_rparen T_SEMI {
-		setasm();
-	  }
-	| T_ASM error
-	;
-
-read_until_rparen:
-	  /* empty */ {
-		ignore_up_to_rparen();
-	  }
-	;
-
-constant_expr_list_opt:
-	  /* empty */
-	| constant_expr_list
-	;
-
-constant_expr_list:
-	  constant_expr
-	| constant_expr_list T_COMMA constant_expr
-	;
-
-constant_expr:			/* C99 6.6 */
-	  expr %prec T_ASSIGN
-	;
-
-expr_opt:
-	  /* empty */ {
-		$$ = NULL;
-	  }
-	| expr
-	;
-
-expr:				/* C99 6.5 */
-	  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_BITXOR 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);
-	  }
-	| cast_expression
-	;
-
-assignment_expression:		/* C99 6.5.16 */
-	  expr %prec T_ASSIGN
-	;
-
-/* K&R 7.1, C90 ???, C99 6.5.1, C11 6.5.1, C18 6.5.1 */
-primary_expression:
-	  T_NAME {
-		/* XXX really necessary? */
-		if (yychar < 0)
-			yychar = yylex();
-		$$ = new_name_node(getsym($1), yychar);
-	  }
-	| T_CON {
-		$$ = expr_new_constant(gettyp($1->v_tspec), $1);
-	  }
-	| string {
-		$$ = new_string_node($1);
-	  }
-	| T_LPAREN expr T_RPAREN {
-		if ($2 != NULL)
-			$2->tn_parenthesized = true;
-		$$ = $2;
-	  }
-	| generic_selection
-	/* GCC primary-expression, see c_parser_postfix_expression */
-	| T_BUILTIN_OFFSETOF T_LPAREN type_name T_COMMA identifier T_RPAREN {
-		symtyp = FMEMBER;
-		$$ = build_offsetof($3, getsym($5));
-	  }
-	;
-
-/* K&R ---, C90 ---, C99 ---, C11 6.5.1.1, C18 6.5.1.1 */
-generic_selection:
-	  T_GENERIC T_LPAREN assignment_expression T_COMMA
-	    generic_assoc_list T_RPAREN {
-	  	/* generic selection requires C11 or later */
-	  	c11ism(345);
-		$$ = build_generic_selection($3, $5);
-	  }
-	;
-
-/* K&R ---, C90 ---, C99 ---, C11 6.5.1.1, C18 6.5.1.1 */
-generic_assoc_list:
-	  generic_association
-	| generic_assoc_list T_COMMA generic_association {
-		$3->ga_prev = $1;
-		$$ = $3;
-	  }
-	;
-
-/* K&R ---, C90 ---, C99 ---, C11 6.5.1.1, C18 6.5.1.1 */
-generic_association:
-	  type_name T_COLON assignment_expression {
-		$$ = getblk(sizeof(*$$));
-		$$->ga_arg = $1;
-		$$->ga_result = $3;
-	  }
-	| T_DEFAULT T_COLON assignment_expression {
-		$$ = getblk(sizeof(*$$));
-		$$->ga_arg = NULL;
-		$$->ga_result = $3;
-	  }
-	;
-
-/* K&R 7.1, C90 ???, C99 6.5.2, C11 6.5.2, C18 6.5.2 */
-postfix_expression:
-	  primary_expression
-	| postfix_expression T_LBRACK expr T_RBRACK {
-		$$ = build(INDIR, build(PLUS, $1, $3), NULL);
-	  }
-	| postfix_expression T_LPAREN T_RPAREN {
-		$$ = new_function_call_node($1, NULL);
-	  }
-	| postfix_expression T_LPAREN argument_expression_list T_RPAREN {
-		$$ = new_function_call_node($1, $3);
-	  }
-	| postfix_expression 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;
-		}
-	  }
-	| postfix_expression T_INCDEC {
-		$$ = build($2 == INC ? INCAFT : DECAFT, $1, NULL);
-	  }
-	| T_LPAREN type_name T_RPAREN {	/* C99 6.5.2.5 "Compound literals" */
-		sym_t *tmp = mktempsym($2);
-		begin_initialization(tmp);
-		cgram_declare(tmp, true, NULL);
-	  } init_lbrace initializer_list comma_opt init_rbrace {
-		if (!Sflag)
-			 /* compound literals are a C9X/GCC extension */
-			 gnuism(319);
-		$$ = new_name_node(*current_initsym(), 0);
-		end_initialization();
-	  }
-	| T_LPAREN compound_statement_lbrace gcc_statement_expr_list {
-		block_level--;
-		mem_block_level--;
-		begin_initialization(mktempsym(dup_type($3->tn_type)));
-		mem_block_level++;
-		block_level++;
-		/* ({ }) is a GCC extension */
-		gnuism(320);
-	  } compound_statement_rbrace T_RPAREN {
-		$$ = new_name_node(*current_initsym(), 0);
-		end_initialization();
-	  }
-	;
-
-/* K&R 7.1, C90 ???, C99 6.5.2, C11 6.5.2, C18 6.5.2 */
-argument_expression_list:
-	  expr %prec T_COMMA {
-		$$ = new_function_argument_node(NULL, $1);
-	  }
-	| argument_expression_list T_COMMA expr {
-		$$ = new_function_argument_node($1, $3);
-	  }
-	;
-
-/* K&R 7.2, C90 ???, C99 6.5.3, C11 6.5.3, C18 6.5.3 */
-unary_expression:
-	  postfix_expression
-	| T_INCDEC unary_expression {
-		$$ = build($1 == INC ? INCBEF : DECBEF, $2, NULL);
-	  }
-	| T_AMPER cast_expression {
-		$$ = build(ADDR, $2, NULL);
-	  }
-	| T_ASTERISK cast_expression {
-		$$ = build(INDIR, $2, NULL);
+jump_statement:			/* C99 6.8.6 */
+	  goto identifier T_SEMI {
+		do_goto(getsym($2));
 	  }
-	| T_ADDITIVE cast_expression {
-		if (tflag && $1 == PLUS) {
-			/* unary + is illegal in traditional C */
-			warning(100);
-		}
-		$$ = build($1 == PLUS ? UPLUS : UMINUS, $2, NULL);
+	| goto error T_SEMI {
+		symtyp = FVFT;
 	  }
-	| T_COMPLEMENT cast_expression {
-		$$ = build(COMPL, $2, NULL);
+	| T_CONTINUE T_SEMI {
+		do_continue();
 	  }
-	| T_LOGNOT cast_expression {
-		$$ = build(NOT, $2, NULL);
+	| T_BREAK T_SEMI {
+		do_break();
 	  }
-	| T_REAL cast_expression {	/* GCC c_parser_unary_expression */
-		$$ = build(REAL, $2, NULL);
+	| T_RETURN T_SEMI {
+		do_return(NULL);
 	  }
-	| T_IMAG cast_expression {	/* GCC c_parser_unary_expression */
-		$$ = build(IMAG, $2, NULL);
+	| T_RETURN expr T_SEMI {
+		do_return($2);
 	  }
-	| T_EXTENSION cast_expression {	/* GCC c_parser_unary_expression */
-		$$ = $2;
+	;
+
+goto:				/* see C99 6.8.6 */
+	  T_GOTO {
+		symtyp = FLABEL;
 	  }
-	| T_SIZEOF unary_expression {
-		$$ = $2 == NULL ? NULL : build_sizeof($2->tn_type);
-		if ($$ != NULL)
-			check_expr_misc($2, false, false, false, false, false, true);
+	;
+
+asm_statement:			/* GCC extension */
+	  T_ASM T_LPAREN read_until_rparen T_SEMI {
+		setasm();
 	  }
-	| T_SIZEOF T_LPAREN type_name T_RPAREN {
-		$$ = build_sizeof($3);
+	| T_ASM T_QUAL T_LPAREN read_until_rparen T_SEMI {
+		setasm();
 	  }
-	/* K&R ---, C90 ---, C99 ---, C11 6.5.3, C18 6.5.3 */
-	| T_ALIGNOF T_LPAREN type_name T_RPAREN {
-		$$ = build_alignof($3);
+	| T_ASM error
+	;
+
+read_until_rparen:		/* helper for 'asm_statement' */
+	  /* empty */ {
+		ignore_up_to_rparen();
 	  }
 	;
 
-/* K&R 7.2, C90 ???, C99 6.5.4, C11 6.5.4, C18 6.5.4 */
-cast_expression:
-	  unary_expression
-	| T_LPAREN type_name T_RPAREN cast_expression {
-		$$ = cast($4, $2);
+translation_unit:		/* C99 6.9 */
+	  external_declaration
+	| translation_unit external_declaration
+	;
+
+external_declaration:		/* C99 6.9 */
+	  asm_statement
+	| function_definition {
+		global_clean_up_decl(false);
+		clear_warning_flags();
+	  }
+	| top_level_declaration {
+		global_clean_up_decl(false);
+		clear_warning_flags();
 	  }
 	;
 
 /*
- * The inner part of a GCC statement-expression of the form ({ ... }).
+ * On the top level, lint allows several forms of declarations that it doesn't
+ * allow in functions.  For example, a single ';' is an empty declaration and
+ * is supported by some compilers, but in a function it would be an empty
+ * statement, not a declaration.  This makes a difference in C90 mode, where
+ * a statement must not be followed by a declaration.
  *
- * https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
+ * See 'declaration' for all other declarations.
  */
-gcc_statement_expr_list:
-	  gcc_statement_expr_item
-	| gcc_statement_expr_list gcc_statement_expr_item {
-		$$ = $2;
-	  }
-	;
-
-gcc_statement_expr_item:
-	  declaration {
-		clear_warning_flags();
-		$$ = NULL;
+top_level_declaration:		/* C99 6.9 calls this 'declaration' */
+	  T_SEMI {
+		if (sflag) {
+			/* empty declaration */
+			error(0);
+		} else if (!tflag) {
+			/* empty declaration */
+			warning(0);
+		}
 	  }
-	| non_expr_statement {
-		$$ = expr_zalloc_tnode();
-		$$->tn_type = gettyp(VOID);
+	| begin_type end_type notype_init_decls T_SEMI {
+		if (sflag) {
+			/* old style declaration; add 'int' */
+			error(1);
+		} else if (!tflag) {
+			/* old style declaration; add 'int' */
+			warning(1);
+		}
 	  }
-	| expr T_SEMI {
-		if ($1 == NULL) {	/* in case of syntax errors */
-			$$ = expr_zalloc_tnode();
-			$$->tn_type = gettyp(VOID);
+	| begin_type_declmods end_type T_SEMI {
+		if (dcs->d_scl == TYPEDEF) {
+			/* typedef declares no type name */
+			warning(72);
 		} else {
-			/* XXX: 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);
-			seen_fallthrough = false;
+			/* empty declaration */
+			warning(2);
 		}
 	  }
-	;
-
-string:
-	  T_STRING
-	| T_STRING string2 {
-		$$ = cat_strings($1, $2);
+	| begin_type_declmods end_type notype_init_decls T_SEMI
+	| begin_type_declaration_specifiers end_type T_SEMI {
+		if (dcs->d_scl == TYPEDEF) {
+			/* typedef declares no type name */
+			warning(72);
+		} else if (!dcs->d_nonempty_decl) {
+			/* empty declaration */
+			warning(2);
+		}
+	  }
+	| begin_type_declaration_specifiers end_type type_init_decls T_SEMI
+	| error T_SEMI {
+		global_clean_up();
+	  }
+	| error T_RBRACE {
+		global_clean_up();
 	  }
 	;
 
-string2:
-	  T_STRING {
-		if (tflag) {
-			/* concatenated strings are illegal in traditional C */
-			warning(219);
+function_definition:		/* C99 6.9.1 */
+	  func_decl {
+		if ($1->s_type->t_tspec != FUNC) {
+			/* syntax error '%s' */
+			error(249, yytext);
+			YYERROR;
 		}
-		$$ = $1;
-	  }
-	| string2 T_STRING {
-		$$ = cat_strings($1, $2);
+		if ($1->s_type->t_typedef) {
+			/* ()-less function definition */
+			error(64);
+			YYERROR;
+		}
+		funcdef($1);
+		block_level++;
+		begin_declaration_level(ARG);
+		if (lwarn == LWARN_NONE)
+			$1->s_used = true;
+	  } arg_declaration_list_opt {
+		end_declaration_level();
+		block_level--;
+		check_func_lint_directives();
+		check_func_old_style_arguments();
+		begin_control_statement(CS_FUNCTION_BODY);
+	  } compound_statement {
+		funcend();
+		end_control_statement(CS_FUNCTION_BODY);
 	  }
 	;
 
-point_or_arrow:
-	  T_POINT {
-		symtyp = FMEMBER;
-		$$ = POINT;
+func_decl:
+	  begin_type end_type notype_decl {
+		$$ = $3;
 	  }
-	| T_ARROW {
-		symtyp = FMEMBER;
-		$$ = ARROW;
+	| begin_type_declmods end_type notype_decl {
+		$$ = $3;
+	  }
+	| begin_type_declaration_specifiers end_type type_decl {
+		$$ = $3;
 	  }
 	;
 
-identifier_sym:
-	  identifier {
-		$$ = getsym($1);
-	  }
+arg_declaration_list_opt:	/* C99 6.9.1p13 example 1 */
+	  /* empty */
+	| arg_declaration_list
 	;
 
-identifier:			/* C99 6.4.2.1 */
-	  T_NAME {
-		$$ = $1;
-		cgram_debug("name '%s'", $$->sb_name);
-	  }
-	| T_TYPENAME {
-		$$ = $1;
-		cgram_debug("typename '%s'", $$->sb_name);
-	  }
+arg_declaration_list:		/* C99 6.9.1p13 example 1 */
+	  arg_declaration
+	| arg_declaration_list arg_declaration
+	/* XXX or better "arg_declaration error" ? */
+	| error
 	;
 
-comma_opt:
-	  /* empty */
-	| T_COMMA
+/*
+ * "arg_declaration" is separated from "declaration" because it
+ * needs other error handling.
+ */
+arg_declaration:
+	  begin_type_declmods end_type T_SEMI {
+		/* empty declaration */
+		warning(2);
+	  }
+	| begin_type_declmods end_type notype_init_decls T_SEMI
+	| begin_type_declaration_specifiers end_type T_SEMI {
+		if (!dcs->d_nonempty_decl) {
+			/* empty declaration */
+			warning(2);
+		} else {
+			/* '%s' declared in argument declaration list */
+			warning(3, type_name(dcs->d_type));
+		}
+	  }
+	| begin_type_declaration_specifiers end_type type_init_decls T_SEMI {
+		if (dcs->d_nonempty_decl) {
+			/* '%s' declared in argument declaration list */
+			warning(3, type_name(dcs->d_type));
+		}
+	  }
+	| begin_type_declmods error
+	| begin_type_declaration_specifiers error
 	;
 
 gcc_attribute_spec_list:

Reply via email to