%{ /* $NetBSD: cgram.y,v 1.54.2.1 2013/06/23 06:29:02 tls Exp $ */ /* * Copyright (c) 1996 Christopher G. Demetriou. All Rights Reserved. * Copyright (c) 1994, 1995 Jochen Pohl * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Jochen Pohl for * The NetBSD Project. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #if defined(__RCSID) && !defined(lint) __RCSID("$NetBSD: cgram.y,v 1.54.2.1 2013/06/23 06:29:02 tls Exp $"); #endif #include #include #include #include "lint1.h" extern char *yytext; /* * Contains the level of current declaration. 0 is extern. * Used for symbol table entries. */ int blklev; /* * level for memory allocation. Normaly the same as blklev. * An exception is the declaration of arguments in prototypes. Memory * for these can't be freed after the declaration, but symbols must * be removed from the symbol table after the declaration. */ int mblklev; /* * Save the no-warns state and restore it to avoid the problem where * if (expr) { stmt } / * NOLINT * / stmt; */ static int olwarn = LWARN_BAD; static int toicon(tnode_t *, int); static void idecl(sym_t *, int, sbuf_t *); static void ignuptorp(void); #ifdef DEBUG static inline void CLRWFLGS(const char *file, size_t line); static inline void CLRWFLGS(const char *file, size_t line) { printf("%s, %d: clear flags %s %zu\n", curr_pos.p_file, curr_pos.p_line, file, line); clrwflgs(); olwarn = LWARN_BAD; } static inline void SAVE(const char *file, size_t line); static inline void SAVE(const char *file, size_t line) { if (olwarn != LWARN_BAD) abort(); printf("%s, %d: save flags %s %zu = %d\n", curr_pos.p_file, curr_pos.p_line, file, line, lwarn); olwarn = lwarn; } static inline void RESTORE(const char *file, size_t line); static inline void RESTORE(const char *file, size_t line) { if (olwarn != LWARN_BAD) { lwarn = olwarn; printf("%s, %d: restore flags %s %zu = %d\n", curr_pos.p_file, curr_pos.p_line, file, line, lwarn); olwarn = LWARN_BAD; } else CLRWFLGS(file, line); } #else #define CLRWFLGS(f, l) clrwflgs(), olwarn = LWARN_BAD #define SAVE(f, l) olwarn = lwarn #define RESTORE(f, l) (void)(olwarn == LWARN_BAD ? (clrwflgs(), 0) : (lwarn = olwarn)) #endif %} %expect 5 %union { int y_int; val_t *y_val; sbuf_t *y_sb; sym_t *y_sym; op_t y_op; scl_t y_scl; tspec_t y_tspec; tqual_t y_tqual; type_t *y_type; tnode_t *y_tnode; range_t y_range; strg_t *y_strg; pqinf_t *y_pqinf; }; %token T_LBRACE T_RBRACE T_LBRACK T_RBRACK T_LPARN T_RPARN %token T_STROP %token T_UNOP %token T_INCDEC %token T_SIZEOF %token T_ALIGNOF %token T_MULT %token T_DIVOP %token T_ADDOP %token T_SHFTOP %token T_RELOP %token T_EQOP %token T_AND %token T_XOR %token T_OR %token T_LOGAND %token T_LOGOR %token T_QUEST %token T_COLON %token T_ASSIGN %token T_OPASS %token T_COMMA %token T_SEMI %token T_ELLIPSE %token T_REAL %token T_IMAG /* storage classes (extern, static, auto, register and typedef) */ %token T_SCLASS /* types (char, int, short, long, unsigned, signed, float, double, void) */ %token T_TYPE /* qualifiers (const, volatile) */ %token T_QUAL /* struct or union */ %token T_SOU /* enum */ %token T_ENUM /* remaining keywords */ %token T_CASE %token T_DEFAULT %token T_IF %token T_ELSE %token T_SWITCH %token T_DO %token T_WHILE %token T_FOR %token T_GOTO %token T_CONTINUE %token T_BREAK %token T_RETURN %token T_ASM %token T_SYMBOLRENAME %token T_PACKED /* Type Attributes */ %token T_ATTRIBUTE %token T_AT_ALIGNED %token T_AT_DEPRECATED %token T_AT_MAY_ALIAS %token T_AT_PACKED %token T_AT_TUINION %token T_AT_TUNION %token T_AT_UNUSED %left T_COMMA %right T_ASSIGN T_OPASS %right T_QUEST T_COLON %left T_LOGOR %left T_LOGAND %left T_OR %left T_XOR %left T_AND %left T_EQOP %left T_RELOP %left T_SHFTOP %left T_ADDOP %left T_MULT T_DIVOP %right T_UNOP T_INCDEC T_SIZEOF T_ALIGNOF T_REAL T_IMAG %left T_LPARN T_LBRACK T_STROP %token T_NAME %token T_TYPENAME %token T_CON %token T_STRING %type func_decl %type notype_decl %type type_decl %type typespec %type clrtyp_typespec %type notype_typespec %type struct_spec %type enum_spec %type type_attribute %type type_attribute_spec %type struct_tag %type enum_tag %type struct %type struct_declaration %type identifier %type member_declaration_list_with_rbrace %type member_declaration_list %type member_declaration %type notype_member_decls %type type_member_decls %type notype_member_decl %type type_member_decl %type constant %type enum_declaration %type enums_with_opt_comma %type enums %type enumerator %type ename %type notype_direct_decl %type type_direct_decl %type pointer %type asterisk %type param_decl %type param_list %type abs_decl_param_list %type direct_param_decl %type notype_param_decl %type direct_notype_param_decl %type type_qualifier_list %type type_qualifier %type identifier_list %type abs_decl %type direct_abs_decl %type vararg_parameter_type_list %type parameter_type_list %type parameter_declaration %type expr %type expr_stmnt_val %type expr_stmnt_list %type term %type func_arg_list %type point_or_arrow %type type_name %type abstract_declaration %type do_while_expr %type opt_expr %type string %type string2 %type opt_asm_or_symbolrename %type range %type lorange %% program: /* empty */ { if (sflag) { /* empty translation unit */ error(272); } else if (!tflag) { /* empty translation unit */ warning(272); } } | translation_unit ; translation_unit: ext_decl | translation_unit ext_decl ; ext_decl: asm_stmnt | func_def { glclup(0); CLRWFLGS(__FILE__, __LINE__); } | data_def { glclup(0); CLRWFLGS(__FILE__, __LINE__); } ; data_def: T_SEMI { if (sflag) { /* syntax error: empty declaration */ error(0); } else if (!tflag) { /* syntax error: empty declaration */ warning(0); } } | clrtyp deftyp notype_init_decls T_SEMI { if (sflag) { /* old style declaration; add "int" */ error(1); } else if (!tflag) { /* old style declaration; add "int" */ warning(1); } } | declmods deftyp T_SEMI { if (dcs->d_scl == TYPEDEF) { /* typedef declares no type name */ warning(72); } else { /* empty declaration */ warning(2); } } | declmods deftyp notype_init_decls T_SEMI | declspecs deftyp T_SEMI { if (dcs->d_scl == TYPEDEF) { /* typedef declares no type name */ warning(72); } else if (!dcs->d_nedecl) { /* empty declaration */ warning(2); } } | declspecs deftyp type_init_decls T_SEMI | error T_SEMI { globclup(); } | error T_RBRACE { globclup(); } ; func_def: func_decl { if ($1->s_type->t_tspec != FUNC) { /* syntax error */ error(249, yytext); YYERROR; } if ($1->s_type->t_typedef) { /* ()-less function definition */ error(64); YYERROR; } funcdef($1); blklev++; pushdecl(ARG); if (lwarn == LWARN_NONE) $1->s_used = 1; } opt_arg_declaration_list { popdecl(); blklev--; cluparg(); pushctrl(0); } comp_stmnt { funcend(); popctrl(0); } ; func_decl: clrtyp deftyp notype_decl { $$ = $3; } | declmods deftyp notype_decl { $$ = $3; } | declspecs deftyp type_decl { $$ = $3; } ; opt_arg_declaration_list: /* empty */ | arg_declaration_list ; arg_declaration_list: arg_declaration | arg_declaration_list arg_declaration /* XXX or better "arg_declaration error" ? */ | error ; /* * "arg_declaration" is separated from "declaration" because it * needs other error handling. */ arg_declaration: declmods deftyp T_SEMI { /* empty declaration */ warning(2); } | declmods deftyp notype_init_decls T_SEMI | declspecs deftyp T_SEMI { if (!dcs->d_nedecl) { /* empty declaration */ warning(2); } else { tspec_t ts = dcs->d_type->t_tspec; /* %s declared in argument declaration list */ warning(3, ts == STRUCT ? "struct" : (ts == UNION ? "union" : "enum")); } } | declspecs deftyp type_init_decls T_SEMI { if (dcs->d_nedecl) { tspec_t ts = dcs->d_type->t_tspec; /* %s declared in argument declaration list */ warning(3, ts == STRUCT ? "struct" : (ts == UNION ? "union" : "enum")); } } | declmods error | declspecs error ; declaration: declmods deftyp T_SEMI { if (dcs->d_scl == TYPEDEF) { /* typedef declares no type name */ warning(72); } else { /* empty declaration */ warning(2); } } | declmods deftyp notype_init_decls T_SEMI | declspecs deftyp T_SEMI { if (dcs->d_scl == TYPEDEF) { /* typedef declares no type name */ warning(72); } else if (!dcs->d_nedecl) { /* empty declaration */ warning(2); } } | declspecs deftyp type_init_decls T_SEMI | error T_SEMI ; type_attribute_spec: T_AT_DEPRECATED | T_AT_ALIGNED T_LPARN constant T_RPARN | T_AT_MAY_ALIAS | T_AT_PACKED { addpacked(); } | T_AT_TUNION | T_AT_UNUSED ; type_attribute: T_ATTRIBUTE T_LPARN T_LPARN type_attribute_spec T_RPARN T_RPARN | T_PACKED { addpacked(); } ; clrtyp: { clrtyp(); } ; deftyp: /* empty */ { deftyp(); } ; declspecs: clrtyp_typespec { addtype($1); } | declmods typespec { addtype($2); } | declspecs type_attribute | declspecs declmod | declspecs notype_typespec { addtype($2); } ; declmods: clrtyp T_QUAL { addqual($2); } | clrtyp T_SCLASS { addscl($2); } | declmods declmod ; declmod: T_QUAL { addqual($1); } | T_SCLASS { addscl($1); } ; clrtyp_typespec: clrtyp notype_typespec { $$ = $2; } | T_TYPENAME clrtyp { $$ = getsym($1)->s_type; } ; typespec: notype_typespec { $$ = $1; } | T_TYPENAME { $$ = getsym($1)->s_type; } ; notype_typespec: T_TYPE { $$ = gettyp($1); } | struct_spec { popdecl(); $$ = $1; } | enum_spec { popdecl(); $$ = $1; } ; struct_spec: struct struct_tag { /* * STDC requires that "struct a;" always introduces * a new tag if "a" is not declared at current level * * yychar is valid because otherwise the parser would * not been able to deceide if he must shift or reduce */ $$ = mktag($2, $1, 0, yychar == T_SEMI); } | struct struct_tag { dcs->d_tagtyp = mktag($2, $1, 1, 0); } struct_declaration { $$ = compltag(dcs->d_tagtyp, $4); } | struct { dcs->d_tagtyp = mktag(NULL, $1, 1, 0); } struct_declaration { $$ = compltag(dcs->d_tagtyp, $3); } | struct error { symtyp = FVFT; $$ = gettyp(INT); } ; struct: struct type_attribute | T_SOU { symtyp = FTAG; pushdecl($1 == STRUCT ? MOS : MOU); dcs->d_offset = 0; dcs->d_stralign = CHAR_BIT; $$ = $1; } ; struct_tag: identifier { $$ = getsym($1); } ; struct_declaration: struct_decl_lbrace member_declaration_list_with_rbrace { $$ = $2; } ; struct_decl_lbrace: T_LBRACE { symtyp = FVFT; } ; member_declaration_list_with_rbrace: member_declaration_list T_SEMI T_RBRACE { $$ = $1; } | member_declaration_list T_RBRACE { if (sflag) { /* syntax req. ";" after last struct/union member */ error(66); } else { /* syntax req. ";" after last struct/union member */ warning(66); } $$ = $1; } | T_RBRACE { $$ = NULL; } ; member_declaration_list: member_declaration { $$ = $1; } | member_declaration_list T_SEMI member_declaration { $$ = lnklst($1, $3); } ; member_declaration: noclass_declmods deftyp { /* too late, i know, but getsym() compensates it */ symtyp = FMOS; } notype_member_decls { symtyp = FVFT; $$ = $4; } | noclass_declspecs deftyp { symtyp = FMOS; } type_member_decls { symtyp = FVFT; $$ = $4; } | noclass_declmods deftyp { /* struct or union member must be named */ warning(49); $$ = NULL; } | noclass_declspecs deftyp { /* struct or union member must be named */ warning(49); $$ = NULL; } | error { symtyp = FVFT; $$ = NULL; } ; noclass_declspecs: clrtyp_typespec { addtype($1); } | noclass_declmods typespec { addtype($2); } | noclass_declspecs T_QUAL { addqual($2); } | noclass_declspecs notype_typespec { addtype($2); } | noclass_declspecs type_attribute ; noclass_declmods: clrtyp T_QUAL { addqual($2); } | noclass_declmods T_QUAL { addqual($2); } ; notype_member_decls: notype_member_decl { $$ = decl1str($1); } | notype_member_decls { symtyp = FMOS; } T_COMMA type_member_decl { $$ = lnklst($1, decl1str($4)); } ; type_member_decls: type_member_decl { $$ = decl1str($1); } | type_member_decls { symtyp = FMOS; } T_COMMA type_member_decl { $$ = lnklst($1, decl1str($4)); } ; notype_member_decl: notype_decl { $$ = $1; } | notype_decl T_COLON constant { $$ = bitfield($1, toicon($3, 1)); } | { symtyp = FVFT; } T_COLON constant { $$ = bitfield(NULL, toicon($3, 1)); } ; type_member_decl: type_decl { $$ = $1; } | type_decl T_COLON constant { $$ = bitfield($1, toicon($3, 1)); } | { symtyp = FVFT; } T_COLON constant { $$ = bitfield(NULL, toicon($3, 1)); } ; enum_spec: enum enum_tag { $$ = mktag($2, ENUM, 0, 0); } | enum enum_tag { dcs->d_tagtyp = mktag($2, ENUM, 1, 0); } enum_declaration { $$ = compltag(dcs->d_tagtyp, $4); } | enum { dcs->d_tagtyp = mktag(NULL, ENUM, 1, 0); } enum_declaration { $$ = compltag(dcs->d_tagtyp, $3); } | enum error { symtyp = FVFT; $$ = gettyp(INT); } ; enum: T_ENUM { symtyp = FTAG; pushdecl(ENUMCON); } ; enum_tag: identifier { $$ = getsym($1); } ; enum_declaration: enum_decl_lbrace enums_with_opt_comma T_RBRACE { $$ = $2; } ; enum_decl_lbrace: T_LBRACE { symtyp = FVFT; enumval = 0; } ; enums_with_opt_comma: enums { $$ = $1; } | enums T_COMMA { if (sflag) { /* trailing "," prohibited in enum declaration */ error(54); } else { /* trailing "," prohibited in enum declaration */ c99ism(54); } $$ = $1; } ; enums: enumerator { $$ = $1; } | enums T_COMMA enumerator { $$ = lnklst($1, $3); } | error { $$ = NULL; } ; enumerator: ename { $$ = ename($1, enumval, 1); } | ename T_ASSIGN constant { $$ = ename($1, toicon($3, 1), 0); } ; ename: identifier { $$ = getsym($1); } ; notype_init_decls: notype_init_decl | notype_init_decls T_COMMA type_init_decl ; type_init_decls: type_init_decl | type_init_decls T_COMMA type_init_decl ; notype_init_decl: notype_decl opt_asm_or_symbolrename { idecl($1, 0, $2); chksz($1); } | notype_decl opt_asm_or_symbolrename { idecl($1, 1, $2); } T_ASSIGN initializer { chksz($1); } ; type_init_decl: type_decl opt_asm_or_symbolrename { idecl($1, 0, $2); chksz($1); } | type_decl opt_asm_or_symbolrename { idecl($1, 1, $2); } T_ASSIGN initializer { chksz($1); } ; notype_decl: notype_direct_decl { $$ = $1; } | pointer notype_direct_decl { $$ = addptr($2, $1); } ; notype_direct_decl: T_NAME { $$ = dname(getsym($1)); } | T_LPARN type_decl T_RPARN { $$ = $2; } | notype_direct_decl T_LBRACK T_RBRACK { $$ = addarray($1, 0, 0); } | notype_direct_decl T_LBRACK constant T_RBRACK { $$ = addarray($1, 1, toicon($3, 0)); } | notype_direct_decl param_list { $$ = addfunc($1, $2); popdecl(); blklev--; } | notype_direct_decl type_attribute ; type_decl: type_direct_decl { $$ = $1; } | pointer type_direct_decl { $$ = addptr($2, $1); } ; type_direct_decl: identifier { $$ = dname(getsym($1)); } | T_LPARN type_decl T_RPARN { $$ = $2; } | type_direct_decl T_LBRACK T_RBRACK { $$ = addarray($1, 0, 0); } | type_direct_decl T_LBRACK constant T_RBRACK { $$ = addarray($1, 1, toicon($3, 0)); } | type_direct_decl param_list { $$ = addfunc($1, $2); popdecl(); blklev--; } | type_direct_decl type_attribute ; /* * param_decl and notype_param_decl exist to avoid a conflict in * argument lists. A typename enclosed in parens should always be * treated as a typename, not an argument. * "typedef int a; f(int (a));" is "typedef int a; f(int foo(a));" * not "typedef int a; f(int a);" */ param_decl: direct_param_decl { $$ = $1; } | pointer direct_param_decl { $$ = addptr($2, $1); } ; direct_param_decl: identifier { $$ = dname(getsym($1)); } | T_LPARN notype_param_decl T_RPARN { $$ = $2; } | direct_param_decl T_LBRACK T_RBRACK { $$ = addarray($1, 0, 0); } | direct_param_decl T_LBRACK constant T_RBRACK { $$ = addarray($1, 1, toicon($3, 0)); } | direct_param_decl param_list { $$ = addfunc($1, $2); popdecl(); blklev--; } ; notype_param_decl: direct_notype_param_decl { $$ = $1; } | pointer direct_notype_param_decl { $$ = addptr($2, $1); } ; direct_notype_param_decl: T_NAME { $$ = dname(getsym($1)); } | T_LPARN notype_param_decl T_RPARN { $$ = $2; } | direct_notype_param_decl T_LBRACK T_RBRACK { $$ = addarray($1, 0, 0); } | direct_notype_param_decl T_LBRACK constant T_RBRACK { $$ = addarray($1, 1, toicon($3, 0)); } | direct_notype_param_decl param_list { $$ = addfunc($1, $2); popdecl(); blklev--; } ; pointer: asterisk { $$ = $1; } | asterisk type_qualifier_list { $$ = mergepq($1, $2); } | asterisk pointer { $$ = mergepq($1, $2); } | asterisk type_qualifier_list pointer { $$ = mergepq(mergepq($1, $2), $3); } ; asterisk: T_MULT { $$ = xcalloc(1, sizeof (pqinf_t)); $$->p_pcnt = 1; } ; type_qualifier_list: type_qualifier { $$ = $1; } | type_qualifier_list type_qualifier { $$ = mergepq($1, $2); } ; type_qualifier: T_QUAL { $$ = xcalloc(1, sizeof (pqinf_t)); if ($1 == CONST) { $$->p_const = 1; } else { $$->p_volatile = 1; } } ; param_list: id_list_lparn identifier_list T_RPARN { $$ = $2; } | abs_decl_param_list { $$ = $1; } ; id_list_lparn: T_LPARN { blklev++; pushdecl(PARG); } ; identifier_list: T_NAME { $$ = iname(getsym($1)); } | identifier_list T_COMMA T_NAME { $$ = lnklst($1, iname(getsym($3))); } | identifier_list error { $$ = $1; } ; abs_decl_param_list: abs_decl_lparn T_RPARN { $$ = NULL; } | abs_decl_lparn vararg_parameter_type_list T_RPARN { dcs->d_proto = 1; $$ = $2; } | abs_decl_lparn error T_RPARN { $$ = NULL; } ; abs_decl_lparn: T_LPARN { blklev++; pushdecl(PARG); } ; vararg_parameter_type_list: parameter_type_list { $$ = $1; } | parameter_type_list T_COMMA T_ELLIPSE { dcs->d_vararg = 1; $$ = $1; } | T_ELLIPSE { if (sflag) { /* ANSI C requires formal parameter before "..." */ error(84); } else if (!tflag) { /* ANSI C requires formal parameter before "..." */ warning(84); } dcs->d_vararg = 1; $$ = NULL; } ; parameter_type_list: parameter_declaration { $$ = $1; } | parameter_type_list T_COMMA parameter_declaration { $$ = lnklst($1, $3); } ; parameter_declaration: declmods deftyp { $$ = decl1arg(aname(), 0); } | declspecs deftyp { $$ = decl1arg(aname(), 0); } | declmods deftyp notype_param_decl { $$ = decl1arg($3, 0); } /* * param_decl is needed because of following conflict: * "typedef int a; f(int (a));" could be parsed as * "function with argument a of type int", or * "function with an abstract argument of type function". * This grammar realizes the second case. */ | declspecs deftyp param_decl { $$ = decl1arg($3, 0); } | declmods deftyp abs_decl { $$ = decl1arg($3, 0); } | declspecs deftyp abs_decl { $$ = decl1arg($3, 0); } ; opt_asm_or_symbolrename: /* expect only one */ /* empty */ { $$ = NULL; } | T_ASM T_LPARN T_STRING T_RPARN { freeyyv(&$3, T_STRING); $$ = NULL; } | T_SYMBOLRENAME T_LPARN T_NAME T_RPARN { $$ = $3; } ; initializer: init_expr ; init_expr: expr %prec T_COMMA { mkinit($1); } | init_by_name init_expr %prec T_COMMA | init_lbrace init_expr_list init_rbrace | init_lbrace init_expr_list T_COMMA init_rbrace | error ; init_expr_list: init_expr %prec T_COMMA | init_expr_list T_COMMA init_expr ; lorange: constant T_ELLIPSE { $$.lo = toicon($1, 1); } ; range: constant { $$.lo = toicon($1, 1); $$.hi = $$.lo + 1; } | lorange constant { $$.lo = $1.lo; $$.hi = toicon($2, 1); } ; init_by_name: T_LBRACK range T_RBRACK T_ASSIGN { if (!Sflag) warning(321); } | point identifier T_ASSIGN { if (!Sflag) warning(313); memberpush($2); } | identifier T_COLON { gnuism(315); memberpush($1); } ; init_lbrace: T_LBRACE { initlbr(); } ; init_rbrace: T_RBRACE { initrbr(); } ; type_name: { pushdecl(ABSTRACT); } abstract_declaration { popdecl(); $$ = $2->s_type; } ; abstract_declaration: noclass_declmods deftyp { $$ = decl1abs(aname()); } | noclass_declspecs deftyp { $$ = decl1abs(aname()); } | noclass_declmods deftyp abs_decl { $$ = decl1abs($3); } | noclass_declspecs deftyp abs_decl { $$ = decl1abs($3); } ; abs_decl: pointer { $$ = addptr(aname(), $1); } | direct_abs_decl { $$ = $1; } | pointer direct_abs_decl { $$ = addptr($2, $1); } ; direct_abs_decl: T_LPARN abs_decl T_RPARN { $$ = $2; } | T_LBRACK T_RBRACK { $$ = addarray(aname(), 0, 0); } | T_LBRACK constant T_RBRACK { $$ = addarray(aname(), 1, toicon($2, 0)); } | direct_abs_decl T_LBRACK T_RBRACK { $$ = addarray($1, 0, 0); } | direct_abs_decl T_LBRACK constant T_RBRACK { $$ = addarray($1, 1, toicon($3, 0)); } | abs_decl_param_list { $$ = addfunc(aname(), $1); popdecl(); blklev--; } | direct_abs_decl abs_decl_param_list { $$ = addfunc($1, $2); popdecl(); blklev--; } | direct_abs_decl type_attribute ; non_expr_stmnt: labeled_stmnt | comp_stmnt | selection_stmnt | iteration_stmnt | jump_stmnt { ftflg = 0; } | asm_stmnt stmnt: expr_stmnt | non_expr_stmnt ; labeled_stmnt: label stmnt ; label: T_NAME T_COLON { symtyp = FLAB; label(T_NAME, getsym($1), NULL); } | T_CASE constant T_COLON { label(T_CASE, NULL, $2); ftflg = 1; } | T_CASE constant T_ELLIPSE constant T_COLON { /* XXX: We don't fill all cases */ label(T_CASE, NULL, $2); ftflg = 1; } | T_DEFAULT T_COLON { label(T_DEFAULT, NULL, NULL); ftflg = 1; } ; stmnt_d_list: stmnt_list | stmnt_d_list declaration_list stmnt_list { if (!Sflag) c99ism(327); } ; comp_stmnt: comp_stmnt_lbrace comp_stmnt_rbrace | comp_stmnt_lbrace stmnt_list comp_stmnt_rbrace | comp_stmnt_lbrace declaration_list comp_stmnt_rbrace | comp_stmnt_lbrace declaration_list stmnt_d_list comp_stmnt_rbrace ; comp_stmnt_lbrace: T_LBRACE { blklev++; mblklev++; pushdecl(AUTO); } ; comp_stmnt_rbrace: T_RBRACE { popdecl(); freeblk(); mblklev--; blklev--; ftflg = 0; } ; stmnt_list: stmnt | stmnt_list stmnt { RESTORE(__FILE__, __LINE__); } | stmnt_list error T_SEMI ; expr_stmnt: expr T_SEMI { expr($1, 0, 0, 1); ftflg = 0; } | T_SEMI { ftflg = 0; } ; /* * The following two productions are used to implement * ({ [[decl-list] stmt-list] }). * XXX: This is not well tested. */ expr_stmnt_val: expr T_SEMI { /* XXX: We should really do that only on the last name */ if ($1->tn_op == NAME) $1->tn_sym->s_used = 1; $$ = $1; expr($1, 0, 0, 0); ftflg = 0; } | non_expr_stmnt { $$ = getnode(); $$->tn_type = gettyp(VOID); } ; expr_stmnt_list: expr_stmnt_val | expr_stmnt_list expr_stmnt_val { $$ = $2; } ; selection_stmnt: if_without_else { SAVE(__FILE__, __LINE__); if2(); if3(0); } | if_without_else T_ELSE { SAVE(__FILE__, __LINE__); if2(); } stmnt { CLRWFLGS(__FILE__, __LINE__); if3(1); } | if_without_else T_ELSE error { CLRWFLGS(__FILE__, __LINE__); if3(0); } | switch_expr stmnt { CLRWFLGS(__FILE__, __LINE__); switch2(); } | switch_expr error { CLRWFLGS(__FILE__, __LINE__); switch2(); } ; if_without_else: if_expr stmnt | if_expr error ; if_expr: T_IF T_LPARN expr T_RPARN { if1($3); CLRWFLGS(__FILE__, __LINE__); } ; switch_expr: T_SWITCH T_LPARN expr T_RPARN { switch1($3); CLRWFLGS(__FILE__, __LINE__); } ; do_stmnt: do stmnt { CLRWFLGS(__FILE__, __LINE__); } ; iteration_stmnt: while_expr stmnt { CLRWFLGS(__FILE__, __LINE__); while2(); } | while_expr error { CLRWFLGS(__FILE__, __LINE__); while2(); } | do_stmnt do_while_expr { do2($2); ftflg = 0; } | do error { CLRWFLGS(__FILE__, __LINE__); do2(NULL); } | for_exprs stmnt { CLRWFLGS(__FILE__, __LINE__); for2(); } | for_exprs error { CLRWFLGS(__FILE__, __LINE__); for2(); } ; while_expr: T_WHILE T_LPARN expr T_RPARN { while1($3); CLRWFLGS(__FILE__, __LINE__); } ; do: T_DO { do1(); } ; do_while_expr: T_WHILE T_LPARN expr T_RPARN T_SEMI { $$ = $3; } ; for_exprs: T_FOR T_LPARN declspecs deftyp notype_init_decls T_SEMI opt_expr T_SEMI opt_expr T_RPARN { c99ism(325); for1(NULL, $7, $9); CLRWFLGS(__FILE__, __LINE__); } | T_FOR T_LPARN opt_expr T_SEMI opt_expr T_SEMI opt_expr T_RPARN { for1($3, $5, $7); CLRWFLGS(__FILE__, __LINE__); } ; opt_expr: /* empty */ { $$ = NULL; } | expr { $$ = $1; } ; jump_stmnt: goto identifier T_SEMI { dogoto(getsym($2)); } | goto error T_SEMI { symtyp = FVFT; } | T_CONTINUE T_SEMI { docont(); } | T_BREAK T_SEMI { dobreak(); } | T_RETURN T_SEMI { doreturn(NULL); } | T_RETURN expr T_SEMI { doreturn($2); } ; goto: T_GOTO { symtyp = FLAB; } ; asm_stmnt: T_ASM T_LPARN read_until_rparn T_SEMI { setasm(); } | T_ASM T_QUAL T_LPARN read_until_rparn T_SEMI { setasm(); } | T_ASM error ; read_until_rparn: /* empty */ { ignuptorp(); } ; declaration_list: declaration { CLRWFLGS(__FILE__, __LINE__); } | declaration_list declaration { CLRWFLGS(__FILE__, __LINE__); } ; constant: expr %prec T_COMMA { $$ = $1; } ; expr: expr T_MULT expr { $$ = build(MULT, $1, $3); } | expr T_DIVOP expr { $$ = build($2, $1, $3); } | expr T_ADDOP expr { $$ = build($2, $1, $3); } | expr T_SHFTOP expr { $$ = build($2, $1, $3); } | expr T_RELOP expr { $$ = build($2, $1, $3); } | expr T_EQOP expr { $$ = build($2, $1, $3); } | expr T_AND expr { $$ = build(AND, $1, $3); } | expr T_XOR expr { $$ = build(XOR, $1, $3); } | expr T_OR expr { $$ = build(OR, $1, $3); } | expr T_LOGAND expr { $$ = build(LOGAND, $1, $3); } | expr T_LOGOR expr { $$ = build(LOGOR, $1, $3); } | expr T_QUEST expr T_COLON expr { $$ = build(QUEST, $1, build(COLON, $3, $5)); } | expr T_ASSIGN expr { $$ = build(ASSIGN, $1, $3); } | expr T_OPASS expr { $$ = build($2, $1, $3); } | expr T_COMMA expr { $$ = build(COMMA, $1, $3); } | term { $$ = $1; } ; term: T_NAME { /* XXX really necessary? */ if (yychar < 0) yychar = yylex(); $$ = getnnode(getsym($1), yychar); } | string { $$ = getsnode($1); } | T_CON { $$ = getcnode(gettyp($1->v_tspec), $1); } | T_LPARN expr T_RPARN { if ($2 != NULL) $2->tn_parn = 1; $$ = $2; } | T_LPARN comp_stmnt_lbrace declaration_list expr_stmnt_list { blklev--; mblklev--; initsym = mktempsym(duptyp($4->tn_type)); mblklev++; blklev++; gnuism(320); } comp_stmnt_rbrace T_RPARN { $$ = getnnode(initsym, 0); } | T_LPARN comp_stmnt_lbrace expr_stmnt_list { blklev--; mblklev--; initsym = mktempsym($3->tn_type); mblklev++; blklev++; gnuism(320); } comp_stmnt_rbrace T_RPARN { $$ = getnnode(initsym, 0); } | term T_INCDEC { $$ = build($2 == INC ? INCAFT : DECAFT, $1, NULL); } | T_INCDEC term { $$ = build($1 == INC ? INCBEF : DECBEF, $2, NULL); } | T_MULT term { $$ = build(STAR, $2, NULL); } | T_AND term { $$ = build(AMPER, $2, NULL); } | T_UNOP term { $$ = build($1, $2, NULL); } | T_ADDOP term { if (tflag && $1 == PLUS) { /* unary + is illegal in traditional C */ warning(100); } $$ = build($1 == PLUS ? UPLUS : UMINUS, $2, NULL); } | term T_LBRACK expr T_RBRACK { $$ = build(STAR, build(PLUS, $1, $3), NULL); } | term T_LPARN T_RPARN { $$ = funccall($1, NULL); } | term T_LPARN func_arg_list T_RPARN { $$ = funccall($1, $3); } | term point_or_arrow T_NAME { if ($1 != NULL) { sym_t *msym; /* XXX strmemb should be integrated in build() */ if ($2 == ARROW) { /* must to this before strmemb is called */ $1 = cconv($1); } msym = strmemb($1, $2, getsym($3)); $$ = build($2, $1, getnnode(msym, 0)); } else { $$ = NULL; } } | T_REAL term { $$ = build(REAL, $2, NULL); } | T_IMAG term { $$ = build(IMAG, $2, NULL); } | T_REAL T_LPARN term T_RPARN { $$ = build(REAL, $3, NULL); } | T_IMAG T_LPARN term T_RPARN { $$ = build(IMAG, $3, NULL); } | T_SIZEOF term %prec T_SIZEOF { if (($$ = $2 == NULL ? NULL : bldszof($2->tn_type)) != NULL) chkmisc($2, 0, 0, 0, 0, 0, 1); } | T_SIZEOF T_LPARN type_name T_RPARN %prec T_SIZEOF { $$ = bldszof($3); } | T_ALIGNOF T_LPARN type_name T_RPARN %prec T_ALIGNOF { $$ = bldalof($3); } | T_LPARN type_name T_RPARN term %prec T_UNOP { $$ = cast($4, $2); } | T_LPARN type_name T_RPARN %prec T_UNOP { sym_t *tmp = mktempsym($2); idecl(tmp, 1, NULL); } init_lbrace init_expr_list init_rbrace { if (!Sflag) gnuism(319); $$ = getnnode(initsym, 0); } ; string: T_STRING { $$ = $1; } | T_STRING string2 { $$ = catstrg($1, $2); } ; string2: T_STRING { if (tflag) { /* concatenated strings are illegal in traditional C */ warning(219); } $$ = $1; } | string2 T_STRING { $$ = catstrg($1, $2); } ; func_arg_list: expr %prec T_COMMA { $$ = funcarg(NULL, $1); } | func_arg_list T_COMMA expr { $$ = funcarg($1, $3); } ; point_or_arrow: T_STROP { symtyp = FMOS; $$ = $1; } ; point: T_STROP { if ($1 != POINT) { error(249, yytext); } } ; identifier: T_NAME { $$ = $1; } | T_TYPENAME { $$ = $1; } ; %% /* ARGSUSED */ int yyerror(const char *msg) { error(249, yytext); if (++sytxerr >= 5) norecover(); return (0); } static __inline int uq_gt(uint64_t, uint64_t); static __inline int q_gt(int64_t, int64_t); static __inline int uq_gt(uint64_t a, uint64_t b) { return (a > b); } static __inline int q_gt(int64_t a, int64_t b) { return (a > b); } #define q_lt(a, b) q_gt(b, a) /* * Gets a node for a constant and returns the value of this constant * as integer. * Is the node not constant or too large for int or of type float, * a warning will be printed. * * toicon() should be used only inside declarations. If it is used in * expressions, it frees the memory used for the expression. */ static int toicon(tnode_t *tn, int required) { int i; tspec_t t; val_t *v; v = constant(tn, required); /* * Abstract declarations are used inside expression. To free * the memory would be a fatal error. */ if (dcs->d_ctx != ABSTRACT) tfreeblk(); if ((t = v->v_tspec) == FLOAT || t == DOUBLE || t == LDOUBLE) { i = (int)v->v_ldbl; /* integral constant expression expected */ error(55); } else { i = (int)v->v_quad; if (isutyp(t)) { if (uq_gt((uint64_t)v->v_quad, (uint64_t)TARG_INT_MAX)) { /* integral constant too large */ warning(56); } } else { if (q_gt(v->v_quad, (int64_t)TARG_INT_MAX) || q_lt(v->v_quad, (int64_t)TARG_INT_MIN)) { /* integral constant too large */ warning(56); } } } free(v); return (i); } static void idecl(sym_t *decl, int initflg, sbuf_t *renaming) { char *s; initerr = 0; initsym = decl; switch (dcs->d_ctx) { case EXTERN: if (renaming != NULL) { if (decl->s_rename != NULL) LERROR("idecl()"); s = getlblk(1, renaming->sb_len + 1); (void)memcpy(s, renaming->sb_name, renaming->sb_len + 1); decl->s_rename = s; freeyyv(&renaming, T_NAME); } decl1ext(decl, initflg); break; case ARG: if (renaming != NULL) { /* symbol renaming can't be used on function arguments */ error(310); freeyyv(&renaming, T_NAME); break; } (void)decl1arg(decl, initflg); break; case AUTO: if (renaming != NULL) { /* symbol renaming can't be used on automatic variables */ error(311); freeyyv(&renaming, T_NAME); break; } decl1loc(decl, initflg); break; default: LERROR("idecl()"); } if (initflg && !initerr) prepinit(); } /* * Discard all input tokens up to and including the next * unmatched right paren */ static void ignuptorp(void) { int level; if (yychar < 0) yychar = yylex(); freeyyv(&yylval, yychar); level = 1; while (yychar != T_RPARN || --level > 0) { if (yychar == T_LPARN) { level++; } else if (yychar <= 0) { break; } freeyyv(&yylval, yychar = yylex()); } yyclearin; }