diff options
| author | phil <phil@NetBSD.org> | 1996-02-28 21:01:27 +0000 |
|---|---|---|
| committer | phil <phil@NetBSD.org> | 1996-02-28 21:01:27 +0000 |
| commit | 4d30f3b4686a8afa41cc5ea8f1cc535b44cc8446 (patch) | |
| tree | 2d52de83432f5191e1804733261f3937f5874108 /gnu | |
| parent | 2d885d15e97e336e6c7fb30125806c14d1b93191 (diff) | |
Updates to get the .y files in our source tree.
Diffstat (limited to 'gnu')
| -rw-r--r-- | gnu/usr.bin/gcc/cc1/c-parse.y | 2068 | ||||
| -rw-r--r-- | gnu/usr.bin/gcc/cc1obj/objc-parse.y | 2820 | ||||
| -rw-r--r-- | gnu/usr.bin/gcc/cc1plus/parse.y | 3906 | ||||
| -rw-r--r-- | gnu/usr.bin/gcc/common/bi-parser.y | 169 | ||||
| -rw-r--r-- | gnu/usr.bin/gcc/common/c-parse.y | 2068 | ||||
| -rw-r--r-- | gnu/usr.bin/gcc/cpp/cexp.y | 1047 | ||||
| -rw-r--r-- | gnu/usr.bin/gcc/gcc2netbsd | 12 |
7 files changed, 12085 insertions, 5 deletions
diff --git a/gnu/usr.bin/gcc/cc1/c-parse.y b/gnu/usr.bin/gcc/cc1/c-parse.y new file mode 100644 index 00000000000..e7aed2c036c --- /dev/null +++ b/gnu/usr.bin/gcc/cc1/c-parse.y @@ -0,0 +1,2068 @@ +/* YACC parser for C syntax and for Objective C. -*-c-*- + Copyright (C) 1987, 88, 89, 92, 93, 94, 1995 Free Software Foundation, Inc. + +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 file defines the grammar of C and that of Objective C. + ifobjc ... end ifobjc conditionals contain code for Objective C only. + ifc ... end ifc conditionals contain code for C only. + Sed commands in Makefile.in are used to convert this file into + c-parse.y and into objc-parse.y. */ + +/* To whomever it may concern: I have heard that such a thing was once + written by AT&T, but I have never seen it. */ + +%expect 34 + +/* These are the 23 conflicts you should get in parse.output; + the state numbers may vary if minor changes in the grammar are made. + +State 42 contains 1 shift/reduce conflict. (Two ways to parse ATTRIBUTE.) +State 44 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 103 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 110 contains 1 shift/reduce conflict. (Two ways to parse ATTRIBUTE.) +State 111 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 115 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 132 contains 1 shift/reduce conflict. (See comment at component_decl.) +State 180 contains 1 shift/reduce conflict. (Two ways to parse ATTRIBUTE.) +State 194 contains 2 shift/reduce conflict. (Four ways to parse this.) +State 202 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 214 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 220 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 304 contains 2 shift/reduce conflicts. (Four ways to parse this.) +State 335 contains 2 shift/reduce conflicts. (Four ways to parse this.) +State 347 contains 1 shift/reduce conflict. (Two ways to parse ATTRIBUTES.) +State 352 contains 1 shift/reduce conflict. (Two ways to parse ATTRIBUTES.) +State 383 contains 2 shift/reduce conflicts. (Four ways to parse this.) +State 434 contains 2 shift/reduce conflicts. (Four ways to parse this.) */ + + +%{ +#include <stdio.h> +#include <errno.h> +#include <setjmp.h> + +#include "config.h" +#include "tree.h" +#include "input.h" +#include "c-lex.h" +#include "c-tree.h" +#include "flags.h" + +#ifdef MULTIBYTE_CHARS +#include <stdlib.h> +#include <locale.h> +#endif + + +/* Since parsers are distinct for each language, put the language string + definition here. */ +char *language_string = "GNU C"; + +#ifndef errno +extern int errno; +#endif + +void yyerror (); + +/* Like YYERROR but do call yyerror. */ +#define YYERROR1 { yyerror ("syntax error"); YYERROR; } + +/* Cause the `yydebug' variable to be defined. */ +#define YYDEBUG 1 +%} + +%start program + +%union {long itype; tree ttype; enum tree_code code; + char *filename; int lineno; int ends_in_label; } + +/* 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 TYPEOF ALIGNOF +%token ATTRIBUTE EXTENSION LABEL +%token REALPART IMAGPART + +/* Add precedence rules to solve dangling else s/r conflict */ +%nonassoc IF +%nonassoc ELSE + +/* 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. */ + +%right <code> ASSIGN '=' +%right <code> '?' ':' +%left <code> OROR +%left <code> ANDAND +%left <code> '|' +%left <code> '^' +%left <code> '&' +%left <code> EQCOMPARE +%left <code> ARITHCOMPARE +%left <code> LSHIFT RSHIFT +%left <code> '+' '-' +%left <code> '*' '/' '%' +%right <code> UNARY PLUSPLUS MINUSMINUS +%left HYPERUNARY +%left <code> POINTSAT '.' '(' '[' + +/* The Objective-C keywords. These are included in C and in + Objective C, so that the token codes are the same in both. */ +%token INTERFACE IMPLEMENTATION END SELECTOR DEFS ENCODE +%token CLASSNAME PUBLIC PRIVATE PROTECTED PROTOCOL OBJECTNAME CLASS ALIAS + +/* Objective-C string constants in raw form. + yylval is an OBJC_STRING_CST node. */ +%token OBJC_STRING + + +%type <code> unop + +%type <ttype> identifier IDENTIFIER TYPENAME CONSTANT expr nonnull_exprlist exprlist +%type <ttype> expr_no_commas cast_expr unary_expr primary string STRING +%type <ttype> typed_declspecs reserved_declspecs +%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 <ttype> initdecls notype_initdecls initdcl notype_initdcl +%type <ttype> init maybeasm +%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 + +%type <ttype> declarator +%type <ttype> notype_declarator after_type_declarator +%type <ttype> parm_declarator + +%type <ttype> structsp component_decl_list component_decl_list2 +%type <ttype> component_decl components component_declarator +%type <ttype> enumlist enumerator +%type <ttype> typename absdcl absdcl1 type_quals +%type <ttype> xexpr parms parm identifiers + +%type <ttype> parmlist parmlist_1 parmlist_2 +%type <ttype> parmlist_or_identifiers parmlist_or_identifiers_1 +%type <ttype> identifiers_or_typenames + +%type <itype> setspecs + +%type <ends_in_label> lineno_stmt_or_label lineno_stmt_or_labels stmt_or_label + +%type <filename> save_filename +%type <lineno> save_lineno + + +%{ +/* Number of statements (loosely speaking) seen so far. */ +static int stmt_count; + +/* Input file and line number of the end of the body of last simple_if; + used by the stmt-rule immediately after simple_if returns. */ +static char *if_stmt_file; +static int if_stmt_line; + +/* List of types and structure classes of the current declaration. */ +static tree current_declspecs; +static tree prefix_attributes = NULL_TREE; + +/* Stack of saved values of current_declspecs and prefix_attributes. */ +static tree declspec_stack; + +/* 1 if we explained undeclared var errors. */ +static int undeclared_variable_notice; + + +/* 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 (); +%} + +%% +program: /* empty */ + { if (pedantic) + pedwarn ("ANSI C forbids an empty source file"); + finish_file (); + } + | extdefs + { + /* In case there were missing closebraces, + get us back to the global binding level. */ + while (! global_bindings_p ()) + poplevel (0, 0, 0); + 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; } extdef + | extdefs {$<ttype>$ = NULL_TREE; } extdef + ; + +extdef: + fndef + | datadef + | ASM_KEYWORD '(' expr ')' ';' + { STRIP_NOPS ($3); + if ((TREE_CODE ($3) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND ($3, 0)) == STRING_CST) + || TREE_CODE ($3) == STRING_CST) + assemble_asm ($3); + else + error ("argument of `asm' is not a constant string"); } + ; + +datadef: + setspecs notype_initdecls ';' + { if (pedantic) + error ("ANSI C forbids data definition with no type or storage class"); + else if (!flag_traditional) + warning ("data definition has no type or storage class"); + + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($1); } + | declmods setspecs notype_initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs setspecs initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods ';' + { pedwarn ("empty declaration"); } + | typed_declspecs ';' + { shadow_tag ($1); } + | error ';' + | error '}' + | ';' + { if (pedantic) + pedwarn ("ANSI C does not allow extra `;' outside of a function"); } + ; + +fndef: + typed_declspecs setspecs declarator + { if (! start_function ($1, $3, prefix_attributes, + NULL_TREE, 0)) + YYERROR1; + reinit_parse_for_function (); } + xdecls + { store_parm_decls (); } + compstmt_or_error + { finish_function (0); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs setspecs declarator error + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_declarator + { if (! start_function ($1, $3, prefix_attributes, + NULL_TREE, 0)) + YYERROR1; + reinit_parse_for_function (); } + xdecls + { store_parm_decls (); } + compstmt_or_error + { finish_function (0); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_declarator error + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | setspecs notype_declarator + { if (! start_function (NULL_TREE, $2, + prefix_attributes, NULL_TREE, 0)) + YYERROR1; + reinit_parse_for_function (); } + xdecls + { store_parm_decls (); } + compstmt_or_error + { finish_function (0); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($1); } + | setspecs notype_declarator error + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($1); } + ; + +identifier: + IDENTIFIER + | TYPENAME + ; + +unop: '&' + { $$ = ADDR_EXPR; } + | '-' + { $$ = NEGATE_EXPR; } + | '+' + { $$ = CONVERT_EXPR; } + | PLUSPLUS + { $$ = PREINCREMENT_EXPR; } + | MINUSMINUS + { $$ = PREDECREMENT_EXPR; } + | '~' + { $$ = BIT_NOT_EXPR; } + | '!' + { $$ = TRUTH_NOT_EXPR; } + ; + +expr: nonnull_exprlist + { $$ = build_compound_expr ($1); } + ; + +exprlist: + /* empty */ + { $$ = NULL_TREE; } + | nonnull_exprlist + ; + +nonnull_exprlist: + expr_no_commas + { $$ = build_tree_list (NULL_TREE, $1); } + | nonnull_exprlist ',' expr_no_commas + { chainon ($1, build_tree_list (NULL_TREE, $3)); } + ; + +unary_expr: + primary + | '*' cast_expr %prec UNARY + { $$ = build_indirect_ref ($2, "unary *"); } + /* __extension__ turns off -pedantic for following primary. */ + | EXTENSION + { $<itype>1 = pedantic; + pedantic = 0; } + cast_expr %prec UNARY + { $$ = $3; + pedantic = $<itype>1; } + | unop cast_expr %prec UNARY + { $$ = build_unary_op ($1, $2, 0); + overflow_warning ($$); } + /* Refer to the address of a label as a pointer. */ + | ANDAND identifier + { tree label = lookup_label ($2); + if (pedantic) + pedwarn ("ANSI C forbids `&&'"); + if (label == 0) + $$ = null_pointer_node; + else + { + TREE_USED (label) = 1; + $$ = build1 (ADDR_EXPR, ptr_type_node, label); + TREE_CONSTANT ($$) = 1; + } + } +/* This seems to be impossible on some machines, so let's turn it off. + You can use __builtin_next_arg to find the anonymous stack args. + | '&' ELLIPSIS + { tree types = TYPE_ARG_TYPES (TREE_TYPE (current_function_decl)); + $$ = error_mark_node; + if (TREE_VALUE (tree_last (types)) == void_type_node) + error ("`&...' used in function with fixed number of arguments"); + else + { + if (pedantic) + pedwarn ("ANSI C forbids `&...'"); + $$ = tree_last (DECL_ARGUMENTS (current_function_decl)); + $$ = build_unary_op (ADDR_EXPR, $$, 0); + } } +*/ + | 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"); + $$ = c_sizeof (TREE_TYPE ($2)); } + | SIZEOF '(' typename ')' %prec HYPERUNARY + { $$ = c_sizeof (groktypename ($3)); } + | ALIGNOF unary_expr %prec UNARY + { $$ = c_alignof_expr ($2); } + | ALIGNOF '(' typename ')' %prec HYPERUNARY + { $$ = c_alignof (groktypename ($3)); } + | REALPART cast_expr %prec UNARY + { $$ = build_unary_op (REALPART_EXPR, $2, 0); } + | IMAGPART cast_expr %prec UNARY + { $$ = build_unary_op (IMAGPART_EXPR, $2, 0); } + ; + +cast_expr: + unary_expr + | '(' typename ')' cast_expr %prec UNARY + { tree type = groktypename ($2); + $$ = build_c_cast (type, $4); } + | '(' typename ')' '{' + { start_init (NULL_TREE, NULL, 0); + $2 = groktypename ($2); + really_start_incremental_init ($2); } + initlist_maybe_comma '}' %prec UNARY + { char *name; + tree result = pop_init_level (0); + tree type = $2; + finish_init (); + + if (pedantic) + pedwarn ("ANSI C forbids constructor expressions"); + if (TYPE_NAME (type) != 0) + { + if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) + name = IDENTIFIER_POINTER (TYPE_NAME (type)); + else + name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); + } + else + name = ""; + $$ = result; + if (TREE_CODE (type) == ARRAY_TYPE && TYPE_SIZE (type) == 0) + { + int failure = complete_array_type (type, $$, 1); + if (failure) + abort (); + } + } + ; + +expr_no_commas: + cast_expr + | expr_no_commas '+' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '-' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '*' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '/' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '%' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas LSHIFT expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas RSHIFT expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas ARITHCOMPARE expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas EQCOMPARE expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '&' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '|' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '^' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas ANDAND expr_no_commas + { $$ = parser_build_binary_op (TRUTH_ANDIF_EXPR, $1, $3); } + | expr_no_commas OROR expr_no_commas + { $$ = parser_build_binary_op (TRUTH_ORIF_EXPR, $1, $3); } + | expr_no_commas '?' xexpr ':' expr_no_commas + { $$ = build_conditional_expr ($1, $3, $5); } + | expr_no_commas '=' expr_no_commas + { $$ = build_modify_expr ($1, NOP_EXPR, $3); + C_SET_EXP_ORIGINAL_CODE ($$, MODIFY_EXPR); } + | expr_no_commas ASSIGN expr_no_commas + { $$ = build_modify_expr ($1, $2, $3); + /* This inhibits warnings in truthvalue_conversion. */ + C_SET_EXP_ORIGINAL_CODE ($$, ERROR_MARK); } + ; + +primary: + IDENTIFIER + { + $$ = lastiddecl; + if (!$$ || $$ == error_mark_node) + { + if (yychar == YYEMPTY) + yychar = YYLEX; + if (yychar == '(') + { + { + /* Ordinary implicit function declaration. */ + $$ = implicitly_declare ($1); + assemble_external ($$); + TREE_USED ($$) = 1; + } + } + else if (current_function_decl == 0) + { + error ("`%s' undeclared here (not in a function)", + IDENTIFIER_POINTER ($1)); + $$ = error_mark_node; + } + else + { + { + if (IDENTIFIER_GLOBAL_VALUE ($1) != error_mark_node + || IDENTIFIER_ERROR_LOCUS ($1) != current_function_decl) + { + error ("`%s' undeclared (first use this function)", + IDENTIFIER_POINTER ($1)); + + if (! undeclared_variable_notice) + { + error ("(Each undeclared identifier is reported only once"); + error ("for each function it appears in.)"); + undeclared_variable_notice = 1; + } + } + $$ = error_mark_node; + /* Prevent repeated error messages. */ + IDENTIFIER_GLOBAL_VALUE ($1) = error_mark_node; + IDENTIFIER_ERROR_LOCUS ($1) = current_function_decl; + } + } + } + else if (TREE_TYPE ($$) == error_mark_node) + $$ = error_mark_node; + else if (C_DECL_ANTICIPATED ($$)) + { + /* The first time we see a build-in function used, + if it has not been declared. */ + C_DECL_ANTICIPATED ($$) = 0; + if (yychar == YYEMPTY) + yychar = YYLEX; + if (yychar == '(') + { + /* Omit the implicit declaration we + would ordinarily do, so we don't lose + the actual built in type. + But print a diagnostic for the mismatch. */ + if (TREE_CODE ($$) != FUNCTION_DECL) + error ("`%s' implicitly declared as function", + IDENTIFIER_POINTER (DECL_NAME ($$))); + else if ((TYPE_MODE (TREE_TYPE (TREE_TYPE ($$))) + != TYPE_MODE (integer_type_node)) + && (TREE_TYPE (TREE_TYPE ($$)) + != void_type_node)) + pedwarn ("type mismatch in implicit declaration for built-in function `%s'", + IDENTIFIER_POINTER (DECL_NAME ($$))); + /* If it really returns void, change that to int. */ + if (TREE_TYPE (TREE_TYPE ($$)) == void_type_node) + TREE_TYPE ($$) + = build_function_type (integer_type_node, + TYPE_ARG_TYPES (TREE_TYPE ($$))); + } + else + pedwarn ("built-in function `%s' used without declaration", + IDENTIFIER_POINTER (DECL_NAME ($$))); + + /* Do what we would ordinarily do when a fn is used. */ + assemble_external ($$); + TREE_USED ($$) = 1; + } + else + { + assemble_external ($$); + TREE_USED ($$) = 1; + } + + if (TREE_CODE ($$) == CONST_DECL) + { + $$ = 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; + } + } + | CONSTANT + | string + { $$ = combine_strings ($1); } + | '(' expr ')' + { char class = TREE_CODE_CLASS (TREE_CODE ($2)); + if (class == 'e' || class == '1' + || class == '2' || class == '<') + C_SET_EXP_ORIGINAL_CODE ($2, ERROR_MARK); + $$ = $2; } + | '(' error ')' + { $$ = error_mark_node; } + | '(' + { if (current_function_decl == 0) + { + error ("braced-group within expression allowed only inside a function"); + YYERROR; + } + /* We must force a BLOCK for this level + so that, if it is not expanded later, + there is a way to turn off the entire subtree of blocks + that are contained in it. */ + keep_next_level (); + push_iterator_stack (); + push_label_level (); + $<ttype>$ = expand_start_stmt_expr (); } + compstmt ')' + { tree rtl_exp; + if (pedantic) + pedwarn ("ANSI C forbids braced-groups within expressions"); + pop_iterator_stack (); + pop_label_level (); + 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 '(' exprlist ')' %prec '.' + { $$ = build_function_call ($1, $3); } + | primary '[' expr ']' %prec '.' + { $$ = build_array_ref ($1, $3); } + | primary '.' identifier + { + $$ = build_component_ref ($1, $3); + } + | primary POINTSAT identifier + { + tree expr = build_indirect_ref ($1, "->"); + + $$ = build_component_ref (expr, $3); + } + | primary PLUSPLUS + { $$ = build_unary_op (POSTINCREMENT_EXPR, $1, 0); } + | primary MINUSMINUS + { $$ = build_unary_op (POSTDECREMENT_EXPR, $1, 0); } + ; + +/* Produces a STRING_CST with perhaps more STRING_CSTs chained onto it. */ +string: + STRING + | string STRING + { $$ = chainon ($1, $2); } + ; + + +xdecls: + /* empty */ + | datadecls + | datadecls ELLIPSIS + /* ... is used here to indicate a varargs function. */ + { c_mark_varargs (); + if (pedantic) + pedwarn ("ANSI C does not permit use of `varargs.h'"); } + ; + +/* The following are analogous to lineno_decl, decls and decl + except that they do not allow nested functions. + They are used for old-style parm decls. */ +lineno_datadecl: + save_filename save_lineno datadecl + { } + ; + +datadecls: + lineno_datadecl + | errstmt + | datadecls lineno_datadecl + | lineno_datadecl errstmt + ; + +datadecl: + typed_declspecs setspecs initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs ';' + { shadow_tag_warned ($1, 1); + pedwarn ("empty declaration"); } + | declmods ';' + { pedwarn ("empty declaration"); } + ; + +/* This combination which saves a lineno before a decl + is the normal thing to use, rather than decl itself. + This is to avoid shift/reduce conflicts in contexts + where statement labels are allowed. */ +lineno_decl: + save_filename save_lineno decl + { } + ; + +decls: + lineno_decl + | errstmt + | decls lineno_decl + | lineno_decl errstmt + ; + +/* records the type and storage class specs to use for processing + the declarators that follow. + Maintains a stack of outer-level values of current_declspecs, + for the sake of parm declarations nested in function declarators. */ +setspecs: /* empty */ + { $$ = suspend_momentary (); + pending_xref_error (); + declspec_stack = tree_cons (prefix_attributes, + current_declspecs, + declspec_stack); + current_declspecs = $<ttype>0; + prefix_attributes = NULL_TREE; } + ; + +setattrs: /* empty */ + { prefix_attributes = chainon (prefix_attributes, $<ttype>0); } + ; + +decl: + typed_declspecs setspecs initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs setspecs nested_function + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_nested_function + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs ';' + { shadow_tag ($1); } + | declmods ';' + { pedwarn ("empty declaration"); } + ; + +/* 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. */ + +typed_declspecs: + typespec reserved_declspecs + { $$ = tree_cons (NULL_TREE, $1, $2); } + | declmods typespec reserved_declspecs + { $$ = chainon ($3, tree_cons (NULL_TREE, $2, $1)); } + ; + +reserved_declspecs: /* empty */ + { $$ = NULL_TREE; } + | reserved_declspecs typespecqual_reserved + { $$ = tree_cons (NULL_TREE, $2, $1); } + | reserved_declspecs SCSPEC + { if (extra_warnings) + warning ("`%s' is not at beginning of declaration", + IDENTIFIER_POINTER ($2)); + $$ = tree_cons (NULL_TREE, $2, $1); } + ; + +/* 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. */ + +declmods: + TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $1, NULL_TREE); + TREE_STATIC ($$) = 1; } + | SCSPEC + { $$ = tree_cons (NULL_TREE, $1, NULL_TREE); } + | declmods TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $2, $1); + TREE_STATIC ($$) = 1; } + | declmods SCSPEC + { if (extra_warnings && TREE_STATIC ($1)) + warning ("`%s' is not at beginning of declaration", + IDENTIFIER_POINTER ($2)); + $$ = tree_cons (NULL_TREE, $2, $1); + TREE_STATIC ($$) = TREE_STATIC ($1); } + ; + + +/* Used instead of declspecs where storage classes are not allowed + (that is, for typenames and structure components). + Don't accept a typedef-name if anything but a modifier precedes it. */ + +typed_typespecs: + typespec reserved_typespecquals + { $$ = tree_cons (NULL_TREE, $1, $2); } + | nonempty_type_quals typespec reserved_typespecquals + { $$ = chainon ($3, tree_cons (NULL_TREE, $2, $1)); } + ; + +reserved_typespecquals: /* empty */ + { $$ = NULL_TREE; } + | reserved_typespecquals typespecqual_reserved + { $$ = tree_cons (NULL_TREE, $2, $1); } + ; + +/* 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: TYPESPEC + | structsp + | TYPENAME + { /* For a typedef name, record the meaning, not the name. + In case of `foo foo, bar;'. */ + $$ = lookup_name ($1); } + | TYPEOF '(' expr ')' + { $$ = TREE_TYPE ($3); } + | TYPEOF '(' typename ')' + { $$ = groktypename ($3); } + ; + +/* A typespec that is a reserved word, or a type qualifier. */ + +typespecqual_reserved: TYPESPEC + | TYPE_QUAL + | structsp + ; + +initdecls: + initdcl + | initdecls ',' initdcl + ; + +notype_initdecls: + notype_initdcl + | notype_initdecls ',' initdcl + ; + +maybeasm: + /* empty */ + { $$ = NULL_TREE; } + | ASM_KEYWORD '(' string ')' + { if (TREE_CHAIN ($3)) $3 = combine_strings ($3); + $$ = $3; + } + ; + +initdcl: + declarator maybeasm maybe_attribute '=' + { $<ttype>$ = start_decl ($1, current_declspecs, 1, + $3, prefix_attributes); + start_init ($<ttype>$, $2, global_bindings_p ()); } + init +/* Note how the declaration of the variable is in effect while its init is parsed! */ + { finish_init (); + finish_decl ($<ttype>5, $6, $2); } + | declarator maybeasm maybe_attribute + { tree d = start_decl ($1, current_declspecs, 0, + $3, prefix_attributes); + finish_decl (d, NULL_TREE, $2); + } + ; + +notype_initdcl: + notype_declarator maybeasm maybe_attribute '=' + { $<ttype>$ = start_decl ($1, current_declspecs, 1, + $3, prefix_attributes); + start_init ($<ttype>$, $2, global_bindings_p ()); } + init +/* Note how the declaration of the variable is in effect while its init is parsed! */ + { finish_init (); + decl_attributes ($<ttype>5, $3, prefix_attributes); + finish_decl ($<ttype>5, $6, $2); } + | notype_declarator maybeasm maybe_attribute + { tree d = start_decl ($1, current_declspecs, 0, + $3, prefix_attributes); + finish_decl (d, NULL_TREE, $2); } + ; +/* 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 '(' 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 + ; + +/* Initializers. `init' is the entry point. */ + +init: + expr_no_commas + | '{' + { really_start_incremental_init (NULL_TREE); + /* Note that the call to clear_momentary + is in process_init_element. */ + push_momentary (); } + initlist_maybe_comma '}' + { $$ = pop_init_level (0); + if ($$ == error_mark_node + && ! (yychar == STRING || yychar == CONSTANT)) + pop_momentary (); + else + pop_momentary_nofree (); } + + | error + { $$ = error_mark_node; } + ; + +/* `initlist_maybe_comma' is the guts of an initializer in braces. */ +initlist_maybe_comma: + /* empty */ + { if (pedantic) + pedwarn ("ANSI C forbids empty initializer braces"); } + | initlist1 maybecomma + ; + +initlist1: + initelt + | initlist1 ',' initelt + ; + +/* `initelt' is a single element of an initializer. + It may use braces. */ +initelt: + expr_no_commas + { process_init_element ($1); } + | '{' + { push_init_level (0); } + initlist_maybe_comma '}' + { process_init_element (pop_init_level (0)); } + | error + /* 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 ELLIPSIS expr_no_commas ']' '=' + { set_init_index ($2, $4); } + initelt + | '[' expr_no_commas ']' '=' + { set_init_index ($2, NULL_TREE); } + initelt + | '[' expr_no_commas ']' + { set_init_index ($2, NULL_TREE); } + initelt + | identifier ':' + { set_init_label ($1); } + initelt + | '.' identifier '=' + { set_init_label ($2); } + initelt + ; + +nested_function: + declarator + { push_c_function_context (); + if (! start_function (current_declspecs, $1, + prefix_attributes, NULL_TREE, 1)) + { + pop_c_function_context (); + YYERROR1; + } + reinit_parse_for_function (); } + xdecls + { store_parm_decls (); } +/* This used to use compstmt_or_error. + That caused a bug with input `f(g) int g {}', + where the use of YYERROR1 above caused an error + which then was handled by compstmt_or_error. + There followed a repeated execution of that same rule, + which called YYERROR1 again, and so on. */ + compstmt + { finish_function (1); + pop_c_function_context (); } + ; + +notype_nested_function: + notype_declarator + { push_c_function_context (); + if (! start_function (current_declspecs, $1, + prefix_attributes, NULL_TREE, 1)) + { + pop_c_function_context (); + YYERROR1; + } + reinit_parse_for_function (); } + xdecls + { store_parm_decls (); } +/* This used to use compstmt_or_error. + That caused a bug with input `f(g) int g {}', + where the use of YYERROR1 above caused an error + which then was handled by compstmt_or_error. + There followed a repeated execution of that same rule, + which called YYERROR1 again, and so on. */ + compstmt + { finish_function (1); + pop_c_function_context (); } + ; + +/* Any kind of declarator (thus, all declarators allowed + after an explicit typespec). */ + +declarator: + after_type_declarator + | notype_declarator + ; + +/* A declarator that is allowed only after an explicit typespec. */ + +after_type_declarator: + '(' after_type_declarator ')' + { $$ = $2; } + | after_type_declarator '(' parmlist_or_identifiers %prec '.' + { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } +/* | after_type_declarator '(' error ')' %prec '.' + { $$ = build_nt (CALL_EXPR, $1, NULL_TREE, NULL_TREE); + poplevel (0, 0, 0); } */ + | after_type_declarator '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, $3); } + | after_type_declarator '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } + | '*' type_quals after_type_declarator %prec UNARY + { $$ = make_pointer_declarator ($2, $3); } + | attributes setattrs after_type_declarator + { $$ = $3; } + | TYPENAME + ; + +/* Kinds of declarator that can appear in a parameter list + in addition to notype_declarator. This is like after_type_declarator + but does not allow a typedef name in parentheses as an identifier + (because it would conflict with a function with that typedef as arg). */ + +parm_declarator: + parm_declarator '(' parmlist_or_identifiers %prec '.' + { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } +/* | parm_declarator '(' error ')' %prec '.' + { $$ = build_nt (CALL_EXPR, $1, NULL_TREE, NULL_TREE); + poplevel (0, 0, 0); } */ + | parm_declarator '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, $3); } + | parm_declarator '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } + | '*' type_quals parm_declarator %prec UNARY + { $$ = make_pointer_declarator ($2, $3); } + | attributes setattrs parm_declarator + { $$ = $3; } + | TYPENAME + ; + +/* A declarator allowed whether or not there has been + an explicit typespec. These cannot redeclare a typedef-name. */ + +notype_declarator: + notype_declarator '(' parmlist_or_identifiers %prec '.' + { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } +/* | notype_declarator '(' error ')' %prec '.' + { $$ = build_nt (CALL_EXPR, $1, NULL_TREE, NULL_TREE); + poplevel (0, 0, 0); } */ + | '(' notype_declarator ')' + { $$ = $2; } + | '*' type_quals notype_declarator %prec UNARY + { $$ = make_pointer_declarator ($2, $3); } + | notype_declarator '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, $3); } + | notype_declarator '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } + | attributes setattrs notype_declarator + { $$ = $3; } + | IDENTIFIER + ; + +structsp: + STRUCT identifier '{' + { $$ = start_struct (RECORD_TYPE, $2); + /* Start scope of tag before parsing components. */ + } + component_decl_list '}' maybe_attribute + { $$ = finish_struct ($<ttype>4, $5, $7); } + | STRUCT '{' component_decl_list '}' maybe_attribute + { $$ = finish_struct (start_struct (RECORD_TYPE, NULL_TREE), + $3, $5); + } + | STRUCT identifier + { $$ = xref_tag (RECORD_TYPE, $2); } + | UNION identifier '{' + { $$ = start_struct (UNION_TYPE, $2); } + component_decl_list '}' maybe_attribute + { $$ = finish_struct ($<ttype>4, $5, $7); } + | UNION '{' component_decl_list '}' maybe_attribute + { $$ = finish_struct (start_struct (UNION_TYPE, NULL_TREE), + $3, $5); + } + | UNION identifier + { $$ = xref_tag (UNION_TYPE, $2); } + | ENUM identifier '{' + { $<itype>3 = suspend_momentary (); + $$ = start_enum ($2); } + enumlist maybecomma_warn '}' maybe_attribute + { $$ = finish_enum ($<ttype>4, nreverse ($5), $8); + resume_momentary ($<itype>3); } + | ENUM '{' + { $<itype>2 = suspend_momentary (); + $$ = start_enum (NULL_TREE); } + enumlist maybecomma_warn '}' maybe_attribute + { $$ = finish_enum ($<ttype>3, nreverse ($4), $7); + resume_momentary ($<itype>2); } + | ENUM identifier + { $$ = xref_tag (ENUMERAL_TYPE, $2); } + ; + +maybecomma: + /* empty */ + | ',' + ; + +maybecomma_warn: + /* empty */ + | ',' + { if (pedantic) pedwarn ("comma at end of enumerator list"); } + ; + +component_decl_list: + component_decl_list2 + { $$ = $1; } + | component_decl_list2 component_decl + { $$ = chainon ($1, $2); + pedwarn ("no semicolon at end of struct or union"); } + ; + +component_decl_list2: /* empty */ + { $$ = NULL_TREE; } + | component_decl_list2 component_decl ';' + { $$ = chainon ($1, $2); } + | component_decl_list2 ';' + { if (pedantic) + pedwarn ("extra semicolon in struct or union specified"); } + ; + +/* There is a shift-reduce conflict here, because `components' may + start with a `typename'. It happens that shifting (the default resolution) + does the right thing, because it treats the `typename' as part of + a `typed_typespecs'. + + It is possible that this same technique would allow the distinction + between `notype_initdecls' and `initdecls' to be eliminated. + But I am being cautious and not trying it. */ + +component_decl: + typed_typespecs setspecs components + { $$ = $3; + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_typespecs + { if (pedantic) + pedwarn ("ANSI C forbids member declarations with no members"); + shadow_tag($1); + $$ = NULL_TREE; } + | nonempty_type_quals setspecs components + { $$ = $3; + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | nonempty_type_quals + { if (pedantic) + pedwarn ("ANSI C forbids member declarations with no members"); + shadow_tag($1); + $$ = NULL_TREE; } + | error + { $$ = NULL_TREE; } + ; + +components: + component_declarator + | components ',' component_declarator + { $$ = chainon ($1, $3); } + ; + +component_declarator: + save_filename save_lineno declarator maybe_attribute + { $$ = grokfield ($1, $2, $3, current_declspecs, NULL_TREE); + decl_attributes ($$, $4, prefix_attributes); } + | save_filename save_lineno + declarator ':' expr_no_commas maybe_attribute + { $$ = grokfield ($1, $2, $3, current_declspecs, $5); + decl_attributes ($$, $6, prefix_attributes); } + | save_filename save_lineno ':' expr_no_commas maybe_attribute + { $$ = grokfield ($1, $2, NULL_TREE, current_declspecs, $4); + decl_attributes ($$, $5, prefix_attributes); } + ; + +/* We chain the enumerators in reverse order. + They are put in forward order where enumlist is used. + (The order used to be significant, but no longer is so. + However, we still maintain the order, just to be clean.) */ + +enumlist: + enumerator + | enumlist ',' enumerator + { if ($1 == error_mark_node) + $$ = $1; + else + $$ = chainon ($3, $1); } + | error + { $$ = error_mark_node; } + ; + + +enumerator: + identifier + { $$ = build_enumerator ($1, NULL_TREE); } + | identifier '=' expr_no_commas + { $$ = build_enumerator ($1, $3); } + ; + +typename: + typed_typespecs absdcl + { $$ = build_tree_list ($1, $2); } + | nonempty_type_quals absdcl + { $$ = build_tree_list ($1, $2); } + ; + +absdcl: /* an absolute declarator */ + /* empty */ + { $$ = NULL_TREE; } + | absdcl1 + ; + +nonempty_type_quals: + TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $1, NULL_TREE); } + | nonempty_type_quals TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $2, $1); } + ; + +type_quals: + /* empty */ + { $$ = NULL_TREE; } + | type_quals TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $2, $1); } + ; + +absdcl1: /* a nonempty absolute declarator */ + '(' absdcl1 ')' + { $$ = $2; } + /* `(typedef)1' is `int'. */ + | '*' type_quals absdcl1 %prec UNARY + { $$ = make_pointer_declarator ($2, $3); } + | '*' type_quals %prec UNARY + { $$ = make_pointer_declarator ($2, NULL_TREE); } + | absdcl1 '(' parmlist %prec '.' + { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } + | absdcl1 '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, $3); } + | absdcl1 '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } + | '(' parmlist %prec '.' + { $$ = build_nt (CALL_EXPR, NULL_TREE, $2, NULL_TREE); } + | '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, NULL_TREE, $2); } + | '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, NULL_TREE, NULL_TREE); } + | attributes setattrs absdcl1 + { $$ = $3; } + ; + +/* at least one statement, the first of which parses without error. */ +/* stmts is used only after decls, so an invalid first statement + is actually regarded as an invalid decl and part of the decls. */ + +stmts: + lineno_stmt_or_labels + { + if (pedantic && $1) + pedwarn ("ANSI C forbids label at end of compound statement"); + } + ; + +lineno_stmt_or_labels: + lineno_stmt_or_label + | lineno_stmt_or_labels lineno_stmt_or_label + { $$ = $2; } + | lineno_stmt_or_labels errstmt + { $$ = 0; } + ; + +xstmts: + /* empty */ + | stmts + ; + +errstmt: error ';' + ; + +pushlevel: /* empty */ + { emit_line_note (input_filename, lineno); + pushlevel (0); + clear_last_expr (); + push_momentary (); + expand_start_bindings (0); + } + ; + +/* 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: '{' '}' + { $$ = convert (void_type_node, integer_zero_node); } + | '{' pushlevel maybe_label_decls decls xstmts '}' + { emit_line_note (input_filename, lineno); + expand_end_bindings (getdecls (), 1, 0); + $$ = poplevel (1, 1, 0); + if (yychar == CONSTANT || yychar == STRING) + pop_momentary_nofree (); + else + pop_momentary (); } + | '{' pushlevel maybe_label_decls error '}' + { emit_line_note (input_filename, lineno); + expand_end_bindings (getdecls (), kept_level_p (), 0); + $$ = poplevel (kept_level_p (), 0, 0); + if (yychar == CONSTANT || yychar == STRING) + pop_momentary_nofree (); + else + pop_momentary (); } + | '{' pushlevel maybe_label_decls stmts '}' + { emit_line_note (input_filename, lineno); + expand_end_bindings (getdecls (), kept_level_p (), 0); + $$ = poplevel (kept_level_p (), 0, 0); + if (yychar == CONSTANT || yychar == STRING) + pop_momentary_nofree (); + else + pop_momentary (); } + ; + +/* Value is number of statements counted as of the closeparen. */ +simple_if: + if_prefix lineno_labeled_stmt +/* Make sure expand_end_cond is run once + for each call to expand_start_cond. + Otherwise a crash is likely. */ + | if_prefix error + ; + +if_prefix: + IF '(' expr ')' + { emit_line_note ($<filename>-1, $<lineno>0); + expand_start_cond (truthvalue_conversion ($3), 0); + $<itype>$ = stmt_count; + if_stmt_file = $<filename>-1; + if_stmt_line = $<lineno>0; + position_after_white_space (); } + ; + +/* This is a subroutine of stmt. + It is used twice, once for valid DO statements + and once for catching errors in parsing the end test. */ +do_stmt_start: + DO + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + /* See comment in `while' alternative, above. */ + emit_nop (); + expand_start_loop_continue_elsewhere (1); + position_after_white_space (); } + lineno_labeled_stmt WHILE + { expand_loop_continue_here (); } + ; + +save_filename: + { $$ = input_filename; } + ; + +save_lineno: + { $$ = lineno; } + ; + +lineno_labeled_stmt: + save_filename save_lineno stmt + { } +/* | save_filename save_lineno error + { } +*/ + | save_filename save_lineno label lineno_labeled_stmt + { } + ; + +lineno_stmt_or_label: + save_filename save_lineno stmt_or_label + { $$ = $3; } + ; + +stmt_or_label: + stmt + { $$ = 0; } + | label + { $$ = 1; } + ; + +/* Parse a single real statement, not including any labels. */ +stmt: + compstmt + { stmt_count++; } + | all_iter_stmt + | expr ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); +/* It appears that this should not be done--that a non-lvalue array + shouldn't get an error if the value isn't used. + Section 3.2.2.1 says that an array lvalue gets converted to a pointer + if it appears as a top-level expression, + but says nothing about non-lvalue arrays. */ +#if 0 + /* Call default_conversion to get an error + on referring to a register array if pedantic. */ + if (TREE_CODE (TREE_TYPE ($1)) == ARRAY_TYPE + || TREE_CODE (TREE_TYPE ($1)) == FUNCTION_TYPE) + $1 = default_conversion ($1); +#endif + iterator_expand ($1); + clear_momentary (); } + | simple_if ELSE + { expand_start_else (); + $<itype>1 = stmt_count; + position_after_white_space (); } + lineno_labeled_stmt + { expand_end_cond (); + if (extra_warnings && stmt_count == $<itype>1) + warning ("empty body in an else-statement"); } + | simple_if %prec IF + { expand_end_cond (); + /* This warning is here instead of in simple_if, because we + do not want a warning if an empty if is followed by an + else statement. Increment stmt_count so we don't + give a second error if this is a nested `if'. */ + if (extra_warnings && stmt_count++ == $<itype>1) + warning_with_file_and_line (if_stmt_file, if_stmt_line, + "empty body in an if-statement"); } +/* Make sure expand_end_cond is run once + for each call to expand_start_cond. + Otherwise a crash is likely. */ + | simple_if ELSE error + { expand_end_cond (); } + | WHILE + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + /* The emit_nop used to come before emit_line_note, + but that made the nop seem like part of the preceding line. + And that was confusing when the preceding line was + inside of an if statement and was not really executed. + I think it ought to work to put the nop after the line number. + We will see. --rms, July 15, 1991. */ + emit_nop (); } + '(' expr ')' + { /* Don't start the loop till we have succeeded + in parsing the end test. This is to make sure + that we end every loop we start. */ + expand_start_loop (1); + emit_line_note (input_filename, lineno); + expand_exit_loop_if_false (NULL_PTR, + truthvalue_conversion ($4)); + position_after_white_space (); } + lineno_labeled_stmt + { expand_end_loop (); } + | do_stmt_start + '(' expr ')' ';' + { emit_line_note (input_filename, lineno); + expand_exit_loop_if_false (NULL_PTR, + truthvalue_conversion ($3)); + expand_end_loop (); + clear_momentary (); } +/* This rule is needed to make sure we end every loop we start. */ + | do_stmt_start error + { expand_end_loop (); + clear_momentary (); } + | FOR + '(' xexpr ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + /* See comment in `while' alternative, above. */ + emit_nop (); + if ($3) c_expand_expr_stmt ($3); + /* Next step is to call expand_start_loop_continue_elsewhere, + but wait till after we parse the entire for (...). + Otherwise, invalid input might cause us to call that + fn without calling expand_end_loop. */ + } + xexpr ';' + /* Can't emit now; wait till after expand_start_loop... */ + { $<lineno>7 = lineno; + $<filename>$ = input_filename; } + xexpr ')' + { + /* Start the loop. Doing this after parsing + all the expressions ensures we will end the loop. */ + expand_start_loop_continue_elsewhere (1); + /* Emit the end-test, with a line number. */ + emit_line_note ($<filename>8, $<lineno>7); + if ($6) + expand_exit_loop_if_false (NULL_PTR, + truthvalue_conversion ($6)); + /* Don't let the tree nodes for $9 be discarded by + clear_momentary during the parsing of the next stmt. */ + push_momentary (); + $<lineno>7 = lineno; + $<filename>8 = input_filename; + position_after_white_space (); } + lineno_labeled_stmt + { /* Emit the increment expression, with a line number. */ + emit_line_note ($<filename>8, $<lineno>7); + expand_loop_continue_here (); + if ($9) + c_expand_expr_stmt ($9); + if (yychar == CONSTANT || yychar == STRING) + pop_momentary_nofree (); + else + pop_momentary (); + expand_end_loop (); } + | SWITCH '(' expr ')' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + c_expand_start_case ($3); + /* Don't let the tree nodes for $3 be discarded by + clear_momentary during the parsing of the next stmt. */ + push_momentary (); + position_after_white_space (); } + lineno_labeled_stmt + { expand_end_case ($3); + if (yychar == CONSTANT || yychar == STRING) + pop_momentary_nofree (); + else + pop_momentary (); } + | BREAK ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + if ( ! expand_exit_something ()) + error ("break statement not within loop or switch"); } + | CONTINUE ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + if (! expand_continue_loop (NULL_PTR)) + error ("continue statement not within a loop"); } + | RETURN ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + c_expand_return (NULL_TREE); } + | RETURN expr ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + c_expand_return ($2); } + | ASM_KEYWORD maybe_type_qual '(' expr ')' ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + STRIP_NOPS ($4); + if ((TREE_CODE ($4) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND ($4, 0)) == STRING_CST) + || TREE_CODE ($4) == STRING_CST) + expand_asm ($4); + else + error ("argument of `asm' is not a constant string"); } + /* This is the case with just output operands. */ + | ASM_KEYWORD maybe_type_qual '(' expr ':' asm_operands ')' ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + c_expand_asm_operands ($4, $6, NULL_TREE, NULL_TREE, + $2 == ridpointers[(int)RID_VOLATILE], + input_filename, lineno); } + /* This is the case with input operands as well. */ + | ASM_KEYWORD maybe_type_qual '(' expr ':' asm_operands ':' asm_operands ')' ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + c_expand_asm_operands ($4, $6, $8, NULL_TREE, + $2 == ridpointers[(int)RID_VOLATILE], + input_filename, lineno); } + /* This is the case with clobbered registers as well. */ + | ASM_KEYWORD maybe_type_qual '(' expr ':' asm_operands ':' + asm_operands ':' asm_clobbers ')' ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + c_expand_asm_operands ($4, $6, $8, $10, + $2 == ridpointers[(int)RID_VOLATILE], + input_filename, lineno); } + | GOTO identifier ';' + { tree decl; + stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + decl = lookup_label ($2); + if (decl != 0) + { + TREE_USED (decl) = 1; + expand_goto (decl); + } + } + | GOTO '*' expr ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + expand_computed_goto (convert (ptr_type_node, $3)); } + | ';' + ; + +all_iter_stmt: + all_iter_stmt_simple +/* | all_iter_stmt_with_decl */ + ; + +all_iter_stmt_simple: + FOR '(' primary ')' + { + /* The value returned by this action is */ + /* 1 if everything is OK */ + /* 0 in case of error or already bound iterator */ + + $<itype>$ = 0; + if (TREE_CODE ($3) != VAR_DECL) + error ("invalid `for (ITERATOR)' syntax"); + else if (! ITERATOR_P ($3)) + error ("`%s' is not an iterator", + IDENTIFIER_POINTER (DECL_NAME ($3))); + else if (ITERATOR_BOUND_P ($3)) + error ("`for (%s)' inside expansion of same iterator", + IDENTIFIER_POINTER (DECL_NAME ($3))); + else + { + $<itype>$ = 1; + iterator_for_loop_start ($3); + } + } + lineno_labeled_stmt + { + if ($<itype>5) + iterator_for_loop_end ($3); + } + +/* This really should allow any kind of declaration, + for generality. Fix it before turning it back on. + +all_iter_stmt_with_decl: + FOR '(' ITERATOR pushlevel setspecs iterator_spec ')' + { +*/ /* The value returned by this action is */ + /* 1 if everything is OK */ + /* 0 in case of error or already bound iterator */ +/* + iterator_for_loop_start ($6); + } + lineno_labeled_stmt + { + iterator_for_loop_end ($6); + emit_line_note (input_filename, lineno); + expand_end_bindings (getdecls (), 1, 0); + $<ttype>$ = poplevel (1, 1, 0); + if (yychar == CONSTANT || yychar == STRING) + pop_momentary_nofree (); + else + pop_momentary (); + } +*/ + +/* Any kind of label, including jump labels and case labels. + ANSI C accepts labels only before statements, but we allow them + also at the end of a compound statement. */ + +label: CASE expr_no_commas ':' + { register tree value = check_case_value ($2); + register tree label + = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); + + stmt_count++; + + if (value != error_mark_node) + { + tree duplicate; + int success = pushcase (value, convert_and_check, + label, &duplicate); + if (success == 1) + error ("case label not within a switch statement"); + else if (success == 2) + { + error ("duplicate case value"); + error_with_decl (duplicate, "this is the first entry for that value"); + } + else if (success == 3) + warning ("case value out of range"); + else if (success == 5) + error ("case label within scope of cleanup or variable array"); + } + position_after_white_space (); } + | CASE expr_no_commas ELLIPSIS expr_no_commas ':' + { register tree value1 = check_case_value ($2); + register tree value2 = check_case_value ($4); + register tree label + = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); + + stmt_count++; + + 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 case value"); + error_with_decl (duplicate, "this is the first entry for that value"); + } + else if (success == 3) + warning ("case value out of range"); + else if (success == 4) + warning ("empty case range"); + else if (success == 5) + error ("case label within scope of cleanup or variable array"); + } + position_after_white_space (); } + | DEFAULT ':' + { + tree duplicate; + register tree label + = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); + int success = pushcase (NULL_TREE, 0, label, &duplicate); + stmt_count++; + 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"); + } + position_after_white_space (); } + | identifier ':' + { tree label = define_label (input_filename, lineno, $1); + stmt_count++; + emit_nop (); + if (label) + expand_label (label); + position_after_white_space (); } + ; + +/* 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 + ; + +/* 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 ($1, $3); } + ; + +asm_operand: + STRING '(' expr ')' + { $$ = build_tree_list ($1, $3); } + ; + +asm_clobbers: + string + { $$ = tree_cons (NULL_TREE, combine_strings ($1), NULL_TREE); } + | asm_clobbers ',' string + { $$ = tree_cons (NULL_TREE, combine_strings ($3), $1); } + ; + +/* This is what appears inside the parens in a function declarator. + Its value is a list of ..._TYPE nodes. */ +parmlist: + { pushlevel (0); + clear_parm_order (); + declare_parm_level (0); } + parmlist_1 + { $$ = $2; + parmlist_tags_warning (); + poplevel (0, 0, 0); } + ; + +parmlist_1: + parmlist_2 ')' + | parms ';' + { tree parm; + if (pedantic) + pedwarn ("ANSI C forbids forward parameter declarations"); + /* Mark the forward decls as such. */ + for (parm = getdecls (); parm; parm = TREE_CHAIN (parm)) + TREE_ASM_WRITTEN (parm) = 1; + clear_parm_order (); } + parmlist_1 + { $$ = $4; } + | error ')' + { $$ = tree_cons (NULL_TREE, NULL_TREE, NULL_TREE); } + ; + +/* This is what appears inside the parens in a function declarator. + Is value is represented in the format that grokdeclarator expects. */ +parmlist_2: /* empty */ + { $$ = get_parm_info (0); } + | ELLIPSIS + { $$ = get_parm_info (0); + /* Gcc used to allow this as an extension. However, it does + not work for all targets, and thus has been disabled. + Also, since func (...) and func () are indistinguishable, + it caused problems with the code in expand_builtin which + tries to verify that BUILT_IN_NEXT_ARG is being used + correctly. */ + error ("ANSI C requires a named argument before `...'"); + } + | parms + { $$ = get_parm_info (1); } + | parms ',' ELLIPSIS + { $$ = get_parm_info (0); } + ; + +parms: + parm + { push_parm_decl ($1); } + | parms ',' parm + { push_parm_decl ($3); } + ; + +/* A single parameter declaration or parameter type name, + as found in a parmlist. */ +parm: + typed_declspecs setspecs parm_declarator maybe_attribute + { $$ = build_tree_list (build_tree_list (current_declspecs, + $3), + build_tree_list (prefix_attributes, + $4)); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs setspecs notype_declarator maybe_attribute + { $$ = build_tree_list (build_tree_list (current_declspecs, + $3), + build_tree_list (prefix_attributes, + $4)); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs setspecs absdcl maybe_attribute + { $$ = build_tree_list (build_tree_list (current_declspecs, + $3), + build_tree_list (prefix_attributes, + $4)); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_declarator maybe_attribute + { $$ = build_tree_list (build_tree_list (current_declspecs, + $3), + build_tree_list (prefix_attributes, + $4)); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + + | declmods setspecs absdcl maybe_attribute + { $$ = build_tree_list (build_tree_list (current_declspecs, + $3), + build_tree_list (prefix_attributes, + $4)); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + ; + +/* This is used in a function definition + where either a parmlist or an identifier list is ok. + Its value is a list of ..._TYPE nodes or a list of identifiers. */ +parmlist_or_identifiers: + { pushlevel (0); + clear_parm_order (); + declare_parm_level (1); } + parmlist_or_identifiers_1 + { $$ = $2; + parmlist_tags_warning (); + poplevel (0, 0, 0); } + ; + +parmlist_or_identifiers_1: + parmlist_1 + | identifiers ')' + { tree t; + for (t = $1; t; t = TREE_CHAIN (t)) + if (TREE_VALUE (t) == NULL_TREE) + error ("`...' in old-style identifier list"); + $$ = tree_cons (NULL_TREE, NULL_TREE, $1); } + ; + +/* A nonempty list of identifiers. */ +identifiers: + IDENTIFIER + { $$ = build_tree_list (NULL_TREE, $1); } + | identifiers ',' IDENTIFIER + { $$ = chainon ($1, build_tree_list (NULL_TREE, $3)); } + ; + +/* 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)); } + ; + +%% diff --git a/gnu/usr.bin/gcc/cc1obj/objc-parse.y b/gnu/usr.bin/gcc/cc1obj/objc-parse.y new file mode 100644 index 00000000000..81b2e85b538 --- /dev/null +++ b/gnu/usr.bin/gcc/cc1obj/objc-parse.y @@ -0,0 +1,2820 @@ +/* YACC parser for C syntax and for Objective C. -*-c-*- + Copyright (C) 1987, 88, 89, 92, 93, 94, 1995 Free Software Foundation, Inc. + +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 file defines the grammar of C and that of Objective C. + ifobjc ... end ifobjc conditionals contain code for Objective C only. + ifc ... end ifc conditionals contain code for C only. + Sed commands in Makefile.in are used to convert this file into + c-parse.y and into objc-parse.y. */ + +/* To whomever it may concern: I have heard that such a thing was once + written by AT&T, but I have never seen it. */ + +%expect 48 + +%{ +#include <stdio.h> +#include <errno.h> +#include <setjmp.h> + +#include "config.h" +#include "tree.h" +#include "input.h" +#include "c-lex.h" +#include "c-tree.h" +#include "flags.h" + +#ifdef MULTIBYTE_CHARS +#include <stdlib.h> +#include <locale.h> +#endif + +#include "objc-act.h" + +/* Since parsers are distinct for each language, put the language string + definition here. */ +char *language_string = "GNU Obj-C"; + +#ifndef errno +extern int errno; +#endif + +void yyerror (); + +/* Like YYERROR but do call yyerror. */ +#define YYERROR1 { yyerror ("syntax error"); YYERROR; } + +/* Cause the `yydebug' variable to be defined. */ +#define YYDEBUG 1 +%} + +%start program + +%union {long itype; tree ttype; enum tree_code code; + char *filename; int lineno; int ends_in_label; } + +/* 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 TYPEOF ALIGNOF +%token ATTRIBUTE EXTENSION LABEL +%token REALPART IMAGPART + +/* Add precedence rules to solve dangling else s/r conflict */ +%nonassoc IF +%nonassoc ELSE + +/* 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. */ + +%right <code> ASSIGN '=' +%right <code> '?' ':' +%left <code> OROR +%left <code> ANDAND +%left <code> '|' +%left <code> '^' +%left <code> '&' +%left <code> EQCOMPARE +%left <code> ARITHCOMPARE +%left <code> LSHIFT RSHIFT +%left <code> '+' '-' +%left <code> '*' '/' '%' +%right <code> UNARY PLUSPLUS MINUSMINUS +%left HYPERUNARY +%left <code> POINTSAT '.' '(' '[' + +/* The Objective-C keywords. These are included in C and in + Objective C, so that the token codes are the same in both. */ +%token INTERFACE IMPLEMENTATION END SELECTOR DEFS ENCODE +%token CLASSNAME PUBLIC PRIVATE PROTECTED PROTOCOL OBJECTNAME CLASS ALIAS + +/* Objective-C string constants in raw form. + yylval is an OBJC_STRING_CST node. */ +%token OBJC_STRING + + +%type <code> unop + +%type <ttype> identifier IDENTIFIER TYPENAME CONSTANT expr nonnull_exprlist exprlist +%type <ttype> expr_no_commas cast_expr unary_expr primary string STRING +%type <ttype> typed_declspecs reserved_declspecs +%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 <ttype> initdecls notype_initdecls initdcl notype_initdcl +%type <ttype> init maybeasm +%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 + +%type <ttype> declarator +%type <ttype> notype_declarator after_type_declarator +%type <ttype> parm_declarator + +%type <ttype> structsp component_decl_list component_decl_list2 +%type <ttype> component_decl components component_declarator +%type <ttype> enumlist enumerator +%type <ttype> typename absdcl absdcl1 type_quals +%type <ttype> xexpr parms parm identifiers + +%type <ttype> parmlist parmlist_1 parmlist_2 +%type <ttype> parmlist_or_identifiers parmlist_or_identifiers_1 +%type <ttype> identifiers_or_typenames + +%type <itype> setspecs + +%type <ends_in_label> lineno_stmt_or_label lineno_stmt_or_labels stmt_or_label + +%type <filename> save_filename +%type <lineno> save_lineno + +/* the Objective-C nonterminals */ + +%type <ttype> ivar_decl_list ivar_decls ivar_decl ivars ivar_declarator +%type <ttype> methoddecl unaryselector keywordselector selector +%type <ttype> keyworddecl receiver objcmessageexpr messageargs +%type <ttype> keywordexpr keywordarglist keywordarg +%type <ttype> myparms myparm optparmlist reservedwords objcselectorexpr +%type <ttype> selectorarg keywordnamelist keywordname objcencodeexpr +%type <ttype> objc_string protocolrefs identifier_list objcprotocolexpr +%type <ttype> CLASSNAME OBJC_STRING OBJECTNAME + +%{ +/* Number of statements (loosely speaking) seen so far. */ +static int stmt_count; + +/* Input file and line number of the end of the body of last simple_if; + used by the stmt-rule immediately after simple_if returns. */ +static char *if_stmt_file; +static int if_stmt_line; + +/* List of types and structure classes of the current declaration. */ +static tree current_declspecs; +static tree prefix_attributes = NULL_TREE; + +/* Stack of saved values of current_declspecs and prefix_attributes. */ +static tree declspec_stack; + +/* 1 if we explained undeclared var errors. */ +static int undeclared_variable_notice; + +/* Objective-C specific information */ + +tree objc_interface_context; +tree objc_implementation_context; +tree objc_method_context; +tree objc_ivar_chain; +tree objc_ivar_context; +enum tree_code objc_inherit_code; +int objc_receiver_context; +int objc_public_flag; + + +/* 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 (); +%} + +%% +program: /* empty */ + { if (pedantic) + pedwarn ("ANSI C forbids an empty source file"); + finish_file (); + } + | extdefs + { + /* In case there were missing closebraces, + get us back to the global binding level. */ + while (! global_bindings_p ()) + poplevel (0, 0, 0); + 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; } extdef + | extdefs {$<ttype>$ = NULL_TREE; } extdef + ; + +extdef: + fndef + | datadef + | objcdef + | ASM_KEYWORD '(' expr ')' ';' + { STRIP_NOPS ($3); + if ((TREE_CODE ($3) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND ($3, 0)) == STRING_CST) + || TREE_CODE ($3) == STRING_CST) + assemble_asm ($3); + else + error ("argument of `asm' is not a constant string"); } + ; + +datadef: + setspecs notype_initdecls ';' + { if (pedantic) + error ("ANSI C forbids data definition with no type or storage class"); + else if (!flag_traditional) + warning ("data definition has no type or storage class"); + + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($1); } + | declmods setspecs notype_initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs setspecs initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods ';' + { pedwarn ("empty declaration"); } + | typed_declspecs ';' + { shadow_tag ($1); } + | error ';' + | error '}' + | ';' + { if (pedantic) + pedwarn ("ANSI C does not allow extra `;' outside of a function"); } + ; + +fndef: + typed_declspecs setspecs declarator + { if (! start_function ($1, $3, prefix_attributes, + NULL_TREE, 0)) + YYERROR1; + reinit_parse_for_function (); } + xdecls + { store_parm_decls (); } + compstmt_or_error + { finish_function (0); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs setspecs declarator error + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_declarator + { if (! start_function ($1, $3, prefix_attributes, + NULL_TREE, 0)) + YYERROR1; + reinit_parse_for_function (); } + xdecls + { store_parm_decls (); } + compstmt_or_error + { finish_function (0); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_declarator error + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | setspecs notype_declarator + { if (! start_function (NULL_TREE, $2, + prefix_attributes, NULL_TREE, 0)) + YYERROR1; + reinit_parse_for_function (); } + xdecls + { store_parm_decls (); } + compstmt_or_error + { finish_function (0); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($1); } + | setspecs notype_declarator error + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($1); } + ; + +identifier: + IDENTIFIER + | TYPENAME + | OBJECTNAME + | CLASSNAME + ; + +unop: '&' + { $$ = ADDR_EXPR; } + | '-' + { $$ = NEGATE_EXPR; } + | '+' + { $$ = CONVERT_EXPR; } + | PLUSPLUS + { $$ = PREINCREMENT_EXPR; } + | MINUSMINUS + { $$ = PREDECREMENT_EXPR; } + | '~' + { $$ = BIT_NOT_EXPR; } + | '!' + { $$ = TRUTH_NOT_EXPR; } + ; + +expr: nonnull_exprlist + { $$ = build_compound_expr ($1); } + ; + +exprlist: + /* empty */ + { $$ = NULL_TREE; } + | nonnull_exprlist + ; + +nonnull_exprlist: + expr_no_commas + { $$ = build_tree_list (NULL_TREE, $1); } + | nonnull_exprlist ',' expr_no_commas + { chainon ($1, build_tree_list (NULL_TREE, $3)); } + ; + +unary_expr: + primary + | '*' cast_expr %prec UNARY + { $$ = build_indirect_ref ($2, "unary *"); } + /* __extension__ turns off -pedantic for following primary. */ + | EXTENSION + { $<itype>1 = pedantic; + pedantic = 0; } + cast_expr %prec UNARY + { $$ = $3; + pedantic = $<itype>1; } + | unop cast_expr %prec UNARY + { $$ = build_unary_op ($1, $2, 0); + overflow_warning ($$); } + /* Refer to the address of a label as a pointer. */ + | ANDAND identifier + { tree label = lookup_label ($2); + if (pedantic) + pedwarn ("ANSI C forbids `&&'"); + if (label == 0) + $$ = null_pointer_node; + else + { + TREE_USED (label) = 1; + $$ = build1 (ADDR_EXPR, ptr_type_node, label); + TREE_CONSTANT ($$) = 1; + } + } +/* This seems to be impossible on some machines, so let's turn it off. + You can use __builtin_next_arg to find the anonymous stack args. + | '&' ELLIPSIS + { tree types = TYPE_ARG_TYPES (TREE_TYPE (current_function_decl)); + $$ = error_mark_node; + if (TREE_VALUE (tree_last (types)) == void_type_node) + error ("`&...' used in function with fixed number of arguments"); + else + { + if (pedantic) + pedwarn ("ANSI C forbids `&...'"); + $$ = tree_last (DECL_ARGUMENTS (current_function_decl)); + $$ = build_unary_op (ADDR_EXPR, $$, 0); + } } +*/ + | 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"); + $$ = c_sizeof (TREE_TYPE ($2)); } + | SIZEOF '(' typename ')' %prec HYPERUNARY + { $$ = c_sizeof (groktypename ($3)); } + | ALIGNOF unary_expr %prec UNARY + { $$ = c_alignof_expr ($2); } + | ALIGNOF '(' typename ')' %prec HYPERUNARY + { $$ = c_alignof (groktypename ($3)); } + | REALPART cast_expr %prec UNARY + { $$ = build_unary_op (REALPART_EXPR, $2, 0); } + | IMAGPART cast_expr %prec UNARY + { $$ = build_unary_op (IMAGPART_EXPR, $2, 0); } + ; + +cast_expr: + unary_expr + | '(' typename ')' cast_expr %prec UNARY + { tree type = groktypename ($2); + $$ = build_c_cast (type, $4); } + | '(' typename ')' '{' + { start_init (NULL_TREE, NULL, 0); + $2 = groktypename ($2); + really_start_incremental_init ($2); } + initlist_maybe_comma '}' %prec UNARY + { char *name; + tree result = pop_init_level (0); + tree type = $2; + finish_init (); + + if (pedantic) + pedwarn ("ANSI C forbids constructor expressions"); + if (TYPE_NAME (type) != 0) + { + if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) + name = IDENTIFIER_POINTER (TYPE_NAME (type)); + else + name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); + } + else + name = ""; + $$ = result; + if (TREE_CODE (type) == ARRAY_TYPE && TYPE_SIZE (type) == 0) + { + int failure = complete_array_type (type, $$, 1); + if (failure) + abort (); + } + } + ; + +expr_no_commas: + cast_expr + | expr_no_commas '+' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '-' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '*' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '/' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '%' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas LSHIFT expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas RSHIFT expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas ARITHCOMPARE expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas EQCOMPARE expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '&' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '|' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '^' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas ANDAND expr_no_commas + { $$ = parser_build_binary_op (TRUTH_ANDIF_EXPR, $1, $3); } + | expr_no_commas OROR expr_no_commas + { $$ = parser_build_binary_op (TRUTH_ORIF_EXPR, $1, $3); } + | expr_no_commas '?' xexpr ':' expr_no_commas + { $$ = build_conditional_expr ($1, $3, $5); } + | expr_no_commas '=' expr_no_commas + { $$ = build_modify_expr ($1, NOP_EXPR, $3); + C_SET_EXP_ORIGINAL_CODE ($$, MODIFY_EXPR); } + | expr_no_commas ASSIGN expr_no_commas + { $$ = build_modify_expr ($1, $2, $3); + /* This inhibits warnings in truthvalue_conversion. */ + C_SET_EXP_ORIGINAL_CODE ($$, ERROR_MARK); } + ; + +primary: + IDENTIFIER + { + $$ = lastiddecl; + if (!$$ || $$ == error_mark_node) + { + if (yychar == YYEMPTY) + yychar = YYLEX; + if (yychar == '(') + { + tree decl; + + if (objc_receiver_context + && ! (objc_receiver_context + && strcmp (IDENTIFIER_POINTER ($1), "super"))) + /* we have a message to super */ + $$ = get_super_receiver (); + else if (objc_method_context + && (decl = is_ivar (objc_ivar_chain, $1))) + { + if (is_private (decl)) + $$ = error_mark_node; + else + $$ = build_ivar_reference ($1); + } + else + { + /* Ordinary implicit function declaration. */ + $$ = implicitly_declare ($1); + assemble_external ($$); + TREE_USED ($$) = 1; + } + } + else if (current_function_decl == 0) + { + error ("`%s' undeclared here (not in a function)", + IDENTIFIER_POINTER ($1)); + $$ = error_mark_node; + } + else + { + tree decl; + + if (objc_receiver_context + && ! strcmp (IDENTIFIER_POINTER ($1), "super")) + /* we have a message to super */ + $$ = get_super_receiver (); + else if (objc_method_context + && (decl = is_ivar (objc_ivar_chain, $1))) + { + if (is_private (decl)) + $$ = error_mark_node; + else + $$ = build_ivar_reference ($1); + } + else + { + if (IDENTIFIER_GLOBAL_VALUE ($1) != error_mark_node + || IDENTIFIER_ERROR_LOCUS ($1) != current_function_decl) + { + error ("`%s' undeclared (first use this function)", + IDENTIFIER_POINTER ($1)); + + if (! undeclared_variable_notice) + { + error ("(Each undeclared identifier is reported only once"); + error ("for each function it appears in.)"); + undeclared_variable_notice = 1; + } + } + $$ = error_mark_node; + /* Prevent repeated error messages. */ + IDENTIFIER_GLOBAL_VALUE ($1) = error_mark_node; + IDENTIFIER_ERROR_LOCUS ($1) = current_function_decl; + } + } + } + else if (TREE_TYPE ($$) == error_mark_node) + $$ = error_mark_node; + else if (C_DECL_ANTICIPATED ($$)) + { + /* The first time we see a build-in function used, + if it has not been declared. */ + C_DECL_ANTICIPATED ($$) = 0; + if (yychar == YYEMPTY) + yychar = YYLEX; + if (yychar == '(') + { + /* Omit the implicit declaration we + would ordinarily do, so we don't lose + the actual built in type. + But print a diagnostic for the mismatch. */ + if (objc_method_context + && is_ivar (objc_ivar_chain, $1)) + error ("Instance variable `%s' implicitly declared as function", + IDENTIFIER_POINTER (DECL_NAME ($$))); + else + if (TREE_CODE ($$) != FUNCTION_DECL) + error ("`%s' implicitly declared as function", + IDENTIFIER_POINTER (DECL_NAME ($$))); + else if ((TYPE_MODE (TREE_TYPE (TREE_TYPE ($$))) + != TYPE_MODE (integer_type_node)) + && (TREE_TYPE (TREE_TYPE ($$)) + != void_type_node)) + pedwarn ("type mismatch in implicit declaration for built-in function `%s'", + IDENTIFIER_POINTER (DECL_NAME ($$))); + /* If it really returns void, change that to int. */ + if (TREE_TYPE (TREE_TYPE ($$)) == void_type_node) + TREE_TYPE ($$) + = build_function_type (integer_type_node, + TYPE_ARG_TYPES (TREE_TYPE ($$))); + } + else + pedwarn ("built-in function `%s' used without declaration", + IDENTIFIER_POINTER (DECL_NAME ($$))); + + /* Do what we would ordinarily do when a fn is used. */ + assemble_external ($$); + TREE_USED ($$) = 1; + } + else + { + assemble_external ($$); + TREE_USED ($$) = 1; + /* we have a definition - still check if iVariable */ + + if (!objc_receiver_context + || (objc_receiver_context + && strcmp (IDENTIFIER_POINTER ($1), "super"))) + { + tree decl; + + if (objc_method_context + && (decl = is_ivar (objc_ivar_chain, $1))) + { + if (IDENTIFIER_LOCAL_VALUE ($1)) + warning ("local declaration of `%s' hides instance variable", + IDENTIFIER_POINTER ($1)); + else + { + if (is_private (decl)) + $$ = error_mark_node; + else + $$ = build_ivar_reference ($1); + } + } + } + else /* we have a message to super */ + $$ = get_super_receiver (); + } + + if (TREE_CODE ($$) == CONST_DECL) + { + $$ = 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; + } + } + | CONSTANT + | string + { $$ = combine_strings ($1); } + | '(' expr ')' + { char class = TREE_CODE_CLASS (TREE_CODE ($2)); + if (class == 'e' || class == '1' + || class == '2' || class == '<') + C_SET_EXP_ORIGINAL_CODE ($2, ERROR_MARK); + $$ = $2; } + | '(' error ')' + { $$ = error_mark_node; } + | '(' + { if (current_function_decl == 0) + { + error ("braced-group within expression allowed only inside a function"); + YYERROR; + } + /* We must force a BLOCK for this level + so that, if it is not expanded later, + there is a way to turn off the entire subtree of blocks + that are contained in it. */ + keep_next_level (); + push_iterator_stack (); + push_label_level (); + $<ttype>$ = expand_start_stmt_expr (); } + compstmt ')' + { tree rtl_exp; + if (pedantic) + pedwarn ("ANSI C forbids braced-groups within expressions"); + pop_iterator_stack (); + pop_label_level (); + 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 '(' exprlist ')' %prec '.' + { $$ = build_function_call ($1, $3); } + | primary '[' expr ']' %prec '.' + { $$ = build_array_ref ($1, $3); } + | primary '.' identifier + { + if (doing_objc_thang) + { + if (is_public ($1, $3)) + $$ = build_component_ref ($1, $3); + else + $$ = error_mark_node; + } + else + $$ = build_component_ref ($1, $3); + } + | primary POINTSAT identifier + { + tree expr = build_indirect_ref ($1, "->"); + + if (doing_objc_thang) + { + if (is_public (expr, $3)) + $$ = build_component_ref (expr, $3); + else + $$ = error_mark_node; + } + else + $$ = build_component_ref (expr, $3); + } + | primary PLUSPLUS + { $$ = build_unary_op (POSTINCREMENT_EXPR, $1, 0); } + | primary MINUSMINUS + { $$ = build_unary_op (POSTDECREMENT_EXPR, $1, 0); } + | objcmessageexpr + { $$ = build_message_expr ($1); } + | objcselectorexpr + { $$ = build_selector_expr ($1); } + | objcprotocolexpr + { $$ = build_protocol_expr ($1); } + | objcencodeexpr + { $$ = build_encode_expr ($1); } + | objc_string + { $$ = build_objc_string_object ($1); } + ; + +/* Produces a STRING_CST with perhaps more STRING_CSTs chained onto it. */ +string: + STRING + | string STRING + { $$ = chainon ($1, $2); } + ; + +/* 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); } + ; + +xdecls: + /* empty */ + | datadecls + | datadecls ELLIPSIS + /* ... is used here to indicate a varargs function. */ + { c_mark_varargs (); + if (pedantic) + pedwarn ("ANSI C does not permit use of `varargs.h'"); } + ; + +/* The following are analogous to lineno_decl, decls and decl + except that they do not allow nested functions. + They are used for old-style parm decls. */ +lineno_datadecl: + save_filename save_lineno datadecl + { } + ; + +datadecls: + lineno_datadecl + | errstmt + | datadecls lineno_datadecl + | lineno_datadecl errstmt + ; + +datadecl: + typed_declspecs setspecs initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs ';' + { shadow_tag_warned ($1, 1); + pedwarn ("empty declaration"); } + | declmods ';' + { pedwarn ("empty declaration"); } + ; + +/* This combination which saves a lineno before a decl + is the normal thing to use, rather than decl itself. + This is to avoid shift/reduce conflicts in contexts + where statement labels are allowed. */ +lineno_decl: + save_filename save_lineno decl + { } + ; + +decls: + lineno_decl + | errstmt + | decls lineno_decl + | lineno_decl errstmt + ; + +/* records the type and storage class specs to use for processing + the declarators that follow. + Maintains a stack of outer-level values of current_declspecs, + for the sake of parm declarations nested in function declarators. */ +setspecs: /* empty */ + { $$ = suspend_momentary (); + pending_xref_error (); + declspec_stack = tree_cons (prefix_attributes, + current_declspecs, + declspec_stack); + current_declspecs = $<ttype>0; + prefix_attributes = NULL_TREE; } + ; + +setattrs: /* empty */ + { prefix_attributes = chainon (prefix_attributes, $<ttype>0); } + ; + +decl: + typed_declspecs setspecs initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs setspecs nested_function + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_nested_function + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs ';' + { shadow_tag ($1); } + | declmods ';' + { pedwarn ("empty declaration"); } + ; + +/* 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. */ + +typed_declspecs: + typespec reserved_declspecs + { $$ = tree_cons (NULL_TREE, $1, $2); } + | declmods typespec reserved_declspecs + { $$ = chainon ($3, tree_cons (NULL_TREE, $2, $1)); } + ; + +reserved_declspecs: /* empty */ + { $$ = NULL_TREE; } + | reserved_declspecs typespecqual_reserved + { $$ = tree_cons (NULL_TREE, $2, $1); } + | reserved_declspecs SCSPEC + { if (extra_warnings) + warning ("`%s' is not at beginning of declaration", + IDENTIFIER_POINTER ($2)); + $$ = tree_cons (NULL_TREE, $2, $1); } + ; + +/* 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. */ + +declmods: + TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $1, NULL_TREE); + TREE_STATIC ($$) = 1; } + | SCSPEC + { $$ = tree_cons (NULL_TREE, $1, NULL_TREE); } + | declmods TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $2, $1); + TREE_STATIC ($$) = 1; } + | declmods SCSPEC + { if (extra_warnings && TREE_STATIC ($1)) + warning ("`%s' is not at beginning of declaration", + IDENTIFIER_POINTER ($2)); + $$ = tree_cons (NULL_TREE, $2, $1); + TREE_STATIC ($$) = TREE_STATIC ($1); } + ; + + +/* Used instead of declspecs where storage classes are not allowed + (that is, for typenames and structure components). + Don't accept a typedef-name if anything but a modifier precedes it. */ + +typed_typespecs: + typespec reserved_typespecquals + { $$ = tree_cons (NULL_TREE, $1, $2); } + | nonempty_type_quals typespec reserved_typespecquals + { $$ = chainon ($3, tree_cons (NULL_TREE, $2, $1)); } + ; + +reserved_typespecquals: /* empty */ + { $$ = NULL_TREE; } + | reserved_typespecquals typespecqual_reserved + { $$ = tree_cons (NULL_TREE, $2, $1); } + ; + +/* 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: TYPESPEC + | structsp + | TYPENAME + { /* For a typedef name, record the meaning, not the name. + In case of `foo foo, bar;'. */ + $$ = lookup_name ($1); } + | CLASSNAME protocolrefs + { $$ = get_static_reference ($1, $2); } + | OBJECTNAME protocolrefs + { $$ = get_object_reference ($2); } + | TYPEOF '(' expr ')' + { $$ = TREE_TYPE ($3); } + | TYPEOF '(' typename ')' + { $$ = groktypename ($3); } + ; + +/* A typespec that is a reserved word, or a type qualifier. */ + +typespecqual_reserved: TYPESPEC + | TYPE_QUAL + | structsp + ; + +initdecls: + initdcl + | initdecls ',' initdcl + ; + +notype_initdecls: + notype_initdcl + | notype_initdecls ',' initdcl + ; + +maybeasm: + /* empty */ + { $$ = NULL_TREE; } + | ASM_KEYWORD '(' string ')' + { if (TREE_CHAIN ($3)) $3 = combine_strings ($3); + $$ = $3; + } + ; + +initdcl: + declarator maybeasm maybe_attribute '=' + { $<ttype>$ = start_decl ($1, current_declspecs, 1, + $3, prefix_attributes); + start_init ($<ttype>$, $2, global_bindings_p ()); } + init +/* Note how the declaration of the variable is in effect while its init is parsed! */ + { finish_init (); + finish_decl ($<ttype>5, $6, $2); } + | declarator maybeasm maybe_attribute + { tree d = start_decl ($1, current_declspecs, 0, + $3, prefix_attributes); + finish_decl (d, NULL_TREE, $2); + } + ; + +notype_initdcl: + notype_declarator maybeasm maybe_attribute '=' + { $<ttype>$ = start_decl ($1, current_declspecs, 1, + $3, prefix_attributes); + start_init ($<ttype>$, $2, global_bindings_p ()); } + init +/* Note how the declaration of the variable is in effect while its init is parsed! */ + { finish_init (); + decl_attributes ($<ttype>5, $3, prefix_attributes); + finish_decl ($<ttype>5, $6, $2); } + | notype_declarator maybeasm maybe_attribute + { tree d = start_decl ($1, current_declspecs, 0, + $3, prefix_attributes); + finish_decl (d, NULL_TREE, $2); } + ; +/* 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 '(' 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 + ; + +/* Initializers. `init' is the entry point. */ + +init: + expr_no_commas + | '{' + { really_start_incremental_init (NULL_TREE); + /* Note that the call to clear_momentary + is in process_init_element. */ + push_momentary (); } + initlist_maybe_comma '}' + { $$ = pop_init_level (0); + if ($$ == error_mark_node + && ! (yychar == STRING || yychar == CONSTANT)) + pop_momentary (); + else + pop_momentary_nofree (); } + + | error + { $$ = error_mark_node; } + ; + +/* `initlist_maybe_comma' is the guts of an initializer in braces. */ +initlist_maybe_comma: + /* empty */ + { if (pedantic) + pedwarn ("ANSI C forbids empty initializer braces"); } + | initlist1 maybecomma + ; + +initlist1: + initelt + | initlist1 ',' initelt + ; + +/* `initelt' is a single element of an initializer. + It may use braces. */ +initelt: + expr_no_commas + { process_init_element ($1); } + | '{' + { push_init_level (0); } + initlist_maybe_comma '}' + { process_init_element (pop_init_level (0)); } + | error + /* 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. */ + | identifier ':' + { set_init_label ($1); } + initelt + | '.' identifier '=' + { set_init_label ($2); } + initelt + ; + +nested_function: + declarator + { push_c_function_context (); + if (! start_function (current_declspecs, $1, + prefix_attributes, NULL_TREE, 1)) + { + pop_c_function_context (); + YYERROR1; + } + reinit_parse_for_function (); } + xdecls + { store_parm_decls (); } +/* This used to use compstmt_or_error. + That caused a bug with input `f(g) int g {}', + where the use of YYERROR1 above caused an error + which then was handled by compstmt_or_error. + There followed a repeated execution of that same rule, + which called YYERROR1 again, and so on. */ + compstmt + { finish_function (1); + pop_c_function_context (); } + ; + +notype_nested_function: + notype_declarator + { push_c_function_context (); + if (! start_function (current_declspecs, $1, + prefix_attributes, NULL_TREE, 1)) + { + pop_c_function_context (); + YYERROR1; + } + reinit_parse_for_function (); } + xdecls + { store_parm_decls (); } +/* This used to use compstmt_or_error. + That caused a bug with input `f(g) int g {}', + where the use of YYERROR1 above caused an error + which then was handled by compstmt_or_error. + There followed a repeated execution of that same rule, + which called YYERROR1 again, and so on. */ + compstmt + { finish_function (1); + pop_c_function_context (); } + ; + +/* Any kind of declarator (thus, all declarators allowed + after an explicit typespec). */ + +declarator: + after_type_declarator + | notype_declarator + ; + +/* A declarator that is allowed only after an explicit typespec. */ + +after_type_declarator: + '(' after_type_declarator ')' + { $$ = $2; } + | after_type_declarator '(' parmlist_or_identifiers %prec '.' + { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } +/* | after_type_declarator '(' error ')' %prec '.' + { $$ = build_nt (CALL_EXPR, $1, NULL_TREE, NULL_TREE); + poplevel (0, 0, 0); } */ + | after_type_declarator '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, $3); } + | after_type_declarator '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } + | '*' type_quals after_type_declarator %prec UNARY + { $$ = make_pointer_declarator ($2, $3); } + | attributes setattrs after_type_declarator + { $$ = $3; } + | TYPENAME + | OBJECTNAME + ; + +/* Kinds of declarator that can appear in a parameter list + in addition to notype_declarator. This is like after_type_declarator + but does not allow a typedef name in parentheses as an identifier + (because it would conflict with a function with that typedef as arg). */ + +parm_declarator: + parm_declarator '(' parmlist_or_identifiers %prec '.' + { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } +/* | parm_declarator '(' error ')' %prec '.' + { $$ = build_nt (CALL_EXPR, $1, NULL_TREE, NULL_TREE); + poplevel (0, 0, 0); } */ + | parm_declarator '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, $3); } + | parm_declarator '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } + | '*' type_quals parm_declarator %prec UNARY + { $$ = make_pointer_declarator ($2, $3); } + | attributes setattrs parm_declarator + { $$ = $3; } + | TYPENAME + ; + +/* A declarator allowed whether or not there has been + an explicit typespec. These cannot redeclare a typedef-name. */ + +notype_declarator: + notype_declarator '(' parmlist_or_identifiers %prec '.' + { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } +/* | notype_declarator '(' error ')' %prec '.' + { $$ = build_nt (CALL_EXPR, $1, NULL_TREE, NULL_TREE); + poplevel (0, 0, 0); } */ + | '(' notype_declarator ')' + { $$ = $2; } + | '*' type_quals notype_declarator %prec UNARY + { $$ = make_pointer_declarator ($2, $3); } + | notype_declarator '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, $3); } + | notype_declarator '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } + | attributes setattrs notype_declarator + { $$ = $3; } + | IDENTIFIER + ; + +structsp: + STRUCT identifier '{' + { $$ = start_struct (RECORD_TYPE, $2); + /* Start scope of tag before parsing components. */ + } + component_decl_list '}' maybe_attribute + { $$ = finish_struct ($<ttype>4, $5, $7); } + | STRUCT '{' component_decl_list '}' maybe_attribute + { $$ = finish_struct (start_struct (RECORD_TYPE, NULL_TREE), + $3, $5); + } + | STRUCT identifier + { $$ = xref_tag (RECORD_TYPE, $2); } + | UNION identifier '{' + { $$ = start_struct (UNION_TYPE, $2); } + component_decl_list '}' maybe_attribute + { $$ = finish_struct ($<ttype>4, $5, $7); } + | UNION '{' component_decl_list '}' maybe_attribute + { $$ = finish_struct (start_struct (UNION_TYPE, NULL_TREE), + $3, $5); + } + | UNION identifier + { $$ = xref_tag (UNION_TYPE, $2); } + | ENUM identifier '{' + { $<itype>3 = suspend_momentary (); + $$ = start_enum ($2); } + enumlist maybecomma_warn '}' maybe_attribute + { $$ = finish_enum ($<ttype>4, nreverse ($5), $8); + resume_momentary ($<itype>3); } + | ENUM '{' + { $<itype>2 = suspend_momentary (); + $$ = start_enum (NULL_TREE); } + enumlist maybecomma_warn '}' maybe_attribute + { $$ = finish_enum ($<ttype>3, nreverse ($4), $7); + resume_momentary ($<itype>2); } + | ENUM identifier + { $$ = xref_tag (ENUMERAL_TYPE, $2); } + ; + +maybecomma: + /* empty */ + | ',' + ; + +maybecomma_warn: + /* empty */ + | ',' + { if (pedantic) pedwarn ("comma at end of enumerator list"); } + ; + +component_decl_list: + component_decl_list2 + { $$ = $1; } + | component_decl_list2 component_decl + { $$ = chainon ($1, $2); + pedwarn ("no semicolon at end of struct or union"); } + ; + +component_decl_list2: /* empty */ + { $$ = NULL_TREE; } + | component_decl_list2 component_decl ';' + { $$ = chainon ($1, $2); } + | component_decl_list2 ';' + { if (pedantic) + pedwarn ("extra semicolon in struct or union specified"); } + /* foo(sizeof(struct{ @defs(ClassName)})); */ + | DEFS '(' CLASSNAME ')' + { + tree interface = lookup_interface ($3); + + if (interface) + $$ = get_class_ivars (interface); + else + { + error ("Cannot find interface declaration for `%s'", + IDENTIFIER_POINTER ($3)); + $$ = NULL_TREE; + } + } + ; + +/* There is a shift-reduce conflict here, because `components' may + start with a `typename'. It happens that shifting (the default resolution) + does the right thing, because it treats the `typename' as part of + a `typed_typespecs'. + + It is possible that this same technique would allow the distinction + between `notype_initdecls' and `initdecls' to be eliminated. + But I am being cautious and not trying it. */ + +component_decl: + typed_typespecs setspecs components + { $$ = $3; + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_typespecs + { if (pedantic) + pedwarn ("ANSI C forbids member declarations with no members"); + shadow_tag($1); + $$ = NULL_TREE; } + | nonempty_type_quals setspecs components + { $$ = $3; + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | nonempty_type_quals + { if (pedantic) + pedwarn ("ANSI C forbids member declarations with no members"); + shadow_tag($1); + $$ = NULL_TREE; } + | error + { $$ = NULL_TREE; } + ; + +components: + component_declarator + | components ',' component_declarator + { $$ = chainon ($1, $3); } + ; + +component_declarator: + save_filename save_lineno declarator maybe_attribute + { $$ = grokfield ($1, $2, $3, current_declspecs, NULL_TREE); + decl_attributes ($$, $4, prefix_attributes); } + | save_filename save_lineno + declarator ':' expr_no_commas maybe_attribute + { $$ = grokfield ($1, $2, $3, current_declspecs, $5); + decl_attributes ($$, $6, prefix_attributes); } + | save_filename save_lineno ':' expr_no_commas maybe_attribute + { $$ = grokfield ($1, $2, NULL_TREE, current_declspecs, $4); + decl_attributes ($$, $5, prefix_attributes); } + ; + +/* We chain the enumerators in reverse order. + They are put in forward order where enumlist is used. + (The order used to be significant, but no longer is so. + However, we still maintain the order, just to be clean.) */ + +enumlist: + enumerator + | enumlist ',' enumerator + { if ($1 == error_mark_node) + $$ = $1; + else + $$ = chainon ($3, $1); } + | error + { $$ = error_mark_node; } + ; + + +enumerator: + identifier + { $$ = build_enumerator ($1, NULL_TREE); } + | identifier '=' expr_no_commas + { $$ = build_enumerator ($1, $3); } + ; + +typename: + typed_typespecs absdcl + { $$ = build_tree_list ($1, $2); } + | nonempty_type_quals absdcl + { $$ = build_tree_list ($1, $2); } + ; + +absdcl: /* an absolute declarator */ + /* empty */ + { $$ = NULL_TREE; } + | absdcl1 + ; + +nonempty_type_quals: + TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $1, NULL_TREE); } + | nonempty_type_quals TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $2, $1); } + ; + +type_quals: + /* empty */ + { $$ = NULL_TREE; } + | type_quals TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $2, $1); } + ; + +absdcl1: /* a nonempty absolute declarator */ + '(' absdcl1 ')' + { $$ = $2; } + /* `(typedef)1' is `int'. */ + | '*' type_quals absdcl1 %prec UNARY + { $$ = make_pointer_declarator ($2, $3); } + | '*' type_quals %prec UNARY + { $$ = make_pointer_declarator ($2, NULL_TREE); } + | absdcl1 '(' parmlist %prec '.' + { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } + | absdcl1 '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, $3); } + | absdcl1 '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } + | '(' parmlist %prec '.' + { $$ = build_nt (CALL_EXPR, NULL_TREE, $2, NULL_TREE); } + | '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, NULL_TREE, $2); } + | '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, NULL_TREE, NULL_TREE); } + | attributes setattrs absdcl1 + { $$ = $3; } + ; + +/* at least one statement, the first of which parses without error. */ +/* stmts is used only after decls, so an invalid first statement + is actually regarded as an invalid decl and part of the decls. */ + +stmts: + lineno_stmt_or_labels + { + if (pedantic && $1) + pedwarn ("ANSI C forbids label at end of compound statement"); + } + ; + +lineno_stmt_or_labels: + lineno_stmt_or_label + | lineno_stmt_or_labels lineno_stmt_or_label + { $$ = $2; } + | lineno_stmt_or_labels errstmt + { $$ = 0; } + ; + +xstmts: + /* empty */ + | stmts + ; + +errstmt: error ';' + ; + +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 (); + } + ; + +/* 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: '{' '}' + { $$ = convert (void_type_node, integer_zero_node); } + | '{' pushlevel maybe_label_decls decls xstmts '}' + { emit_line_note (input_filename, lineno); + expand_end_bindings (getdecls (), 1, 0); + $$ = poplevel (1, 1, 0); + if (yychar == CONSTANT || yychar == STRING) + pop_momentary_nofree (); + else + pop_momentary (); } + | '{' pushlevel maybe_label_decls error '}' + { emit_line_note (input_filename, lineno); + expand_end_bindings (getdecls (), kept_level_p (), 0); + $$ = poplevel (kept_level_p (), 0, 0); + if (yychar == CONSTANT || yychar == STRING) + pop_momentary_nofree (); + else + pop_momentary (); } + | '{' pushlevel maybe_label_decls stmts '}' + { emit_line_note (input_filename, lineno); + expand_end_bindings (getdecls (), kept_level_p (), 0); + $$ = poplevel (kept_level_p (), 0, 0); + if (yychar == CONSTANT || yychar == STRING) + pop_momentary_nofree (); + else + pop_momentary (); } + ; + +/* Value is number of statements counted as of the closeparen. */ +simple_if: + if_prefix lineno_labeled_stmt +/* Make sure expand_end_cond is run once + for each call to expand_start_cond. + Otherwise a crash is likely. */ + | if_prefix error + ; + +if_prefix: + IF '(' expr ')' + { emit_line_note ($<filename>-1, $<lineno>0); + expand_start_cond (truthvalue_conversion ($3), 0); + $<itype>$ = stmt_count; + if_stmt_file = $<filename>-1; + if_stmt_line = $<lineno>0; + position_after_white_space (); } + ; + +/* This is a subroutine of stmt. + It is used twice, once for valid DO statements + and once for catching errors in parsing the end test. */ +do_stmt_start: + DO + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + /* See comment in `while' alternative, above. */ + emit_nop (); + expand_start_loop_continue_elsewhere (1); + position_after_white_space (); } + lineno_labeled_stmt WHILE + { expand_loop_continue_here (); } + ; + +save_filename: + { $$ = input_filename; } + ; + +save_lineno: + { $$ = lineno; } + ; + +lineno_labeled_stmt: + save_filename save_lineno stmt + { } +/* | save_filename save_lineno error + { } +*/ + | save_filename save_lineno label lineno_labeled_stmt + { } + ; + +lineno_stmt_or_label: + save_filename save_lineno stmt_or_label + { $$ = $3; } + ; + +stmt_or_label: + stmt + { $$ = 0; } + | label + { $$ = 1; } + ; + +/* Parse a single real statement, not including any labels. */ +stmt: + compstmt + { stmt_count++; } + | all_iter_stmt + | expr ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); +/* It appears that this should not be done--that a non-lvalue array + shouldn't get an error if the value isn't used. + Section 3.2.2.1 says that an array lvalue gets converted to a pointer + if it appears as a top-level expression, + but says nothing about non-lvalue arrays. */ +#if 0 + /* Call default_conversion to get an error + on referring to a register array if pedantic. */ + if (TREE_CODE (TREE_TYPE ($1)) == ARRAY_TYPE + || TREE_CODE (TREE_TYPE ($1)) == FUNCTION_TYPE) + $1 = default_conversion ($1); +#endif + iterator_expand ($1); + clear_momentary (); } + | simple_if ELSE + { expand_start_else (); + $<itype>1 = stmt_count; + position_after_white_space (); } + lineno_labeled_stmt + { expand_end_cond (); + if (extra_warnings && stmt_count == $<itype>1) + warning ("empty body in an else-statement"); } + | simple_if %prec IF + { expand_end_cond (); + /* This warning is here instead of in simple_if, because we + do not want a warning if an empty if is followed by an + else statement. Increment stmt_count so we don't + give a second error if this is a nested `if'. */ + if (extra_warnings && stmt_count++ == $<itype>1) + warning_with_file_and_line (if_stmt_file, if_stmt_line, + "empty body in an if-statement"); } +/* Make sure expand_end_cond is run once + for each call to expand_start_cond. + Otherwise a crash is likely. */ + | simple_if ELSE error + { expand_end_cond (); } + | WHILE + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + /* The emit_nop used to come before emit_line_note, + but that made the nop seem like part of the preceding line. + And that was confusing when the preceding line was + inside of an if statement and was not really executed. + I think it ought to work to put the nop after the line number. + We will see. --rms, July 15, 1991. */ + emit_nop (); } + '(' expr ')' + { /* Don't start the loop till we have succeeded + in parsing the end test. This is to make sure + that we end every loop we start. */ + expand_start_loop (1); + emit_line_note (input_filename, lineno); + expand_exit_loop_if_false (NULL_PTR, + truthvalue_conversion ($4)); + position_after_white_space (); } + lineno_labeled_stmt + { expand_end_loop (); } + | do_stmt_start + '(' expr ')' ';' + { emit_line_note (input_filename, lineno); + expand_exit_loop_if_false (NULL_PTR, + truthvalue_conversion ($3)); + expand_end_loop (); + clear_momentary (); } +/* This rule is needed to make sure we end every loop we start. */ + | do_stmt_start error + { expand_end_loop (); + clear_momentary (); } + | FOR + '(' xexpr ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + /* See comment in `while' alternative, above. */ + emit_nop (); + if ($3) c_expand_expr_stmt ($3); + /* Next step is to call expand_start_loop_continue_elsewhere, + but wait till after we parse the entire for (...). + Otherwise, invalid input might cause us to call that + fn without calling expand_end_loop. */ + } + xexpr ';' + /* Can't emit now; wait till after expand_start_loop... */ + { $<lineno>7 = lineno; + $<filename>$ = input_filename; } + xexpr ')' + { + /* Start the loop. Doing this after parsing + all the expressions ensures we will end the loop. */ + expand_start_loop_continue_elsewhere (1); + /* Emit the end-test, with a line number. */ + emit_line_note ($<filename>8, $<lineno>7); + if ($6) + expand_exit_loop_if_false (NULL_PTR, + truthvalue_conversion ($6)); + /* Don't let the tree nodes for $9 be discarded by + clear_momentary during the parsing of the next stmt. */ + push_momentary (); + $<lineno>7 = lineno; + $<filename>8 = input_filename; + position_after_white_space (); } + lineno_labeled_stmt + { /* Emit the increment expression, with a line number. */ + emit_line_note ($<filename>8, $<lineno>7); + expand_loop_continue_here (); + if ($9) + c_expand_expr_stmt ($9); + if (yychar == CONSTANT || yychar == STRING) + pop_momentary_nofree (); + else + pop_momentary (); + expand_end_loop (); } + | SWITCH '(' expr ')' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + c_expand_start_case ($3); + /* Don't let the tree nodes for $3 be discarded by + clear_momentary during the parsing of the next stmt. */ + push_momentary (); + position_after_white_space (); } + lineno_labeled_stmt + { expand_end_case ($3); + if (yychar == CONSTANT || yychar == STRING) + pop_momentary_nofree (); + else + pop_momentary (); } + | BREAK ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + if ( ! expand_exit_something ()) + error ("break statement not within loop or switch"); } + | CONTINUE ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + if (! expand_continue_loop (NULL_PTR)) + error ("continue statement not within a loop"); } + | RETURN ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + c_expand_return (NULL_TREE); } + | RETURN expr ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + c_expand_return ($2); } + | ASM_KEYWORD maybe_type_qual '(' expr ')' ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + STRIP_NOPS ($4); + if ((TREE_CODE ($4) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND ($4, 0)) == STRING_CST) + || TREE_CODE ($4) == STRING_CST) + expand_asm ($4); + else + error ("argument of `asm' is not a constant string"); } + /* This is the case with just output operands. */ + | ASM_KEYWORD maybe_type_qual '(' expr ':' asm_operands ')' ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + c_expand_asm_operands ($4, $6, NULL_TREE, NULL_TREE, + $2 == ridpointers[(int)RID_VOLATILE], + input_filename, lineno); } + /* This is the case with input operands as well. */ + | ASM_KEYWORD maybe_type_qual '(' expr ':' asm_operands ':' asm_operands ')' ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + c_expand_asm_operands ($4, $6, $8, NULL_TREE, + $2 == ridpointers[(int)RID_VOLATILE], + input_filename, lineno); } + /* This is the case with clobbered registers as well. */ + | ASM_KEYWORD maybe_type_qual '(' expr ':' asm_operands ':' + asm_operands ':' asm_clobbers ')' ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + c_expand_asm_operands ($4, $6, $8, $10, + $2 == ridpointers[(int)RID_VOLATILE], + input_filename, lineno); } + | GOTO identifier ';' + { tree decl; + stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + decl = lookup_label ($2); + if (decl != 0) + { + TREE_USED (decl) = 1; + expand_goto (decl); + } + } + | GOTO '*' expr ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + expand_computed_goto (convert (ptr_type_node, $3)); } + | ';' + ; + +all_iter_stmt: + all_iter_stmt_simple +/* | all_iter_stmt_with_decl */ + ; + +all_iter_stmt_simple: + FOR '(' primary ')' + { + /* The value returned by this action is */ + /* 1 if everything is OK */ + /* 0 in case of error or already bound iterator */ + + $<itype>$ = 0; + if (TREE_CODE ($3) != VAR_DECL) + error ("invalid `for (ITERATOR)' syntax"); + else if (! ITERATOR_P ($3)) + error ("`%s' is not an iterator", + IDENTIFIER_POINTER (DECL_NAME ($3))); + else if (ITERATOR_BOUND_P ($3)) + error ("`for (%s)' inside expansion of same iterator", + IDENTIFIER_POINTER (DECL_NAME ($3))); + else + { + $<itype>$ = 1; + iterator_for_loop_start ($3); + } + } + lineno_labeled_stmt + { + if ($<itype>5) + iterator_for_loop_end ($3); + } + +/* This really should allow any kind of declaration, + for generality. Fix it before turning it back on. + +all_iter_stmt_with_decl: + FOR '(' ITERATOR pushlevel setspecs iterator_spec ')' + { +*/ /* The value returned by this action is */ + /* 1 if everything is OK */ + /* 0 in case of error or already bound iterator */ +/* + iterator_for_loop_start ($6); + } + lineno_labeled_stmt + { + iterator_for_loop_end ($6); + emit_line_note (input_filename, lineno); + expand_end_bindings (getdecls (), 1, 0); + $<ttype>$ = poplevel (1, 1, 0); + if (yychar == CONSTANT || yychar == STRING) + pop_momentary_nofree (); + else + pop_momentary (); + } +*/ + +/* Any kind of label, including jump labels and case labels. + ANSI C accepts labels only before statements, but we allow them + also at the end of a compound statement. */ + +label: CASE expr_no_commas ':' + { register tree value = check_case_value ($2); + register tree label + = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); + + stmt_count++; + + if (value != error_mark_node) + { + tree duplicate; + int success = pushcase (value, convert_and_check, + label, &duplicate); + if (success == 1) + error ("case label not within a switch statement"); + else if (success == 2) + { + error ("duplicate case value"); + error_with_decl (duplicate, "this is the first entry for that value"); + } + else if (success == 3) + warning ("case value out of range"); + else if (success == 5) + error ("case label within scope of cleanup or variable array"); + } + position_after_white_space (); } + | CASE expr_no_commas ELLIPSIS expr_no_commas ':' + { register tree value1 = check_case_value ($2); + register tree value2 = check_case_value ($4); + register tree label + = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); + + stmt_count++; + + 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 case value"); + error_with_decl (duplicate, "this is the first entry for that value"); + } + else if (success == 3) + warning ("case value out of range"); + else if (success == 4) + warning ("empty case range"); + else if (success == 5) + error ("case label within scope of cleanup or variable array"); + } + position_after_white_space (); } + | DEFAULT ':' + { + tree duplicate; + register tree label + = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); + int success = pushcase (NULL_TREE, 0, label, &duplicate); + stmt_count++; + 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"); + } + position_after_white_space (); } + | identifier ':' + { tree label = define_label (input_filename, lineno, $1); + stmt_count++; + emit_nop (); + if (label) + expand_label (label); + position_after_white_space (); } + ; + +/* 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 + ; + +/* 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 ($1, $3); } + ; + +asm_operand: + STRING '(' expr ')' + { $$ = build_tree_list ($1, $3); } + ; + +asm_clobbers: + string + { $$ = tree_cons (NULL_TREE, combine_strings ($1), NULL_TREE); } + | asm_clobbers ',' string + { $$ = tree_cons (NULL_TREE, combine_strings ($3), $1); } + ; + +/* This is what appears inside the parens in a function declarator. + Its value is a list of ..._TYPE nodes. */ +parmlist: + { pushlevel (0); + clear_parm_order (); + declare_parm_level (0); } + parmlist_1 + { $$ = $2; + parmlist_tags_warning (); + poplevel (0, 0, 0); } + ; + +parmlist_1: + parmlist_2 ')' + | parms ';' + { tree parm; + if (pedantic) + pedwarn ("ANSI C forbids forward parameter declarations"); + /* Mark the forward decls as such. */ + for (parm = getdecls (); parm; parm = TREE_CHAIN (parm)) + TREE_ASM_WRITTEN (parm) = 1; + clear_parm_order (); } + parmlist_1 + { $$ = $4; } + | error ')' + { $$ = tree_cons (NULL_TREE, NULL_TREE, NULL_TREE); } + ; + +/* This is what appears inside the parens in a function declarator. + Is value is represented in the format that grokdeclarator expects. */ +parmlist_2: /* empty */ + { $$ = get_parm_info (0); } + | ELLIPSIS + { $$ = get_parm_info (0); + /* Gcc used to allow this as an extension. However, it does + not work for all targets, and thus has been disabled. + Also, since func (...) and func () are indistinguishable, + it caused problems with the code in expand_builtin which + tries to verify that BUILT_IN_NEXT_ARG is being used + correctly. */ + error ("ANSI C requires a named argument before `...'"); + } + | parms + { $$ = get_parm_info (1); } + | parms ',' ELLIPSIS + { $$ = get_parm_info (0); } + ; + +parms: + parm + { push_parm_decl ($1); } + | parms ',' parm + { push_parm_decl ($3); } + ; + +/* A single parameter declaration or parameter type name, + as found in a parmlist. */ +parm: + typed_declspecs setspecs parm_declarator maybe_attribute + { $$ = build_tree_list (build_tree_list (current_declspecs, + $3), + build_tree_list (prefix_attributes, + $4)); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs setspecs notype_declarator maybe_attribute + { $$ = build_tree_list (build_tree_list (current_declspecs, + $3), + build_tree_list (prefix_attributes, + $4)); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs setspecs absdcl maybe_attribute + { $$ = build_tree_list (build_tree_list (current_declspecs, + $3), + build_tree_list (prefix_attributes, + $4)); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_declarator maybe_attribute + { $$ = build_tree_list (build_tree_list (current_declspecs, + $3), + build_tree_list (prefix_attributes, + $4)); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + + | declmods setspecs absdcl maybe_attribute + { $$ = build_tree_list (build_tree_list (current_declspecs, + $3), + build_tree_list (prefix_attributes, + $4)); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + ; + +/* This is used in a function definition + where either a parmlist or an identifier list is ok. + Its value is a list of ..._TYPE nodes or a list of identifiers. */ +parmlist_or_identifiers: + { pushlevel (0); + clear_parm_order (); + declare_parm_level (1); } + parmlist_or_identifiers_1 + { $$ = $2; + parmlist_tags_warning (); + poplevel (0, 0, 0); } + ; + +parmlist_or_identifiers_1: + parmlist_1 + | identifiers ')' + { tree t; + for (t = $1; t; t = TREE_CHAIN (t)) + if (TREE_VALUE (t) == NULL_TREE) + error ("`...' in old-style identifier list"); + $$ = tree_cons (NULL_TREE, NULL_TREE, $1); } + ; + +/* A nonempty list of identifiers. */ +identifiers: + IDENTIFIER + { $$ = build_tree_list (NULL_TREE, $1); } + | identifiers ',' IDENTIFIER + { $$ = chainon ($1, build_tree_list (NULL_TREE, $3)); } + ; + +/* 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)); } + ; + +/* Objective-C productions. */ + +objcdef: + classdef + | classdecl + | 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); + } + +aliasdecl: + ALIAS identifier identifier ';' + { + objc_declare_alias ($2, $3); + } + +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 ':' identifier protocolrefs '{' + { + objc_interface_context = objc_ivar_context + = start_class (CLASS_INTERFACE_TYPE, $2, $4, $5); + 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 ':' identifier protocolrefs + { + objc_interface_context + = start_class (CLASS_INTERFACE_TYPE, $2, $4, $5); + 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 ':' identifier '{' + { + objc_implementation_context = objc_ivar_context + = start_class (CLASS_IMPLEMENTATION_TYPE, $2, $4, NULL_TREE); + objc_public_flag = 0; + } + ivar_decl_list '}' + { + objc_ivar_chain + = continue_class (objc_implementation_context); + } + + | IMPLEMENTATION identifier ':' identifier + { + objc_implementation_context + = start_class (CLASS_IMPLEMENTATION_TYPE, $2, $4, 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 + { + remember_protocol_qualifiers (); + objc_interface_context + = start_protocol(PROTOCOL_INTERFACE_TYPE, $2, $3); + } + methodprotolist END + { + forget_protocol_qualifiers(); + finish_protocol(objc_interface_context); + objc_interface_context = NULL_TREE; + } + ; + +protocolrefs: + /* empty */ + { + $$ = NULL_TREE; + } + | ARITHCOMPARE identifier_list ARITHCOMPARE + { + if ($1 == LT_EXPR && $3 == GT_EXPR) + $$ = $2; + else + YYERROR1; + } + ; + +ivar_decl_list: + ivar_decl_list visibility_spec ivar_decls + | ivar_decls + ; + +visibility_spec: + PRIVATE { objc_public_flag = 2; } + | PROTECTED { objc_public_flag = 0; } + | PUBLIC { objc_public_flag = 1; } + ; + +ivar_decls: + /* empty */ + { + $$ = NULL_TREE; + } + | ivar_decls ivar_decl ';' + | ivar_decls ';' + { + if (pedantic) + pedwarn ("extra semicolon in struct or union specified"); + } + ; + + +/* There is a shift-reduce conflict here, because `components' may + start with a `typename'. It happens that shifting (the default resolution) + does the right thing, because it treats the `typename' as part of + a `typed_typespecs'. + + It is possible that this same technique would allow the distinction + between `notype_initdecls' and `initdecls' to be eliminated. + But I am being cautious and not trying it. */ + +ivar_decl: + typed_typespecs setspecs ivars + { $$ = $3; + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | nonempty_type_quals setspecs ivars + { $$ = $3; + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($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: + '+' + { + remember_protocol_qualifiers (); + if (objc_implementation_context) + objc_inherit_code = CLASS_METHOD_DECL; + else + fatal ("method definition not in class context"); + } + methoddecl + { + forget_protocol_qualifiers (); + 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; + } + + | '-' + { + remember_protocol_qualifiers (); + if (objc_implementation_context) + objc_inherit_code = INSTANCE_METHOD_DECL; + else + fatal ("method definition not in class context"); + } + methoddecl + { + forget_protocol_qualifiers (); + 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 + ; + +methoddecl: + '(' typename ')' unaryselector + { + $$ = build_method_decl (objc_inherit_code, $2, $4, NULL_TREE); + } + + | unaryselector + { + $$ = build_method_decl (objc_inherit_code, NULL_TREE, $1, NULL_TREE); + } + + | '(' typename ')' keywordselector optparmlist + { + $$ = build_method_decl (objc_inherit_code, $2, $4, $5); + } + + | keywordselector optparmlist + { + $$ = build_method_decl (objc_inherit_code, NULL_TREE, $1, $2); + } + ; + +/* "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 ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs ';' + { shadow_tag ($1); } + | declmods ';' + { pedwarn ("empty declaration"); } + ; + +myparms: + myparm + { push_parm_decl ($1); } + | myparms ',' myparm + { 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: + parm_declarator maybe_attribute + { $$ = build_tree_list (build_tree_list (current_declspecs, + $1), + build_tree_list (prefix_attributes, + $2)); } + | notype_declarator maybe_attribute + { $$ = build_tree_list (build_tree_list (current_declspecs, + $1), + build_tree_list (prefix_attributes, + $2)); } + | absdcl maybe_attribute + { $$ = build_tree_list (build_tree_list (current_declspecs, + $1), + build_tree_list (prefix_attributes, + $2)); } + ; + +optparmlist: + /* empty */ + { + $$ = NULL_TREE; + } + | ',' ELLIPSIS + { + /* oh what a kludge! */ + $$ = (tree)1; + } + | ',' + { + pushlevel (0); + } + parmlist_2 + { + /* 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 + | reservedwords + ; + +reservedwords: + ENUM { $$ = get_identifier (token_buffer); } + | STRUCT { $$ = get_identifier (token_buffer); } + | UNION { $$ = get_identifier (token_buffer); } + | IF { $$ = get_identifier (token_buffer); } + | ELSE { $$ = get_identifier (token_buffer); } + | WHILE { $$ = get_identifier (token_buffer); } + | DO { $$ = get_identifier (token_buffer); } + | FOR { $$ = get_identifier (token_buffer); } + | SWITCH { $$ = get_identifier (token_buffer); } + | CASE { $$ = get_identifier (token_buffer); } + | DEFAULT { $$ = get_identifier (token_buffer); } + | BREAK { $$ = get_identifier (token_buffer); } + | CONTINUE { $$ = get_identifier (token_buffer); } + | RETURN { $$ = get_identifier (token_buffer); } + | GOTO { $$ = get_identifier (token_buffer); } + | ASM_KEYWORD { $$ = get_identifier (token_buffer); } + | SIZEOF { $$ = get_identifier (token_buffer); } + | TYPEOF { $$ = get_identifier (token_buffer); } + | ALIGNOF { $$ = get_identifier (token_buffer); } + | TYPESPEC | TYPE_QUAL + ; + +keyworddecl: + selector ':' '(' typename ')' identifier + { + $$ = build_keyword_decl ($1, $4, $6); + } + + | selector ':' identifier + { + $$ = build_keyword_decl ($1, NULL_TREE, $3); + } + + | ':' '(' typename ')' identifier + { + $$ = build_keyword_decl (NULL_TREE, $3, $5); + } + + | ':' 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: + expr + | CLASSNAME + { + $$ = get_class_reference ($1); + } + ; + +objcmessageexpr: + '[' + { objc_receiver_context = 1; } + receiver + { objc_receiver_context = 0; } + messageargs ']' + { + $$ = build_tree_list ($3, $5); + } + ; + +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); + } + ; + +objcselectorexpr: + SELECTOR '(' selectorarg ')' + { + $$ = $3; + } + ; + +objcprotocolexpr: + PROTOCOL '(' identifier ')' + { + $$ = $3; + } + ; + +/* extension to support C-structures in the archiver */ + +objcencodeexpr: + ENCODE '(' typename ')' + { + $$ = groktypename ($3); + } + ; + +%% diff --git a/gnu/usr.bin/gcc/cc1plus/parse.y b/gnu/usr.bin/gcc/cc1plus/parse.y new file mode 100644 index 00000000000..fd034af418d --- /dev/null +++ b/gnu/usr.bin/gcc/cc1plus/parse.y @@ -0,0 +1,3906 @@ +/* YACC parser for 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" + +/* 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; + +/* 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 + +/* 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 ALL +%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 + +/* 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; + +/* 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); + 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 (); } + ; + +extdef: + fndef + { if (pending_inlines) do_pending_inlines (); } + | datadef + { if (pending_inlines) do_pending_inlines (); } + | 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 + ; + +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; } + ; + +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 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); + } + | 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); } + ; + +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 ($$); } + | 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); } + | 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 + | 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, ¤t_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, ¤t_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, ¤t_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, ¤t_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. */ + | '[' 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"); + } + ; + +/* 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, ¤t_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, ¤t_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, ¤t_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, ¤t_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, ¤t_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; } + ; + +/* 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 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 + ; + +/* 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); } + ; + +.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: +/* + typed_declspecs dont_see_typename '*' IDENTIFIER + { tree specs = strip_attrs ($1); + $$ = build_tree_list (specs, build_parse_node (INDIRECT_REF, $4)); + see_typename (); } + | typed_declspecs dont_see_typename '&' IDENTIFIER + { tree specs = strip_attrs ($1); + $$ = build_tree_list (specs, build_parse_node (ADDR_EXPR, $4)); + see_typename (); } + | TYPENAME IDENTIFIER + { $$ = build_tree_list (get_decl_list ($$), $2); } + | TYPESPEC IDENTIFIER + { $$ = build_tree_list (get_decl_list ($$), $2); } + | */ + /* 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 (); } + ; + +/* +dont_see_typename: %prec EMPTY + { dont_see_typename (); } + ; + +try_for_typename: + { + if ($<ttype>-1 == error_mark_node) + $$ = 0; + else + { + $$ = 1; + pushclass ($<ttype>-1, 1); + } + } + ; +*/ + +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]; } + ; + +%% + +#ifdef SPEW_DEBUG +const char * +debug_yytranslate (value) + int value; +{ + return yytname[YYTRANSLATE (value)]; +} + +#endif diff --git a/gnu/usr.bin/gcc/common/bi-parser.y b/gnu/usr.bin/gcc/common/bi-parser.y new file mode 100644 index 00000000000..0a03d0f05ad --- /dev/null +++ b/gnu/usr.bin/gcc/common/bi-parser.y @@ -0,0 +1,169 @@ +/* Bytecode definition file parser. + Copyright (C) 1993 Free Software Foundation, Inc. + +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. */ + + +%{ + +#include <stdio.h> +#include "hconfig.h" +#include "bi-defs.h" + +extern char yytext[]; +extern int yyleng; + + +/* Chain of all defs built by the parser. */ +struct def *defs; +int ndefs; + +static struct node *makenode (); +static struct variation *makevar (); +static struct def *makedef (); + +void yyerror (); + +%} + +%union +{ + char *string; + struct def *def; + struct variation *variation; + struct node *node; +} + +%token <string> DEFOP STRING +%type <string> opt_string +%type <def> defs def +%type <variation> variations variation +%type <node> list items item + +%% + +top: + defs + { defs = $1; } + ; + +defs: + def + | defs def + { $2->next = $1; $$ = $2; } + ; + +def: + DEFOP '(' STRING ',' opt_string ',' '(' variations ')' ')' + { $$ = makedef ($3, $5, $8); } + ; + +variations: + variation + | variations ',' variation + { $3->next = $1; $$ = $3; } + ; + +variation: + '(' opt_string ')' + { $$ = makevar ($2, (struct node *) NULL, (struct node *) NULL, (struct node *) NULL); } + | '(' opt_string ',' list ')' + { $$ = makevar ($2, $4, (struct node *) NULL, (struct node *) NULL); } + | '(' opt_string ',' list ',' list ')' + { $$ = makevar ($2, $4, $6, (struct node *) NULL); } + | '(' opt_string ',' list ',' list ',' list ')' + { $$ = makevar ($2, $4, $6, $8); } + ; + +opt_string: + /* empty */ { $$ = ""; } + | STRING { $$ = $1; } + ; + +list: + '(' items ')' + { $$ = $2; } + | /* empty */ + { $$ = NULL; } + ; + +items: + item + /* Note right recursion. */ + | item ',' items + { $1->next = $3; $$ = $1; } + ; + +item: + STRING + { $$ = makenode ($1); } + ; + +%% + +static struct node * +makenode (s) + char *s; +{ + struct node *n; + + n = (struct node *) malloc (sizeof (struct node)); + n->text = s; + n->next = NULL; + return n; +} + +static struct variation * +makevar (name, inputs, outputs, literals) + char *name; + struct node *inputs, *outputs, *literals; +{ + struct variation *v; + + v = (struct variation *) malloc (sizeof (struct variation)); + v->name = name; + v->code = ndefs++; + v->inputs = inputs; + v->outputs = outputs; + v->literals = literals; + v->next = NULL; + return v; +} + +static struct def * +makedef (name, template, vars) + char *name, *template; + struct variation *vars; +{ + struct def *d; + + d = (struct def *) malloc (sizeof (struct def)); + d->basename = name; + d->template = template; + d->variations = vars; + d->next = NULL; + return d; +} + +void +yyerror (s) + char *s; +{ + fprintf (stderr, "syntax error in input\n"); + exit (FATAL_EXIT_CODE); +} diff --git a/gnu/usr.bin/gcc/common/c-parse.y b/gnu/usr.bin/gcc/common/c-parse.y new file mode 100644 index 00000000000..e7aed2c036c --- /dev/null +++ b/gnu/usr.bin/gcc/common/c-parse.y @@ -0,0 +1,2068 @@ +/* YACC parser for C syntax and for Objective C. -*-c-*- + Copyright (C) 1987, 88, 89, 92, 93, 94, 1995 Free Software Foundation, Inc. + +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 file defines the grammar of C and that of Objective C. + ifobjc ... end ifobjc conditionals contain code for Objective C only. + ifc ... end ifc conditionals contain code for C only. + Sed commands in Makefile.in are used to convert this file into + c-parse.y and into objc-parse.y. */ + +/* To whomever it may concern: I have heard that such a thing was once + written by AT&T, but I have never seen it. */ + +%expect 34 + +/* These are the 23 conflicts you should get in parse.output; + the state numbers may vary if minor changes in the grammar are made. + +State 42 contains 1 shift/reduce conflict. (Two ways to parse ATTRIBUTE.) +State 44 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 103 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 110 contains 1 shift/reduce conflict. (Two ways to parse ATTRIBUTE.) +State 111 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 115 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 132 contains 1 shift/reduce conflict. (See comment at component_decl.) +State 180 contains 1 shift/reduce conflict. (Two ways to parse ATTRIBUTE.) +State 194 contains 2 shift/reduce conflict. (Four ways to parse this.) +State 202 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 214 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 220 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 304 contains 2 shift/reduce conflicts. (Four ways to parse this.) +State 335 contains 2 shift/reduce conflicts. (Four ways to parse this.) +State 347 contains 1 shift/reduce conflict. (Two ways to parse ATTRIBUTES.) +State 352 contains 1 shift/reduce conflict. (Two ways to parse ATTRIBUTES.) +State 383 contains 2 shift/reduce conflicts. (Four ways to parse this.) +State 434 contains 2 shift/reduce conflicts. (Four ways to parse this.) */ + + +%{ +#include <stdio.h> +#include <errno.h> +#include <setjmp.h> + +#include "config.h" +#include "tree.h" +#include "input.h" +#include "c-lex.h" +#include "c-tree.h" +#include "flags.h" + +#ifdef MULTIBYTE_CHARS +#include <stdlib.h> +#include <locale.h> +#endif + + +/* Since parsers are distinct for each language, put the language string + definition here. */ +char *language_string = "GNU C"; + +#ifndef errno +extern int errno; +#endif + +void yyerror (); + +/* Like YYERROR but do call yyerror. */ +#define YYERROR1 { yyerror ("syntax error"); YYERROR; } + +/* Cause the `yydebug' variable to be defined. */ +#define YYDEBUG 1 +%} + +%start program + +%union {long itype; tree ttype; enum tree_code code; + char *filename; int lineno; int ends_in_label; } + +/* 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 TYPEOF ALIGNOF +%token ATTRIBUTE EXTENSION LABEL +%token REALPART IMAGPART + +/* Add precedence rules to solve dangling else s/r conflict */ +%nonassoc IF +%nonassoc ELSE + +/* 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. */ + +%right <code> ASSIGN '=' +%right <code> '?' ':' +%left <code> OROR +%left <code> ANDAND +%left <code> '|' +%left <code> '^' +%left <code> '&' +%left <code> EQCOMPARE +%left <code> ARITHCOMPARE +%left <code> LSHIFT RSHIFT +%left <code> '+' '-' +%left <code> '*' '/' '%' +%right <code> UNARY PLUSPLUS MINUSMINUS +%left HYPERUNARY +%left <code> POINTSAT '.' '(' '[' + +/* The Objective-C keywords. These are included in C and in + Objective C, so that the token codes are the same in both. */ +%token INTERFACE IMPLEMENTATION END SELECTOR DEFS ENCODE +%token CLASSNAME PUBLIC PRIVATE PROTECTED PROTOCOL OBJECTNAME CLASS ALIAS + +/* Objective-C string constants in raw form. + yylval is an OBJC_STRING_CST node. */ +%token OBJC_STRING + + +%type <code> unop + +%type <ttype> identifier IDENTIFIER TYPENAME CONSTANT expr nonnull_exprlist exprlist +%type <ttype> expr_no_commas cast_expr unary_expr primary string STRING +%type <ttype> typed_declspecs reserved_declspecs +%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 <ttype> initdecls notype_initdecls initdcl notype_initdcl +%type <ttype> init maybeasm +%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 + +%type <ttype> declarator +%type <ttype> notype_declarator after_type_declarator +%type <ttype> parm_declarator + +%type <ttype> structsp component_decl_list component_decl_list2 +%type <ttype> component_decl components component_declarator +%type <ttype> enumlist enumerator +%type <ttype> typename absdcl absdcl1 type_quals +%type <ttype> xexpr parms parm identifiers + +%type <ttype> parmlist parmlist_1 parmlist_2 +%type <ttype> parmlist_or_identifiers parmlist_or_identifiers_1 +%type <ttype> identifiers_or_typenames + +%type <itype> setspecs + +%type <ends_in_label> lineno_stmt_or_label lineno_stmt_or_labels stmt_or_label + +%type <filename> save_filename +%type <lineno> save_lineno + + +%{ +/* Number of statements (loosely speaking) seen so far. */ +static int stmt_count; + +/* Input file and line number of the end of the body of last simple_if; + used by the stmt-rule immediately after simple_if returns. */ +static char *if_stmt_file; +static int if_stmt_line; + +/* List of types and structure classes of the current declaration. */ +static tree current_declspecs; +static tree prefix_attributes = NULL_TREE; + +/* Stack of saved values of current_declspecs and prefix_attributes. */ +static tree declspec_stack; + +/* 1 if we explained undeclared var errors. */ +static int undeclared_variable_notice; + + +/* 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 (); +%} + +%% +program: /* empty */ + { if (pedantic) + pedwarn ("ANSI C forbids an empty source file"); + finish_file (); + } + | extdefs + { + /* In case there were missing closebraces, + get us back to the global binding level. */ + while (! global_bindings_p ()) + poplevel (0, 0, 0); + 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; } extdef + | extdefs {$<ttype>$ = NULL_TREE; } extdef + ; + +extdef: + fndef + | datadef + | ASM_KEYWORD '(' expr ')' ';' + { STRIP_NOPS ($3); + if ((TREE_CODE ($3) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND ($3, 0)) == STRING_CST) + || TREE_CODE ($3) == STRING_CST) + assemble_asm ($3); + else + error ("argument of `asm' is not a constant string"); } + ; + +datadef: + setspecs notype_initdecls ';' + { if (pedantic) + error ("ANSI C forbids data definition with no type or storage class"); + else if (!flag_traditional) + warning ("data definition has no type or storage class"); + + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($1); } + | declmods setspecs notype_initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs setspecs initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods ';' + { pedwarn ("empty declaration"); } + | typed_declspecs ';' + { shadow_tag ($1); } + | error ';' + | error '}' + | ';' + { if (pedantic) + pedwarn ("ANSI C does not allow extra `;' outside of a function"); } + ; + +fndef: + typed_declspecs setspecs declarator + { if (! start_function ($1, $3, prefix_attributes, + NULL_TREE, 0)) + YYERROR1; + reinit_parse_for_function (); } + xdecls + { store_parm_decls (); } + compstmt_or_error + { finish_function (0); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs setspecs declarator error + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_declarator + { if (! start_function ($1, $3, prefix_attributes, + NULL_TREE, 0)) + YYERROR1; + reinit_parse_for_function (); } + xdecls + { store_parm_decls (); } + compstmt_or_error + { finish_function (0); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_declarator error + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | setspecs notype_declarator + { if (! start_function (NULL_TREE, $2, + prefix_attributes, NULL_TREE, 0)) + YYERROR1; + reinit_parse_for_function (); } + xdecls + { store_parm_decls (); } + compstmt_or_error + { finish_function (0); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($1); } + | setspecs notype_declarator error + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($1); } + ; + +identifier: + IDENTIFIER + | TYPENAME + ; + +unop: '&' + { $$ = ADDR_EXPR; } + | '-' + { $$ = NEGATE_EXPR; } + | '+' + { $$ = CONVERT_EXPR; } + | PLUSPLUS + { $$ = PREINCREMENT_EXPR; } + | MINUSMINUS + { $$ = PREDECREMENT_EXPR; } + | '~' + { $$ = BIT_NOT_EXPR; } + | '!' + { $$ = TRUTH_NOT_EXPR; } + ; + +expr: nonnull_exprlist + { $$ = build_compound_expr ($1); } + ; + +exprlist: + /* empty */ + { $$ = NULL_TREE; } + | nonnull_exprlist + ; + +nonnull_exprlist: + expr_no_commas + { $$ = build_tree_list (NULL_TREE, $1); } + | nonnull_exprlist ',' expr_no_commas + { chainon ($1, build_tree_list (NULL_TREE, $3)); } + ; + +unary_expr: + primary + | '*' cast_expr %prec UNARY + { $$ = build_indirect_ref ($2, "unary *"); } + /* __extension__ turns off -pedantic for following primary. */ + | EXTENSION + { $<itype>1 = pedantic; + pedantic = 0; } + cast_expr %prec UNARY + { $$ = $3; + pedantic = $<itype>1; } + | unop cast_expr %prec UNARY + { $$ = build_unary_op ($1, $2, 0); + overflow_warning ($$); } + /* Refer to the address of a label as a pointer. */ + | ANDAND identifier + { tree label = lookup_label ($2); + if (pedantic) + pedwarn ("ANSI C forbids `&&'"); + if (label == 0) + $$ = null_pointer_node; + else + { + TREE_USED (label) = 1; + $$ = build1 (ADDR_EXPR, ptr_type_node, label); + TREE_CONSTANT ($$) = 1; + } + } +/* This seems to be impossible on some machines, so let's turn it off. + You can use __builtin_next_arg to find the anonymous stack args. + | '&' ELLIPSIS + { tree types = TYPE_ARG_TYPES (TREE_TYPE (current_function_decl)); + $$ = error_mark_node; + if (TREE_VALUE (tree_last (types)) == void_type_node) + error ("`&...' used in function with fixed number of arguments"); + else + { + if (pedantic) + pedwarn ("ANSI C forbids `&...'"); + $$ = tree_last (DECL_ARGUMENTS (current_function_decl)); + $$ = build_unary_op (ADDR_EXPR, $$, 0); + } } +*/ + | 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"); + $$ = c_sizeof (TREE_TYPE ($2)); } + | SIZEOF '(' typename ')' %prec HYPERUNARY + { $$ = c_sizeof (groktypename ($3)); } + | ALIGNOF unary_expr %prec UNARY + { $$ = c_alignof_expr ($2); } + | ALIGNOF '(' typename ')' %prec HYPERUNARY + { $$ = c_alignof (groktypename ($3)); } + | REALPART cast_expr %prec UNARY + { $$ = build_unary_op (REALPART_EXPR, $2, 0); } + | IMAGPART cast_expr %prec UNARY + { $$ = build_unary_op (IMAGPART_EXPR, $2, 0); } + ; + +cast_expr: + unary_expr + | '(' typename ')' cast_expr %prec UNARY + { tree type = groktypename ($2); + $$ = build_c_cast (type, $4); } + | '(' typename ')' '{' + { start_init (NULL_TREE, NULL, 0); + $2 = groktypename ($2); + really_start_incremental_init ($2); } + initlist_maybe_comma '}' %prec UNARY + { char *name; + tree result = pop_init_level (0); + tree type = $2; + finish_init (); + + if (pedantic) + pedwarn ("ANSI C forbids constructor expressions"); + if (TYPE_NAME (type) != 0) + { + if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) + name = IDENTIFIER_POINTER (TYPE_NAME (type)); + else + name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); + } + else + name = ""; + $$ = result; + if (TREE_CODE (type) == ARRAY_TYPE && TYPE_SIZE (type) == 0) + { + int failure = complete_array_type (type, $$, 1); + if (failure) + abort (); + } + } + ; + +expr_no_commas: + cast_expr + | expr_no_commas '+' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '-' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '*' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '/' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '%' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas LSHIFT expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas RSHIFT expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas ARITHCOMPARE expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas EQCOMPARE expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '&' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '|' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas '^' expr_no_commas + { $$ = parser_build_binary_op ($2, $1, $3); } + | expr_no_commas ANDAND expr_no_commas + { $$ = parser_build_binary_op (TRUTH_ANDIF_EXPR, $1, $3); } + | expr_no_commas OROR expr_no_commas + { $$ = parser_build_binary_op (TRUTH_ORIF_EXPR, $1, $3); } + | expr_no_commas '?' xexpr ':' expr_no_commas + { $$ = build_conditional_expr ($1, $3, $5); } + | expr_no_commas '=' expr_no_commas + { $$ = build_modify_expr ($1, NOP_EXPR, $3); + C_SET_EXP_ORIGINAL_CODE ($$, MODIFY_EXPR); } + | expr_no_commas ASSIGN expr_no_commas + { $$ = build_modify_expr ($1, $2, $3); + /* This inhibits warnings in truthvalue_conversion. */ + C_SET_EXP_ORIGINAL_CODE ($$, ERROR_MARK); } + ; + +primary: + IDENTIFIER + { + $$ = lastiddecl; + if (!$$ || $$ == error_mark_node) + { + if (yychar == YYEMPTY) + yychar = YYLEX; + if (yychar == '(') + { + { + /* Ordinary implicit function declaration. */ + $$ = implicitly_declare ($1); + assemble_external ($$); + TREE_USED ($$) = 1; + } + } + else if (current_function_decl == 0) + { + error ("`%s' undeclared here (not in a function)", + IDENTIFIER_POINTER ($1)); + $$ = error_mark_node; + } + else + { + { + if (IDENTIFIER_GLOBAL_VALUE ($1) != error_mark_node + || IDENTIFIER_ERROR_LOCUS ($1) != current_function_decl) + { + error ("`%s' undeclared (first use this function)", + IDENTIFIER_POINTER ($1)); + + if (! undeclared_variable_notice) + { + error ("(Each undeclared identifier is reported only once"); + error ("for each function it appears in.)"); + undeclared_variable_notice = 1; + } + } + $$ = error_mark_node; + /* Prevent repeated error messages. */ + IDENTIFIER_GLOBAL_VALUE ($1) = error_mark_node; + IDENTIFIER_ERROR_LOCUS ($1) = current_function_decl; + } + } + } + else if (TREE_TYPE ($$) == error_mark_node) + $$ = error_mark_node; + else if (C_DECL_ANTICIPATED ($$)) + { + /* The first time we see a build-in function used, + if it has not been declared. */ + C_DECL_ANTICIPATED ($$) = 0; + if (yychar == YYEMPTY) + yychar = YYLEX; + if (yychar == '(') + { + /* Omit the implicit declaration we + would ordinarily do, so we don't lose + the actual built in type. + But print a diagnostic for the mismatch. */ + if (TREE_CODE ($$) != FUNCTION_DECL) + error ("`%s' implicitly declared as function", + IDENTIFIER_POINTER (DECL_NAME ($$))); + else if ((TYPE_MODE (TREE_TYPE (TREE_TYPE ($$))) + != TYPE_MODE (integer_type_node)) + && (TREE_TYPE (TREE_TYPE ($$)) + != void_type_node)) + pedwarn ("type mismatch in implicit declaration for built-in function `%s'", + IDENTIFIER_POINTER (DECL_NAME ($$))); + /* If it really returns void, change that to int. */ + if (TREE_TYPE (TREE_TYPE ($$)) == void_type_node) + TREE_TYPE ($$) + = build_function_type (integer_type_node, + TYPE_ARG_TYPES (TREE_TYPE ($$))); + } + else + pedwarn ("built-in function `%s' used without declaration", + IDENTIFIER_POINTER (DECL_NAME ($$))); + + /* Do what we would ordinarily do when a fn is used. */ + assemble_external ($$); + TREE_USED ($$) = 1; + } + else + { + assemble_external ($$); + TREE_USED ($$) = 1; + } + + if (TREE_CODE ($$) == CONST_DECL) + { + $$ = 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; + } + } + | CONSTANT + | string + { $$ = combine_strings ($1); } + | '(' expr ')' + { char class = TREE_CODE_CLASS (TREE_CODE ($2)); + if (class == 'e' || class == '1' + || class == '2' || class == '<') + C_SET_EXP_ORIGINAL_CODE ($2, ERROR_MARK); + $$ = $2; } + | '(' error ')' + { $$ = error_mark_node; } + | '(' + { if (current_function_decl == 0) + { + error ("braced-group within expression allowed only inside a function"); + YYERROR; + } + /* We must force a BLOCK for this level + so that, if it is not expanded later, + there is a way to turn off the entire subtree of blocks + that are contained in it. */ + keep_next_level (); + push_iterator_stack (); + push_label_level (); + $<ttype>$ = expand_start_stmt_expr (); } + compstmt ')' + { tree rtl_exp; + if (pedantic) + pedwarn ("ANSI C forbids braced-groups within expressions"); + pop_iterator_stack (); + pop_label_level (); + 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 '(' exprlist ')' %prec '.' + { $$ = build_function_call ($1, $3); } + | primary '[' expr ']' %prec '.' + { $$ = build_array_ref ($1, $3); } + | primary '.' identifier + { + $$ = build_component_ref ($1, $3); + } + | primary POINTSAT identifier + { + tree expr = build_indirect_ref ($1, "->"); + + $$ = build_component_ref (expr, $3); + } + | primary PLUSPLUS + { $$ = build_unary_op (POSTINCREMENT_EXPR, $1, 0); } + | primary MINUSMINUS + { $$ = build_unary_op (POSTDECREMENT_EXPR, $1, 0); } + ; + +/* Produces a STRING_CST with perhaps more STRING_CSTs chained onto it. */ +string: + STRING + | string STRING + { $$ = chainon ($1, $2); } + ; + + +xdecls: + /* empty */ + | datadecls + | datadecls ELLIPSIS + /* ... is used here to indicate a varargs function. */ + { c_mark_varargs (); + if (pedantic) + pedwarn ("ANSI C does not permit use of `varargs.h'"); } + ; + +/* The following are analogous to lineno_decl, decls and decl + except that they do not allow nested functions. + They are used for old-style parm decls. */ +lineno_datadecl: + save_filename save_lineno datadecl + { } + ; + +datadecls: + lineno_datadecl + | errstmt + | datadecls lineno_datadecl + | lineno_datadecl errstmt + ; + +datadecl: + typed_declspecs setspecs initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs ';' + { shadow_tag_warned ($1, 1); + pedwarn ("empty declaration"); } + | declmods ';' + { pedwarn ("empty declaration"); } + ; + +/* This combination which saves a lineno before a decl + is the normal thing to use, rather than decl itself. + This is to avoid shift/reduce conflicts in contexts + where statement labels are allowed. */ +lineno_decl: + save_filename save_lineno decl + { } + ; + +decls: + lineno_decl + | errstmt + | decls lineno_decl + | lineno_decl errstmt + ; + +/* records the type and storage class specs to use for processing + the declarators that follow. + Maintains a stack of outer-level values of current_declspecs, + for the sake of parm declarations nested in function declarators. */ +setspecs: /* empty */ + { $$ = suspend_momentary (); + pending_xref_error (); + declspec_stack = tree_cons (prefix_attributes, + current_declspecs, + declspec_stack); + current_declspecs = $<ttype>0; + prefix_attributes = NULL_TREE; } + ; + +setattrs: /* empty */ + { prefix_attributes = chainon (prefix_attributes, $<ttype>0); } + ; + +decl: + typed_declspecs setspecs initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs setspecs nested_function + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_nested_function + { current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs ';' + { shadow_tag ($1); } + | declmods ';' + { pedwarn ("empty declaration"); } + ; + +/* 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. */ + +typed_declspecs: + typespec reserved_declspecs + { $$ = tree_cons (NULL_TREE, $1, $2); } + | declmods typespec reserved_declspecs + { $$ = chainon ($3, tree_cons (NULL_TREE, $2, $1)); } + ; + +reserved_declspecs: /* empty */ + { $$ = NULL_TREE; } + | reserved_declspecs typespecqual_reserved + { $$ = tree_cons (NULL_TREE, $2, $1); } + | reserved_declspecs SCSPEC + { if (extra_warnings) + warning ("`%s' is not at beginning of declaration", + IDENTIFIER_POINTER ($2)); + $$ = tree_cons (NULL_TREE, $2, $1); } + ; + +/* 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. */ + +declmods: + TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $1, NULL_TREE); + TREE_STATIC ($$) = 1; } + | SCSPEC + { $$ = tree_cons (NULL_TREE, $1, NULL_TREE); } + | declmods TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $2, $1); + TREE_STATIC ($$) = 1; } + | declmods SCSPEC + { if (extra_warnings && TREE_STATIC ($1)) + warning ("`%s' is not at beginning of declaration", + IDENTIFIER_POINTER ($2)); + $$ = tree_cons (NULL_TREE, $2, $1); + TREE_STATIC ($$) = TREE_STATIC ($1); } + ; + + +/* Used instead of declspecs where storage classes are not allowed + (that is, for typenames and structure components). + Don't accept a typedef-name if anything but a modifier precedes it. */ + +typed_typespecs: + typespec reserved_typespecquals + { $$ = tree_cons (NULL_TREE, $1, $2); } + | nonempty_type_quals typespec reserved_typespecquals + { $$ = chainon ($3, tree_cons (NULL_TREE, $2, $1)); } + ; + +reserved_typespecquals: /* empty */ + { $$ = NULL_TREE; } + | reserved_typespecquals typespecqual_reserved + { $$ = tree_cons (NULL_TREE, $2, $1); } + ; + +/* 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: TYPESPEC + | structsp + | TYPENAME + { /* For a typedef name, record the meaning, not the name. + In case of `foo foo, bar;'. */ + $$ = lookup_name ($1); } + | TYPEOF '(' expr ')' + { $$ = TREE_TYPE ($3); } + | TYPEOF '(' typename ')' + { $$ = groktypename ($3); } + ; + +/* A typespec that is a reserved word, or a type qualifier. */ + +typespecqual_reserved: TYPESPEC + | TYPE_QUAL + | structsp + ; + +initdecls: + initdcl + | initdecls ',' initdcl + ; + +notype_initdecls: + notype_initdcl + | notype_initdecls ',' initdcl + ; + +maybeasm: + /* empty */ + { $$ = NULL_TREE; } + | ASM_KEYWORD '(' string ')' + { if (TREE_CHAIN ($3)) $3 = combine_strings ($3); + $$ = $3; + } + ; + +initdcl: + declarator maybeasm maybe_attribute '=' + { $<ttype>$ = start_decl ($1, current_declspecs, 1, + $3, prefix_attributes); + start_init ($<ttype>$, $2, global_bindings_p ()); } + init +/* Note how the declaration of the variable is in effect while its init is parsed! */ + { finish_init (); + finish_decl ($<ttype>5, $6, $2); } + | declarator maybeasm maybe_attribute + { tree d = start_decl ($1, current_declspecs, 0, + $3, prefix_attributes); + finish_decl (d, NULL_TREE, $2); + } + ; + +notype_initdcl: + notype_declarator maybeasm maybe_attribute '=' + { $<ttype>$ = start_decl ($1, current_declspecs, 1, + $3, prefix_attributes); + start_init ($<ttype>$, $2, global_bindings_p ()); } + init +/* Note how the declaration of the variable is in effect while its init is parsed! */ + { finish_init (); + decl_attributes ($<ttype>5, $3, prefix_attributes); + finish_decl ($<ttype>5, $6, $2); } + | notype_declarator maybeasm maybe_attribute + { tree d = start_decl ($1, current_declspecs, 0, + $3, prefix_attributes); + finish_decl (d, NULL_TREE, $2); } + ; +/* 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 '(' 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 + ; + +/* Initializers. `init' is the entry point. */ + +init: + expr_no_commas + | '{' + { really_start_incremental_init (NULL_TREE); + /* Note that the call to clear_momentary + is in process_init_element. */ + push_momentary (); } + initlist_maybe_comma '}' + { $$ = pop_init_level (0); + if ($$ == error_mark_node + && ! (yychar == STRING || yychar == CONSTANT)) + pop_momentary (); + else + pop_momentary_nofree (); } + + | error + { $$ = error_mark_node; } + ; + +/* `initlist_maybe_comma' is the guts of an initializer in braces. */ +initlist_maybe_comma: + /* empty */ + { if (pedantic) + pedwarn ("ANSI C forbids empty initializer braces"); } + | initlist1 maybecomma + ; + +initlist1: + initelt + | initlist1 ',' initelt + ; + +/* `initelt' is a single element of an initializer. + It may use braces. */ +initelt: + expr_no_commas + { process_init_element ($1); } + | '{' + { push_init_level (0); } + initlist_maybe_comma '}' + { process_init_element (pop_init_level (0)); } + | error + /* 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 ELLIPSIS expr_no_commas ']' '=' + { set_init_index ($2, $4); } + initelt + | '[' expr_no_commas ']' '=' + { set_init_index ($2, NULL_TREE); } + initelt + | '[' expr_no_commas ']' + { set_init_index ($2, NULL_TREE); } + initelt + | identifier ':' + { set_init_label ($1); } + initelt + | '.' identifier '=' + { set_init_label ($2); } + initelt + ; + +nested_function: + declarator + { push_c_function_context (); + if (! start_function (current_declspecs, $1, + prefix_attributes, NULL_TREE, 1)) + { + pop_c_function_context (); + YYERROR1; + } + reinit_parse_for_function (); } + xdecls + { store_parm_decls (); } +/* This used to use compstmt_or_error. + That caused a bug with input `f(g) int g {}', + where the use of YYERROR1 above caused an error + which then was handled by compstmt_or_error. + There followed a repeated execution of that same rule, + which called YYERROR1 again, and so on. */ + compstmt + { finish_function (1); + pop_c_function_context (); } + ; + +notype_nested_function: + notype_declarator + { push_c_function_context (); + if (! start_function (current_declspecs, $1, + prefix_attributes, NULL_TREE, 1)) + { + pop_c_function_context (); + YYERROR1; + } + reinit_parse_for_function (); } + xdecls + { store_parm_decls (); } +/* This used to use compstmt_or_error. + That caused a bug with input `f(g) int g {}', + where the use of YYERROR1 above caused an error + which then was handled by compstmt_or_error. + There followed a repeated execution of that same rule, + which called YYERROR1 again, and so on. */ + compstmt + { finish_function (1); + pop_c_function_context (); } + ; + +/* Any kind of declarator (thus, all declarators allowed + after an explicit typespec). */ + +declarator: + after_type_declarator + | notype_declarator + ; + +/* A declarator that is allowed only after an explicit typespec. */ + +after_type_declarator: + '(' after_type_declarator ')' + { $$ = $2; } + | after_type_declarator '(' parmlist_or_identifiers %prec '.' + { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } +/* | after_type_declarator '(' error ')' %prec '.' + { $$ = build_nt (CALL_EXPR, $1, NULL_TREE, NULL_TREE); + poplevel (0, 0, 0); } */ + | after_type_declarator '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, $3); } + | after_type_declarator '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } + | '*' type_quals after_type_declarator %prec UNARY + { $$ = make_pointer_declarator ($2, $3); } + | attributes setattrs after_type_declarator + { $$ = $3; } + | TYPENAME + ; + +/* Kinds of declarator that can appear in a parameter list + in addition to notype_declarator. This is like after_type_declarator + but does not allow a typedef name in parentheses as an identifier + (because it would conflict with a function with that typedef as arg). */ + +parm_declarator: + parm_declarator '(' parmlist_or_identifiers %prec '.' + { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } +/* | parm_declarator '(' error ')' %prec '.' + { $$ = build_nt (CALL_EXPR, $1, NULL_TREE, NULL_TREE); + poplevel (0, 0, 0); } */ + | parm_declarator '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, $3); } + | parm_declarator '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } + | '*' type_quals parm_declarator %prec UNARY + { $$ = make_pointer_declarator ($2, $3); } + | attributes setattrs parm_declarator + { $$ = $3; } + | TYPENAME + ; + +/* A declarator allowed whether or not there has been + an explicit typespec. These cannot redeclare a typedef-name. */ + +notype_declarator: + notype_declarator '(' parmlist_or_identifiers %prec '.' + { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } +/* | notype_declarator '(' error ')' %prec '.' + { $$ = build_nt (CALL_EXPR, $1, NULL_TREE, NULL_TREE); + poplevel (0, 0, 0); } */ + | '(' notype_declarator ')' + { $$ = $2; } + | '*' type_quals notype_declarator %prec UNARY + { $$ = make_pointer_declarator ($2, $3); } + | notype_declarator '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, $3); } + | notype_declarator '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } + | attributes setattrs notype_declarator + { $$ = $3; } + | IDENTIFIER + ; + +structsp: + STRUCT identifier '{' + { $$ = start_struct (RECORD_TYPE, $2); + /* Start scope of tag before parsing components. */ + } + component_decl_list '}' maybe_attribute + { $$ = finish_struct ($<ttype>4, $5, $7); } + | STRUCT '{' component_decl_list '}' maybe_attribute + { $$ = finish_struct (start_struct (RECORD_TYPE, NULL_TREE), + $3, $5); + } + | STRUCT identifier + { $$ = xref_tag (RECORD_TYPE, $2); } + | UNION identifier '{' + { $$ = start_struct (UNION_TYPE, $2); } + component_decl_list '}' maybe_attribute + { $$ = finish_struct ($<ttype>4, $5, $7); } + | UNION '{' component_decl_list '}' maybe_attribute + { $$ = finish_struct (start_struct (UNION_TYPE, NULL_TREE), + $3, $5); + } + | UNION identifier + { $$ = xref_tag (UNION_TYPE, $2); } + | ENUM identifier '{' + { $<itype>3 = suspend_momentary (); + $$ = start_enum ($2); } + enumlist maybecomma_warn '}' maybe_attribute + { $$ = finish_enum ($<ttype>4, nreverse ($5), $8); + resume_momentary ($<itype>3); } + | ENUM '{' + { $<itype>2 = suspend_momentary (); + $$ = start_enum (NULL_TREE); } + enumlist maybecomma_warn '}' maybe_attribute + { $$ = finish_enum ($<ttype>3, nreverse ($4), $7); + resume_momentary ($<itype>2); } + | ENUM identifier + { $$ = xref_tag (ENUMERAL_TYPE, $2); } + ; + +maybecomma: + /* empty */ + | ',' + ; + +maybecomma_warn: + /* empty */ + | ',' + { if (pedantic) pedwarn ("comma at end of enumerator list"); } + ; + +component_decl_list: + component_decl_list2 + { $$ = $1; } + | component_decl_list2 component_decl + { $$ = chainon ($1, $2); + pedwarn ("no semicolon at end of struct or union"); } + ; + +component_decl_list2: /* empty */ + { $$ = NULL_TREE; } + | component_decl_list2 component_decl ';' + { $$ = chainon ($1, $2); } + | component_decl_list2 ';' + { if (pedantic) + pedwarn ("extra semicolon in struct or union specified"); } + ; + +/* There is a shift-reduce conflict here, because `components' may + start with a `typename'. It happens that shifting (the default resolution) + does the right thing, because it treats the `typename' as part of + a `typed_typespecs'. + + It is possible that this same technique would allow the distinction + between `notype_initdecls' and `initdecls' to be eliminated. + But I am being cautious and not trying it. */ + +component_decl: + typed_typespecs setspecs components + { $$ = $3; + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_typespecs + { if (pedantic) + pedwarn ("ANSI C forbids member declarations with no members"); + shadow_tag($1); + $$ = NULL_TREE; } + | nonempty_type_quals setspecs components + { $$ = $3; + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | nonempty_type_quals + { if (pedantic) + pedwarn ("ANSI C forbids member declarations with no members"); + shadow_tag($1); + $$ = NULL_TREE; } + | error + { $$ = NULL_TREE; } + ; + +components: + component_declarator + | components ',' component_declarator + { $$ = chainon ($1, $3); } + ; + +component_declarator: + save_filename save_lineno declarator maybe_attribute + { $$ = grokfield ($1, $2, $3, current_declspecs, NULL_TREE); + decl_attributes ($$, $4, prefix_attributes); } + | save_filename save_lineno + declarator ':' expr_no_commas maybe_attribute + { $$ = grokfield ($1, $2, $3, current_declspecs, $5); + decl_attributes ($$, $6, prefix_attributes); } + | save_filename save_lineno ':' expr_no_commas maybe_attribute + { $$ = grokfield ($1, $2, NULL_TREE, current_declspecs, $4); + decl_attributes ($$, $5, prefix_attributes); } + ; + +/* We chain the enumerators in reverse order. + They are put in forward order where enumlist is used. + (The order used to be significant, but no longer is so. + However, we still maintain the order, just to be clean.) */ + +enumlist: + enumerator + | enumlist ',' enumerator + { if ($1 == error_mark_node) + $$ = $1; + else + $$ = chainon ($3, $1); } + | error + { $$ = error_mark_node; } + ; + + +enumerator: + identifier + { $$ = build_enumerator ($1, NULL_TREE); } + | identifier '=' expr_no_commas + { $$ = build_enumerator ($1, $3); } + ; + +typename: + typed_typespecs absdcl + { $$ = build_tree_list ($1, $2); } + | nonempty_type_quals absdcl + { $$ = build_tree_list ($1, $2); } + ; + +absdcl: /* an absolute declarator */ + /* empty */ + { $$ = NULL_TREE; } + | absdcl1 + ; + +nonempty_type_quals: + TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $1, NULL_TREE); } + | nonempty_type_quals TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $2, $1); } + ; + +type_quals: + /* empty */ + { $$ = NULL_TREE; } + | type_quals TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $2, $1); } + ; + +absdcl1: /* a nonempty absolute declarator */ + '(' absdcl1 ')' + { $$ = $2; } + /* `(typedef)1' is `int'. */ + | '*' type_quals absdcl1 %prec UNARY + { $$ = make_pointer_declarator ($2, $3); } + | '*' type_quals %prec UNARY + { $$ = make_pointer_declarator ($2, NULL_TREE); } + | absdcl1 '(' parmlist %prec '.' + { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } + | absdcl1 '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, $3); } + | absdcl1 '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } + | '(' parmlist %prec '.' + { $$ = build_nt (CALL_EXPR, NULL_TREE, $2, NULL_TREE); } + | '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, NULL_TREE, $2); } + | '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, NULL_TREE, NULL_TREE); } + | attributes setattrs absdcl1 + { $$ = $3; } + ; + +/* at least one statement, the first of which parses without error. */ +/* stmts is used only after decls, so an invalid first statement + is actually regarded as an invalid decl and part of the decls. */ + +stmts: + lineno_stmt_or_labels + { + if (pedantic && $1) + pedwarn ("ANSI C forbids label at end of compound statement"); + } + ; + +lineno_stmt_or_labels: + lineno_stmt_or_label + | lineno_stmt_or_labels lineno_stmt_or_label + { $$ = $2; } + | lineno_stmt_or_labels errstmt + { $$ = 0; } + ; + +xstmts: + /* empty */ + | stmts + ; + +errstmt: error ';' + ; + +pushlevel: /* empty */ + { emit_line_note (input_filename, lineno); + pushlevel (0); + clear_last_expr (); + push_momentary (); + expand_start_bindings (0); + } + ; + +/* 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: '{' '}' + { $$ = convert (void_type_node, integer_zero_node); } + | '{' pushlevel maybe_label_decls decls xstmts '}' + { emit_line_note (input_filename, lineno); + expand_end_bindings (getdecls (), 1, 0); + $$ = poplevel (1, 1, 0); + if (yychar == CONSTANT || yychar == STRING) + pop_momentary_nofree (); + else + pop_momentary (); } + | '{' pushlevel maybe_label_decls error '}' + { emit_line_note (input_filename, lineno); + expand_end_bindings (getdecls (), kept_level_p (), 0); + $$ = poplevel (kept_level_p (), 0, 0); + if (yychar == CONSTANT || yychar == STRING) + pop_momentary_nofree (); + else + pop_momentary (); } + | '{' pushlevel maybe_label_decls stmts '}' + { emit_line_note (input_filename, lineno); + expand_end_bindings (getdecls (), kept_level_p (), 0); + $$ = poplevel (kept_level_p (), 0, 0); + if (yychar == CONSTANT || yychar == STRING) + pop_momentary_nofree (); + else + pop_momentary (); } + ; + +/* Value is number of statements counted as of the closeparen. */ +simple_if: + if_prefix lineno_labeled_stmt +/* Make sure expand_end_cond is run once + for each call to expand_start_cond. + Otherwise a crash is likely. */ + | if_prefix error + ; + +if_prefix: + IF '(' expr ')' + { emit_line_note ($<filename>-1, $<lineno>0); + expand_start_cond (truthvalue_conversion ($3), 0); + $<itype>$ = stmt_count; + if_stmt_file = $<filename>-1; + if_stmt_line = $<lineno>0; + position_after_white_space (); } + ; + +/* This is a subroutine of stmt. + It is used twice, once for valid DO statements + and once for catching errors in parsing the end test. */ +do_stmt_start: + DO + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + /* See comment in `while' alternative, above. */ + emit_nop (); + expand_start_loop_continue_elsewhere (1); + position_after_white_space (); } + lineno_labeled_stmt WHILE + { expand_loop_continue_here (); } + ; + +save_filename: + { $$ = input_filename; } + ; + +save_lineno: + { $$ = lineno; } + ; + +lineno_labeled_stmt: + save_filename save_lineno stmt + { } +/* | save_filename save_lineno error + { } +*/ + | save_filename save_lineno label lineno_labeled_stmt + { } + ; + +lineno_stmt_or_label: + save_filename save_lineno stmt_or_label + { $$ = $3; } + ; + +stmt_or_label: + stmt + { $$ = 0; } + | label + { $$ = 1; } + ; + +/* Parse a single real statement, not including any labels. */ +stmt: + compstmt + { stmt_count++; } + | all_iter_stmt + | expr ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); +/* It appears that this should not be done--that a non-lvalue array + shouldn't get an error if the value isn't used. + Section 3.2.2.1 says that an array lvalue gets converted to a pointer + if it appears as a top-level expression, + but says nothing about non-lvalue arrays. */ +#if 0 + /* Call default_conversion to get an error + on referring to a register array if pedantic. */ + if (TREE_CODE (TREE_TYPE ($1)) == ARRAY_TYPE + || TREE_CODE (TREE_TYPE ($1)) == FUNCTION_TYPE) + $1 = default_conversion ($1); +#endif + iterator_expand ($1); + clear_momentary (); } + | simple_if ELSE + { expand_start_else (); + $<itype>1 = stmt_count; + position_after_white_space (); } + lineno_labeled_stmt + { expand_end_cond (); + if (extra_warnings && stmt_count == $<itype>1) + warning ("empty body in an else-statement"); } + | simple_if %prec IF + { expand_end_cond (); + /* This warning is here instead of in simple_if, because we + do not want a warning if an empty if is followed by an + else statement. Increment stmt_count so we don't + give a second error if this is a nested `if'. */ + if (extra_warnings && stmt_count++ == $<itype>1) + warning_with_file_and_line (if_stmt_file, if_stmt_line, + "empty body in an if-statement"); } +/* Make sure expand_end_cond is run once + for each call to expand_start_cond. + Otherwise a crash is likely. */ + | simple_if ELSE error + { expand_end_cond (); } + | WHILE + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + /* The emit_nop used to come before emit_line_note, + but that made the nop seem like part of the preceding line. + And that was confusing when the preceding line was + inside of an if statement and was not really executed. + I think it ought to work to put the nop after the line number. + We will see. --rms, July 15, 1991. */ + emit_nop (); } + '(' expr ')' + { /* Don't start the loop till we have succeeded + in parsing the end test. This is to make sure + that we end every loop we start. */ + expand_start_loop (1); + emit_line_note (input_filename, lineno); + expand_exit_loop_if_false (NULL_PTR, + truthvalue_conversion ($4)); + position_after_white_space (); } + lineno_labeled_stmt + { expand_end_loop (); } + | do_stmt_start + '(' expr ')' ';' + { emit_line_note (input_filename, lineno); + expand_exit_loop_if_false (NULL_PTR, + truthvalue_conversion ($3)); + expand_end_loop (); + clear_momentary (); } +/* This rule is needed to make sure we end every loop we start. */ + | do_stmt_start error + { expand_end_loop (); + clear_momentary (); } + | FOR + '(' xexpr ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + /* See comment in `while' alternative, above. */ + emit_nop (); + if ($3) c_expand_expr_stmt ($3); + /* Next step is to call expand_start_loop_continue_elsewhere, + but wait till after we parse the entire for (...). + Otherwise, invalid input might cause us to call that + fn without calling expand_end_loop. */ + } + xexpr ';' + /* Can't emit now; wait till after expand_start_loop... */ + { $<lineno>7 = lineno; + $<filename>$ = input_filename; } + xexpr ')' + { + /* Start the loop. Doing this after parsing + all the expressions ensures we will end the loop. */ + expand_start_loop_continue_elsewhere (1); + /* Emit the end-test, with a line number. */ + emit_line_note ($<filename>8, $<lineno>7); + if ($6) + expand_exit_loop_if_false (NULL_PTR, + truthvalue_conversion ($6)); + /* Don't let the tree nodes for $9 be discarded by + clear_momentary during the parsing of the next stmt. */ + push_momentary (); + $<lineno>7 = lineno; + $<filename>8 = input_filename; + position_after_white_space (); } + lineno_labeled_stmt + { /* Emit the increment expression, with a line number. */ + emit_line_note ($<filename>8, $<lineno>7); + expand_loop_continue_here (); + if ($9) + c_expand_expr_stmt ($9); + if (yychar == CONSTANT || yychar == STRING) + pop_momentary_nofree (); + else + pop_momentary (); + expand_end_loop (); } + | SWITCH '(' expr ')' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + c_expand_start_case ($3); + /* Don't let the tree nodes for $3 be discarded by + clear_momentary during the parsing of the next stmt. */ + push_momentary (); + position_after_white_space (); } + lineno_labeled_stmt + { expand_end_case ($3); + if (yychar == CONSTANT || yychar == STRING) + pop_momentary_nofree (); + else + pop_momentary (); } + | BREAK ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + if ( ! expand_exit_something ()) + error ("break statement not within loop or switch"); } + | CONTINUE ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + if (! expand_continue_loop (NULL_PTR)) + error ("continue statement not within a loop"); } + | RETURN ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + c_expand_return (NULL_TREE); } + | RETURN expr ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + c_expand_return ($2); } + | ASM_KEYWORD maybe_type_qual '(' expr ')' ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + STRIP_NOPS ($4); + if ((TREE_CODE ($4) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND ($4, 0)) == STRING_CST) + || TREE_CODE ($4) == STRING_CST) + expand_asm ($4); + else + error ("argument of `asm' is not a constant string"); } + /* This is the case with just output operands. */ + | ASM_KEYWORD maybe_type_qual '(' expr ':' asm_operands ')' ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + c_expand_asm_operands ($4, $6, NULL_TREE, NULL_TREE, + $2 == ridpointers[(int)RID_VOLATILE], + input_filename, lineno); } + /* This is the case with input operands as well. */ + | ASM_KEYWORD maybe_type_qual '(' expr ':' asm_operands ':' asm_operands ')' ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + c_expand_asm_operands ($4, $6, $8, NULL_TREE, + $2 == ridpointers[(int)RID_VOLATILE], + input_filename, lineno); } + /* This is the case with clobbered registers as well. */ + | ASM_KEYWORD maybe_type_qual '(' expr ':' asm_operands ':' + asm_operands ':' asm_clobbers ')' ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + c_expand_asm_operands ($4, $6, $8, $10, + $2 == ridpointers[(int)RID_VOLATILE], + input_filename, lineno); } + | GOTO identifier ';' + { tree decl; + stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + decl = lookup_label ($2); + if (decl != 0) + { + TREE_USED (decl) = 1; + expand_goto (decl); + } + } + | GOTO '*' expr ';' + { stmt_count++; + emit_line_note ($<filename>-1, $<lineno>0); + expand_computed_goto (convert (ptr_type_node, $3)); } + | ';' + ; + +all_iter_stmt: + all_iter_stmt_simple +/* | all_iter_stmt_with_decl */ + ; + +all_iter_stmt_simple: + FOR '(' primary ')' + { + /* The value returned by this action is */ + /* 1 if everything is OK */ + /* 0 in case of error or already bound iterator */ + + $<itype>$ = 0; + if (TREE_CODE ($3) != VAR_DECL) + error ("invalid `for (ITERATOR)' syntax"); + else if (! ITERATOR_P ($3)) + error ("`%s' is not an iterator", + IDENTIFIER_POINTER (DECL_NAME ($3))); + else if (ITERATOR_BOUND_P ($3)) + error ("`for (%s)' inside expansion of same iterator", + IDENTIFIER_POINTER (DECL_NAME ($3))); + else + { + $<itype>$ = 1; + iterator_for_loop_start ($3); + } + } + lineno_labeled_stmt + { + if ($<itype>5) + iterator_for_loop_end ($3); + } + +/* This really should allow any kind of declaration, + for generality. Fix it before turning it back on. + +all_iter_stmt_with_decl: + FOR '(' ITERATOR pushlevel setspecs iterator_spec ')' + { +*/ /* The value returned by this action is */ + /* 1 if everything is OK */ + /* 0 in case of error or already bound iterator */ +/* + iterator_for_loop_start ($6); + } + lineno_labeled_stmt + { + iterator_for_loop_end ($6); + emit_line_note (input_filename, lineno); + expand_end_bindings (getdecls (), 1, 0); + $<ttype>$ = poplevel (1, 1, 0); + if (yychar == CONSTANT || yychar == STRING) + pop_momentary_nofree (); + else + pop_momentary (); + } +*/ + +/* Any kind of label, including jump labels and case labels. + ANSI C accepts labels only before statements, but we allow them + also at the end of a compound statement. */ + +label: CASE expr_no_commas ':' + { register tree value = check_case_value ($2); + register tree label + = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); + + stmt_count++; + + if (value != error_mark_node) + { + tree duplicate; + int success = pushcase (value, convert_and_check, + label, &duplicate); + if (success == 1) + error ("case label not within a switch statement"); + else if (success == 2) + { + error ("duplicate case value"); + error_with_decl (duplicate, "this is the first entry for that value"); + } + else if (success == 3) + warning ("case value out of range"); + else if (success == 5) + error ("case label within scope of cleanup or variable array"); + } + position_after_white_space (); } + | CASE expr_no_commas ELLIPSIS expr_no_commas ':' + { register tree value1 = check_case_value ($2); + register tree value2 = check_case_value ($4); + register tree label + = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); + + stmt_count++; + + 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 case value"); + error_with_decl (duplicate, "this is the first entry for that value"); + } + else if (success == 3) + warning ("case value out of range"); + else if (success == 4) + warning ("empty case range"); + else if (success == 5) + error ("case label within scope of cleanup or variable array"); + } + position_after_white_space (); } + | DEFAULT ':' + { + tree duplicate; + register tree label + = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); + int success = pushcase (NULL_TREE, 0, label, &duplicate); + stmt_count++; + 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"); + } + position_after_white_space (); } + | identifier ':' + { tree label = define_label (input_filename, lineno, $1); + stmt_count++; + emit_nop (); + if (label) + expand_label (label); + position_after_white_space (); } + ; + +/* 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 + ; + +/* 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 ($1, $3); } + ; + +asm_operand: + STRING '(' expr ')' + { $$ = build_tree_list ($1, $3); } + ; + +asm_clobbers: + string + { $$ = tree_cons (NULL_TREE, combine_strings ($1), NULL_TREE); } + | asm_clobbers ',' string + { $$ = tree_cons (NULL_TREE, combine_strings ($3), $1); } + ; + +/* This is what appears inside the parens in a function declarator. + Its value is a list of ..._TYPE nodes. */ +parmlist: + { pushlevel (0); + clear_parm_order (); + declare_parm_level (0); } + parmlist_1 + { $$ = $2; + parmlist_tags_warning (); + poplevel (0, 0, 0); } + ; + +parmlist_1: + parmlist_2 ')' + | parms ';' + { tree parm; + if (pedantic) + pedwarn ("ANSI C forbids forward parameter declarations"); + /* Mark the forward decls as such. */ + for (parm = getdecls (); parm; parm = TREE_CHAIN (parm)) + TREE_ASM_WRITTEN (parm) = 1; + clear_parm_order (); } + parmlist_1 + { $$ = $4; } + | error ')' + { $$ = tree_cons (NULL_TREE, NULL_TREE, NULL_TREE); } + ; + +/* This is what appears inside the parens in a function declarator. + Is value is represented in the format that grokdeclarator expects. */ +parmlist_2: /* empty */ + { $$ = get_parm_info (0); } + | ELLIPSIS + { $$ = get_parm_info (0); + /* Gcc used to allow this as an extension. However, it does + not work for all targets, and thus has been disabled. + Also, since func (...) and func () are indistinguishable, + it caused problems with the code in expand_builtin which + tries to verify that BUILT_IN_NEXT_ARG is being used + correctly. */ + error ("ANSI C requires a named argument before `...'"); + } + | parms + { $$ = get_parm_info (1); } + | parms ',' ELLIPSIS + { $$ = get_parm_info (0); } + ; + +parms: + parm + { push_parm_decl ($1); } + | parms ',' parm + { push_parm_decl ($3); } + ; + +/* A single parameter declaration or parameter type name, + as found in a parmlist. */ +parm: + typed_declspecs setspecs parm_declarator maybe_attribute + { $$ = build_tree_list (build_tree_list (current_declspecs, + $3), + build_tree_list (prefix_attributes, + $4)); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs setspecs notype_declarator maybe_attribute + { $$ = build_tree_list (build_tree_list (current_declspecs, + $3), + build_tree_list (prefix_attributes, + $4)); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs setspecs absdcl maybe_attribute + { $$ = build_tree_list (build_tree_list (current_declspecs, + $3), + build_tree_list (prefix_attributes, + $4)); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_declarator maybe_attribute + { $$ = build_tree_list (build_tree_list (current_declspecs, + $3), + build_tree_list (prefix_attributes, + $4)); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + + | declmods setspecs absdcl maybe_attribute + { $$ = build_tree_list (build_tree_list (current_declspecs, + $3), + build_tree_list (prefix_attributes, + $4)); + current_declspecs = TREE_VALUE (declspec_stack); + prefix_attributes = TREE_PURPOSE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + ; + +/* This is used in a function definition + where either a parmlist or an identifier list is ok. + Its value is a list of ..._TYPE nodes or a list of identifiers. */ +parmlist_or_identifiers: + { pushlevel (0); + clear_parm_order (); + declare_parm_level (1); } + parmlist_or_identifiers_1 + { $$ = $2; + parmlist_tags_warning (); + poplevel (0, 0, 0); } + ; + +parmlist_or_identifiers_1: + parmlist_1 + | identifiers ')' + { tree t; + for (t = $1; t; t = TREE_CHAIN (t)) + if (TREE_VALUE (t) == NULL_TREE) + error ("`...' in old-style identifier list"); + $$ = tree_cons (NULL_TREE, NULL_TREE, $1); } + ; + +/* A nonempty list of identifiers. */ +identifiers: + IDENTIFIER + { $$ = build_tree_list (NULL_TREE, $1); } + | identifiers ',' IDENTIFIER + { $$ = chainon ($1, build_tree_list (NULL_TREE, $3)); } + ; + +/* 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)); } + ; + +%% diff --git a/gnu/usr.bin/gcc/cpp/cexp.y b/gnu/usr.bin/gcc/cpp/cexp.y new file mode 100644 index 00000000000..d83de803364 --- /dev/null +++ b/gnu/usr.bin/gcc/cpp/cexp.y @@ -0,0 +1,1047 @@ +/* Parse C expressions for CCCP. + Copyright (C) 1987, 1992, 1994, 1995 Free Software Foundation. + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! + + Adapted from expread.y of GDB by Paul Rubin, July 1986. */ + +/* Parse a C expression from text in a string */ + +%{ +#include "config.h" +#include <setjmp.h> +/* #define YYDEBUG 1 */ + +#ifdef MULTIBYTE_CHARS +#include <stdlib.h> +#include <locale.h> +#endif + +#include <stdio.h> + +typedef unsigned char U_CHAR; + +/* This is used for communicating lists of keywords with cccp.c. */ +struct arglist { + struct arglist *next; + U_CHAR *name; + int length; + int argno; +}; + +/* Define a generic NULL if one hasn't already been defined. */ + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef GENERIC_PTR +#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__) +#define GENERIC_PTR void * +#else +#define GENERIC_PTR char * +#endif +#endif + +/* Find the largest host integer type and set its size and type. */ + +#ifndef HOST_BITS_PER_WIDE_INT + +#if HOST_BITS_PER_LONG > HOST_BITS_PER_INT +#define HOST_BITS_PER_WIDE_INT HOST_BITS_PER_LONG +#define HOST_WIDE_INT long +#else +#define HOST_BITS_PER_WIDE_INT HOST_BITS_PER_INT +#define HOST_WIDE_INT int +#endif + +#endif + +#ifndef NULL_PTR +#define NULL_PTR ((GENERIC_PTR)0) +#endif + +int yylex (); +void yyerror (); +HOST_WIDE_INT expression_value; + +static jmp_buf parse_return_error; + +/* Nonzero means count most punctuation as part of a name. */ +static int keyword_parsing = 0; + +/* Nonzero means do not evaluate this expression. + This is a count, since unevaluated expressions can nest. */ +static int skip_evaluation; + +/* some external tables of character types */ +extern unsigned char is_idstart[], is_idchar[], is_hor_space[]; + +extern char *xmalloc (); + +/* Flag for -pedantic. */ +extern int pedantic; + +/* Flag for -traditional. */ +extern int traditional; + +#ifndef CHAR_TYPE_SIZE +#define CHAR_TYPE_SIZE BITS_PER_UNIT +#endif + +#ifndef INT_TYPE_SIZE +#define INT_TYPE_SIZE BITS_PER_WORD +#endif + +#ifndef LONG_TYPE_SIZE +#define LONG_TYPE_SIZE BITS_PER_WORD +#endif + +#ifndef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE INT_TYPE_SIZE +#endif + +#ifndef MAX_CHAR_TYPE_SIZE +#define MAX_CHAR_TYPE_SIZE CHAR_TYPE_SIZE +#endif + +#ifndef MAX_INT_TYPE_SIZE +#define MAX_INT_TYPE_SIZE INT_TYPE_SIZE +#endif + +#ifndef MAX_LONG_TYPE_SIZE +#define MAX_LONG_TYPE_SIZE LONG_TYPE_SIZE +#endif + +#ifndef MAX_WCHAR_TYPE_SIZE +#define MAX_WCHAR_TYPE_SIZE WCHAR_TYPE_SIZE +#endif + +/* Yield nonzero if adding two numbers with A's and B's signs can yield a + number with SUM's sign, where A, B, and SUM are all C integers. */ +#define possible_sum_sign(a, b, sum) ((((a) ^ (b)) | ~ ((a) ^ (sum))) < 0) + +static void integer_overflow (); +static long left_shift (); +static long right_shift (); +%} + +%union { + struct constant {long value; int unsignedp;} integer; + struct name {U_CHAR *address; int length;} name; + struct arglist *keywords; +} + +%type <integer> exp exp1 start +%type <keywords> keywords +%token <integer> INT CHAR +%token <name> NAME +%token <integer> ERROR + +%right '?' ':' +%left ',' +%left OR +%left AND +%left '|' +%left '^' +%left '&' +%left EQUAL NOTEQUAL +%left '<' '>' LEQ GEQ +%left LSH RSH +%left '+' '-' +%left '*' '/' '%' +%right UNARY + +/* %expect 40 */ + +%% + +start : exp1 + { expression_value = $1.value; } + ; + +/* Expressions, including the comma operator. */ +exp1 : exp + | exp1 ',' exp + { if (pedantic) + pedwarn ("comma operator in operand of `#if'"); + $$ = $3; } + ; + +/* Expressions, not including the comma operator. */ +exp : '-' exp %prec UNARY + { $$.value = - $2.value; + if (($$.value & $2.value) < 0 && ! $2.unsignedp) + integer_overflow (); + $$.unsignedp = $2.unsignedp; } + | '!' exp %prec UNARY + { $$.value = ! $2.value; + $$.unsignedp = 0; } + | '+' exp %prec UNARY + { $$ = $2; } + | '~' exp %prec UNARY + { $$.value = ~ $2.value; + $$.unsignedp = $2.unsignedp; } + | '#' NAME + { $$.value = check_assertion ($2.address, $2.length, + 0, NULL_PTR); + $$.unsignedp = 0; } + | '#' NAME + { keyword_parsing = 1; } + '(' keywords ')' + { $$.value = check_assertion ($2.address, $2.length, + 1, $5); + keyword_parsing = 0; + $$.unsignedp = 0; } + | '(' exp1 ')' + { $$ = $2; } + ; + +/* Binary operators in order of decreasing precedence. */ +exp : exp '*' exp + { $$.unsignedp = $1.unsignedp || $3.unsignedp; + if ($$.unsignedp) + $$.value = (unsigned long) $1.value * $3.value; + else + { + $$.value = $1.value * $3.value; + if ($1.value + && ($$.value / $1.value != $3.value + || ($$.value & $1.value & $3.value) < 0)) + integer_overflow (); + } } + | exp '/' exp + { if ($3.value == 0) + { + if (!skip_evaluation) + error ("division by zero in #if"); + $3.value = 1; + } + $$.unsignedp = $1.unsignedp || $3.unsignedp; + if ($$.unsignedp) + $$.value = (unsigned long) $1.value / $3.value; + else + { + $$.value = $1.value / $3.value; + if (($$.value & $1.value & $3.value) < 0) + integer_overflow (); + } } + | exp '%' exp + { if ($3.value == 0) + { + if (!skip_evaluation) + error ("division by zero in #if"); + $3.value = 1; + } + $$.unsignedp = $1.unsignedp || $3.unsignedp; + if ($$.unsignedp) + $$.value = (unsigned long) $1.value % $3.value; + else + $$.value = $1.value % $3.value; } + | exp '+' exp + { $$.value = $1.value + $3.value; + $$.unsignedp = $1.unsignedp || $3.unsignedp; + if (! $$.unsignedp + && ! possible_sum_sign ($1.value, $3.value, + $$.value)) + integer_overflow (); } + | exp '-' exp + { $$.value = $1.value - $3.value; + $$.unsignedp = $1.unsignedp || $3.unsignedp; + if (! $$.unsignedp + && ! possible_sum_sign ($$.value, $3.value, + $1.value)) + integer_overflow (); } + | exp LSH exp + { $$.unsignedp = $1.unsignedp; + if ($3.value < 0 && ! $3.unsignedp) + $$.value = right_shift (&$1, -$3.value); + else + $$.value = left_shift (&$1, $3.value); } + | exp RSH exp + { $$.unsignedp = $1.unsignedp; + if ($3.value < 0 && ! $3.unsignedp) + $$.value = left_shift (&$1, -$3.value); + else + $$.value = right_shift (&$1, $3.value); } + | exp EQUAL exp + { $$.value = ($1.value == $3.value); + $$.unsignedp = 0; } + | exp NOTEQUAL exp + { $$.value = ($1.value != $3.value); + $$.unsignedp = 0; } + | exp LEQ exp + { $$.unsignedp = 0; + if ($1.unsignedp || $3.unsignedp) + $$.value = (unsigned long) $1.value <= $3.value; + else + $$.value = $1.value <= $3.value; } + | exp GEQ exp + { $$.unsignedp = 0; + if ($1.unsignedp || $3.unsignedp) + $$.value = (unsigned long) $1.value >= $3.value; + else + $$.value = $1.value >= $3.value; } + | exp '<' exp + { $$.unsignedp = 0; + if ($1.unsignedp || $3.unsignedp) + $$.value = (unsigned long) $1.value < $3.value; + else + $$.value = $1.value < $3.value; } + | exp '>' exp + { $$.unsignedp = 0; + if ($1.unsignedp || $3.unsignedp) + $$.value = (unsigned long) $1.value > $3.value; + else + $$.value = $1.value > $3.value; } + | exp '&' exp + { $$.value = $1.value & $3.value; + $$.unsignedp = $1.unsignedp || $3.unsignedp; } + | exp '^' exp + { $$.value = $1.value ^ $3.value; + $$.unsignedp = $1.unsignedp || $3.unsignedp; } + | exp '|' exp + { $$.value = $1.value | $3.value; + $$.unsignedp = $1.unsignedp || $3.unsignedp; } + | exp AND + { skip_evaluation += !$1.value; } + exp + { skip_evaluation -= !$1.value; + $$.value = ($1.value && $4.value); + $$.unsignedp = 0; } + | exp OR + { skip_evaluation += !!$1.value; } + exp + { skip_evaluation -= !!$1.value; + $$.value = ($1.value || $4.value); + $$.unsignedp = 0; } + | exp '?' + { skip_evaluation += !$1.value; } + exp ':' + { skip_evaluation += !!$1.value - !$1.value; } + exp + { skip_evaluation -= !!$1.value; + $$.value = $1.value ? $4.value : $7.value; + $$.unsignedp = $4.unsignedp || $7.unsignedp; } + | INT + { $$ = yylval.integer; } + | CHAR + { $$ = yylval.integer; } + | NAME + { $$.value = 0; + $$.unsignedp = 0; } + ; + +keywords : + { $$ = 0; } + | '(' keywords ')' keywords + { struct arglist *temp; + $$ = (struct arglist *) xmalloc (sizeof (struct arglist)); + $$->next = $2; + $$->name = (U_CHAR *) "("; + $$->length = 1; + temp = $$; + while (temp != 0 && temp->next != 0) + temp = temp->next; + temp->next = (struct arglist *) xmalloc (sizeof (struct arglist)); + temp->next->next = $4; + temp->next->name = (U_CHAR *) ")"; + temp->next->length = 1; } + | NAME keywords + { $$ = (struct arglist *) xmalloc (sizeof (struct arglist)); + $$->name = $1.address; + $$->length = $1.length; + $$->next = $2; } + ; +%% + +/* During parsing of a C expression, the pointer to the next character + is in this variable. */ + +static char *lexptr; + +/* Take care of parsing a number (anything that starts with a digit). + Set yylval and return the token type; update lexptr. + LEN is the number of characters in it. */ + +/* maybe needs to actually deal with floating point numbers */ + +int +parse_number (olen) + int olen; +{ + register char *p = lexptr; + register int c; + register unsigned long n = 0, nd, ULONG_MAX_over_base; + register int base = 10; + register int len = olen; + register int overflow = 0; + register int digit, largest_digit = 0; + int spec_long = 0; + + for (c = 0; c < len; c++) + if (p[c] == '.') { + /* It's a float since it contains a point. */ + yyerror ("floating point numbers not allowed in #if expressions"); + return ERROR; + } + + yylval.integer.unsignedp = 0; + + if (len >= 3 && (!strncmp (p, "0x", 2) || !strncmp (p, "0X", 2))) { + p += 2; + base = 16; + len -= 2; + } + else if (*p == '0') + base = 8; + + ULONG_MAX_over_base = (unsigned long) -1 / base; + + for (; len > 0; len--) { + c = *p++; + + if (c >= '0' && c <= '9') + digit = c - '0'; + else if (base == 16 && c >= 'a' && c <= 'f') + digit = c - 'a' + 10; + else if (base == 16 && c >= 'A' && c <= 'F') + digit = c - 'A' + 10; + else { + /* `l' means long, and `u' means unsigned. */ + while (1) { + if (c == 'l' || c == 'L') + { + if (spec_long) + yyerror ("two `l's in integer constant"); + spec_long = 1; + } + else if (c == 'u' || c == 'U') + { + if (yylval.integer.unsignedp) + yyerror ("two `u's in integer constant"); + yylval.integer.unsignedp = 1; + } + else + break; + + if (--len == 0) + break; + c = *p++; + } + /* Don't look for any more digits after the suffixes. */ + break; + } + if (largest_digit < digit) + largest_digit = digit; + nd = n * base + digit; + overflow |= ULONG_MAX_over_base < n | nd < n; + n = nd; + } + + if (len != 0) { + yyerror ("Invalid number in #if expression"); + return ERROR; + } + + if (base <= largest_digit) + warning ("integer constant contains digits beyond the radix"); + + if (overflow) + warning ("integer constant out of range"); + + /* If too big to be signed, consider it unsigned. */ + if ((long) n < 0 && ! yylval.integer.unsignedp) + { + if (base == 10) + warning ("integer constant is so large that it is unsigned"); + yylval.integer.unsignedp = 1; + } + + lexptr = p; + yylval.integer.value = n; + return INT; +} + +struct token { + char *operator; + int token; +}; + +static struct token tokentab2[] = { + {"&&", AND}, + {"||", OR}, + {"<<", LSH}, + {">>", RSH}, + {"==", EQUAL}, + {"!=", NOTEQUAL}, + {"<=", LEQ}, + {">=", GEQ}, + {"++", ERROR}, + {"--", ERROR}, + {NULL, ERROR} +}; + +/* Read one token, getting characters through lexptr. */ + +int +yylex () +{ + register int c; + register int namelen; + register unsigned char *tokstart; + register struct token *toktab; + int wide_flag; + + retry: + + tokstart = (unsigned char *) lexptr; + c = *tokstart; + /* See if it is a special token of length 2. */ + if (! keyword_parsing) + for (toktab = tokentab2; toktab->operator != NULL; toktab++) + if (c == *toktab->operator && tokstart[1] == toktab->operator[1]) { + lexptr += 2; + if (toktab->token == ERROR) + { + char *buf = (char *) alloca (40); + sprintf (buf, "`%s' not allowed in operand of `#if'", toktab->operator); + yyerror (buf); + } + return toktab->token; + } + + switch (c) { + case 0: + return 0; + + case ' ': + case '\t': + case '\r': + case '\n': + lexptr++; + goto retry; + + case 'L': + /* Capital L may start a wide-string or wide-character constant. */ + if (lexptr[1] == '\'') + { + lexptr++; + wide_flag = 1; + goto char_constant; + } + if (lexptr[1] == '"') + { + lexptr++; + wide_flag = 1; + goto string_constant; + } + break; + + case '\'': + wide_flag = 0; + char_constant: + lexptr++; + if (keyword_parsing) { + char *start_ptr = lexptr - 1; + while (1) { + c = *lexptr++; + if (c == '\\') + c = parse_escape (&lexptr); + else if (c == '\'') + break; + } + yylval.name.address = tokstart; + yylval.name.length = lexptr - start_ptr; + return NAME; + } + + /* This code for reading a character constant + handles multicharacter constants and wide characters. + It is mostly copied from c-lex.c. */ + { + register int result = 0; + register num_chars = 0; + unsigned width = MAX_CHAR_TYPE_SIZE; + int max_chars; + char *token_buffer; + + if (wide_flag) + { + width = MAX_WCHAR_TYPE_SIZE; +#ifdef MULTIBYTE_CHARS + max_chars = MB_CUR_MAX; +#else + max_chars = 1; +#endif + } + else + max_chars = MAX_LONG_TYPE_SIZE / width; + + token_buffer = (char *) alloca (max_chars + 1); + + while (1) + { + c = *lexptr++; + + if (c == '\'' || c == EOF) + break; + + if (c == '\\') + { + c = parse_escape (&lexptr); + if (width < HOST_BITS_PER_INT + && (unsigned) c >= (1 << width)) + pedwarn ("escape sequence out of range for character"); + } + + num_chars++; + + /* Merge character into result; ignore excess chars. */ + if (num_chars < max_chars + 1) + { + if (width < HOST_BITS_PER_INT) + result = (result << width) | (c & ((1 << width) - 1)); + else + result = c; + token_buffer[num_chars - 1] = c; + } + } + + token_buffer[num_chars] = 0; + + if (c != '\'') + error ("malformatted character constant"); + else if (num_chars == 0) + error ("empty character constant"); + else if (num_chars > max_chars) + { + num_chars = max_chars; + error ("character constant too long"); + } + else if (num_chars != 1 && ! traditional) + warning ("multi-character character constant"); + + /* If char type is signed, sign-extend the constant. */ + if (! wide_flag) + { + int num_bits = num_chars * width; + + if (lookup ("__CHAR_UNSIGNED__", sizeof ("__CHAR_UNSIGNED__")-1, -1) + || ((result >> (num_bits - 1)) & 1) == 0) + yylval.integer.value + = result & ((unsigned long) ~0 >> (HOST_BITS_PER_LONG - num_bits)); + else + yylval.integer.value + = result | ~((unsigned long) ~0 >> (HOST_BITS_PER_LONG - num_bits)); + } + else + { +#ifdef MULTIBYTE_CHARS + /* Set the initial shift state and convert the next sequence. */ + result = 0; + /* In all locales L'\0' is zero and mbtowc will return zero, + so don't use it. */ + if (num_chars > 1 + || (num_chars == 1 && token_buffer[0] != '\0')) + { + wchar_t wc; + (void) mbtowc (NULL_PTR, NULL_PTR, 0); + if (mbtowc (& wc, token_buffer, num_chars) == num_chars) + result = wc; + else + warning ("Ignoring invalid multibyte character"); + } +#endif + yylval.integer.value = result; + } + } + + /* This is always a signed type. */ + yylval.integer.unsignedp = 0; + + return CHAR; + + /* some of these chars are invalid in constant expressions; + maybe do something about them later */ + case '/': + case '+': + case '-': + case '*': + case '%': + case '|': + case '&': + case '^': + case '~': + case '!': + case '@': + case '<': + case '>': + case '[': + case ']': + case '.': + case '?': + case ':': + case '=': + case '{': + case '}': + case ',': + case '#': + if (keyword_parsing) + break; + case '(': + case ')': + lexptr++; + return c; + + case '"': + string_constant: + if (keyword_parsing) { + char *start_ptr = lexptr; + lexptr++; + while (1) { + c = *lexptr++; + if (c == '\\') + c = parse_escape (&lexptr); + else if (c == '"') + break; + } + yylval.name.address = tokstart; + yylval.name.length = lexptr - start_ptr; + return NAME; + } + yyerror ("string constants not allowed in #if expressions"); + return ERROR; + } + + if (c >= '0' && c <= '9' && !keyword_parsing) { + /* It's a number */ + for (namelen = 0; + c = tokstart[namelen], is_idchar[c] || c == '.'; + namelen++) + ; + return parse_number (namelen); + } + + /* It is a name. See how long it is. */ + + if (keyword_parsing) { + for (namelen = 0;; namelen++) { + if (is_hor_space[tokstart[namelen]]) + break; + if (tokstart[namelen] == '(' || tokstart[namelen] == ')') + break; + if (tokstart[namelen] == '"' || tokstart[namelen] == '\'') + break; + } + } else { + if (!is_idstart[c]) { + yyerror ("Invalid token in expression"); + return ERROR; + } + + for (namelen = 0; is_idchar[tokstart[namelen]]; namelen++) + ; + } + + lexptr += namelen; + yylval.name.address = tokstart; + yylval.name.length = namelen; + return NAME; +} + + +/* Parse a C escape sequence. STRING_PTR points to a variable + containing a pointer to the string to parse. That pointer + is updated past the characters we use. The value of the + escape sequence is returned. + + A negative value means the sequence \ newline was seen, + which is supposed to be equivalent to nothing at all. + + If \ is followed by a null character, we return a negative + value and leave the string pointer pointing at the null character. + + If \ is followed by 000, we return 0 and leave the string pointer + after the zeros. A value of 0 does not mean end of string. */ + +int +parse_escape (string_ptr) + char **string_ptr; +{ + register int c = *(*string_ptr)++; + switch (c) + { + case 'a': + return TARGET_BELL; + case 'b': + return TARGET_BS; + case 'e': + case 'E': + if (pedantic) + pedwarn ("non-ANSI-standard escape sequence, `\\%c'", c); + return 033; + case 'f': + return TARGET_FF; + case 'n': + return TARGET_NEWLINE; + case 'r': + return TARGET_CR; + case 't': + return TARGET_TAB; + case 'v': + return TARGET_VT; + case '\n': + return -2; + case 0: + (*string_ptr)--; + return 0; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + register int i = c - '0'; + register int count = 0; + while (++count < 3) + { + c = *(*string_ptr)++; + if (c >= '0' && c <= '7') + i = (i << 3) + c - '0'; + else + { + (*string_ptr)--; + break; + } + } + if ((i & ~((1 << MAX_CHAR_TYPE_SIZE) - 1)) != 0) + { + i &= (1 << MAX_CHAR_TYPE_SIZE) - 1; + warning ("octal character constant does not fit in a byte"); + } + return i; + } + case 'x': + { + register unsigned i = 0, overflow = 0, digits_found = 0, digit; + for (;;) + { + c = *(*string_ptr)++; + if (c >= '0' && c <= '9') + digit = c - '0'; + else if (c >= 'a' && c <= 'f') + digit = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + digit = c - 'A' + 10; + else + { + (*string_ptr)--; + break; + } + overflow |= i ^ (i << 4 >> 4); + i = (i << 4) + digit; + digits_found = 1; + } + if (!digits_found) + yyerror ("\\x used with no following hex digits"); + if (overflow | (i & ~((1 << BITS_PER_UNIT) - 1))) + { + i &= (1 << BITS_PER_UNIT) - 1; + warning ("hex character constant does not fit in a byte"); + } + return i; + } + default: + return c; + } +} + +void +yyerror (s) + char *s; +{ + error (s); + skip_evaluation = 0; + longjmp (parse_return_error, 1); +} + +static void +integer_overflow () +{ + if (!skip_evaluation && pedantic) + pedwarn ("integer overflow in preprocessor expression"); +} + +static long +left_shift (a, b) + struct constant *a; + unsigned long b; +{ + /* It's unclear from the C standard whether shifts can overflow. + The following code ignores overflow; perhaps a C standard + interpretation ruling is needed. */ + if (b >= HOST_BITS_PER_LONG) + return 0; + else if (a->unsignedp) + return (unsigned long) a->value << b; + else + return a->value << b; +} + +static long +right_shift (a, b) + struct constant *a; + unsigned long b; +{ + if (b >= HOST_BITS_PER_LONG) + return a->unsignedp ? 0 : a->value >> (HOST_BITS_PER_LONG - 1); + else if (a->unsignedp) + return (unsigned long) a->value >> b; + else + return a->value >> b; +} + +/* This page contains the entry point to this file. */ + +/* Parse STRING as an expression, and complain if this fails + to use up all of the contents of STRING. */ +/* We do not support C comments. They should be removed before + this function is called. */ + +HOST_WIDE_INT +parse_c_expression (string) + char *string; +{ + lexptr = string; + + if (lexptr == 0 || *lexptr == 0) { + error ("empty #if expression"); + return 0; /* don't include the #if group */ + } + + /* if there is some sort of scanning error, just return 0 and assume + the parsing routine has printed an error message somewhere. + there is surely a better thing to do than this. */ + if (setjmp (parse_return_error)) + return 0; + + if (yyparse ()) + return 0; /* actually this is never reached + the way things stand. */ + if (*lexptr) + error ("Junk after end of expression."); + + return expression_value; /* set by yyparse () */ +} + +#ifdef TEST_EXP_READER +extern int yydebug; + +/* Main program for testing purposes. */ +int +main () +{ + int n, c; + char buf[1024]; + +/* + yydebug = 1; +*/ + initialize_random_junk (); + + for (;;) { + printf ("enter expression: "); + n = 0; + while ((buf[n] = getchar ()) != '\n' && buf[n] != EOF) + n++; + if (buf[n] == EOF) + break; + buf[n] = '\0'; + printf ("parser returned %ld\n", parse_c_expression (buf)); + } + + return 0; +} + +/* table to tell if char can be part of a C identifier. */ +unsigned char is_idchar[256]; +/* table to tell if char can be first char of a c identifier. */ +unsigned char is_idstart[256]; +/* table to tell if c is horizontal space. isspace () thinks that + newline is space; this is not a good idea for this program. */ +char is_hor_space[256]; + +/* + * initialize random junk in the hash table and maybe other places + */ +initialize_random_junk () +{ + register int i; + + /* + * Set up is_idchar and is_idstart tables. These should be + * faster than saying (is_alpha (c) || c == '_'), etc. + * Must do set up these things before calling any routines tthat + * refer to them. + */ + for (i = 'a'; i <= 'z'; i++) { + ++is_idchar[i - 'a' + 'A']; + ++is_idchar[i]; + ++is_idstart[i - 'a' + 'A']; + ++is_idstart[i]; + } + for (i = '0'; i <= '9'; i++) + ++is_idchar[i]; + ++is_idchar['_']; + ++is_idstart['_']; +#if DOLLARS_IN_IDENTIFIERS + ++is_idchar['$']; + ++is_idstart['$']; +#endif + + /* horizontal space table */ + ++is_hor_space[' ']; + ++is_hor_space['\t']; +} + +error (msg) +{ + printf ("error: %s\n", msg); +} + +warning (msg) +{ + printf ("warning: %s\n", msg); +} + +struct hashnode * +lookup (name, len, hash) + char *name; + int len; + int hash; +{ + return (DEFAULT_SIGNED_CHAR) ? 0 : ((struct hashnode *) -1); +} +#endif diff --git a/gnu/usr.bin/gcc/gcc2netbsd b/gnu/usr.bin/gcc/gcc2netbsd index 6d254eebe0a..988d0b61ccd 100644 --- a/gnu/usr.bin/gcc/gcc2netbsd +++ b/gnu/usr.bin/gcc/gcc2netbsd @@ -1,6 +1,6 @@ #!/usr/gnu/bin/perl # -# $NetBSD: gcc2netbsd,v 1.4 1996/01/08 03:51:38 phil Exp $ +# $NetBSD: gcc2netbsd,v 1.5 1996/02/28 21:01:27 phil Exp $ # # Perl script to convert a standard distribution directory for gcc into # a NetBSD source tree. @@ -44,9 +44,11 @@ $version = "2.7.2"; @cc1f = ("c-parse.c", "c-lang.c"); +@cc1of = ("c-parse.y"); + @cc1objf = ("objc-parse.c", "objc-act.c"); -@cc1objof = ("objc-tree.def", "objc-act.h"); +@cc1objof = ("objc-tree.def", "objc-act.h", "objc-parse.y"); @cc1plusf = ("call.c", "decl.c", "errfn.c", "expr.c", "pt.c", "sig.c", "typeck2.c", "class.c", "decl2.c", "error.c", "gc.c", @@ -55,7 +57,7 @@ $version = "2.7.2"; "search.c", "tree.c", "xref.c", "repo.c"); @cc1plusof = ("tree.def", "class.h", "cp-tree.h", "decl.h", "hash.h", - "lex.h", "parse.h", "input.c"); + "lex.h", "parse.h", "input.c", "parse.y"); @commonf = ("bc-emit.c", "bc-optab.c", "bi-reverse.c", "c-aux-info.c", "c-common.c", "c-convert.c", "c-decl.c", "c-iterate.c", @@ -83,7 +85,7 @@ $version = "2.7.2"; "hard-reg-set.h", "input.h", "integrate.h", "loop.h", "machmode.h", "obstack.h", "output.h", "real.h", "recog.h", "regs.h", "reload.h", "rtl.h", "stack.h", - "tree.h", "typeclass.h", "xcoffout.h"); + "tree.h", "typeclass.h", "xcoffout.h", "bi-parser.y"); @commonplus = ("lang-specs.h", "lang-options.h"); @@ -95,7 +97,7 @@ $version = "2.7.2"; @cppf = ("cccp.c", "cexp.c"); -@cppof = ("pcp.h"); +@cppof = ("pcp.h", "cexp.y"); @gplusf = ("g++.c"); |
