%{ /* $OpenBSD: cgram.y,v 1.4 2002/02/16 21:27:59 millert Exp $ */ /* $NetBSD: cgram.y,v 1.8 1995/10/02 17:31:35 jpo Exp $ */ /* * 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. */ #ifndef lint static char rcsid[] = "$OpenBSD: cgram.y,v 1.4 2002/02/16 21:27:59 millert Exp $"; #endif #include #include #include "lint1.h" /* * 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 exeption 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; static int toicon(tnode_t *); static void idecl(sym_t *, int); static void ignuptorp(void); %} %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; 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_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 /* 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 %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 %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 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 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 %% 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: func_def { glclup(0); clrwflgs(); } | data_def { glclup(0); clrwflgs(); } ; 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); YYERROR; } if ($1->s_type->t_typedef) { /* ()-less function definition */ error(64); YYERROR; } funcdef($1); blklev++; pushdecl(ARG); } 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 ; clrtyp: { clrtyp(); } ; deftyp: /* empty */ { deftyp(); } ; declspecs: clrtyp_typespec { addtype($1); } | declmods typespec { addtype($2); } | 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: 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_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)); } | { symtyp = FVFT; } T_COLON constant { $$ = bitfield(NULL, toicon($3)); } ; type_member_decl: type_decl { $$ = $1; } | type_decl T_COLON constant { $$ = bitfield($1, toicon($3)); } | { symtyp = FVFT; } T_COLON constant { $$ = bitfield(NULL, toicon($3)); } ; 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 */ warning(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), 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_spec { idecl($1, 0); chksz($1); } | notype_decl opt_asm_spec { idecl($1, 1); } T_ASSIGN initializer { chksz($1); } ; type_init_decl: type_decl opt_asm_spec { idecl($1, 0); chksz($1); } | type_decl opt_asm_spec { idecl($1, 1); } 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)); } | notype_direct_decl param_list { $$ = addfunc($1, $2); popdecl(); blklev--; } ; 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)); } | type_direct_decl param_list { $$ = addfunc($1, $2); popdecl(); blklev--; } ; /* * 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)); } | 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)); } | 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 opt_asm_spec { $$ = $1; } | parameter_type_list T_COMMA parameter_declaration opt_asm_spec { $$ = 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_spec: /* empty */ | T_ASM T_LPARN T_STRING T_RPARN { freeyyv(&$3, T_STRING); } ; initializer: init_expr ; init_expr: expr %prec T_COMMA { mkinit($1); } | 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 ; 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)); } | direct_abs_decl T_LBRACK T_RBRACK { $$ = addarray($1, 0, 0); } | direct_abs_decl T_LBRACK constant T_RBRACK { $$ = addarray($1, 1, toicon($3)); } | abs_decl_param_list { $$ = addfunc(aname(), $1); popdecl(); blklev--; } | direct_abs_decl abs_decl_param_list { $$ = addfunc($1, $2); popdecl(); blklev--; } ; stmnt: labeled_stmnt | expr_stmnt | comp_stmnt | selection_stmnt | iteration_stmnt | jump_stmnt { ftflg = 0; } | asm_stmnt ; labeled_stmnt: label stmnt ; label: identifier T_COLON { symtyp = FLAB; label(T_NAME, getsym($1), NULL); } | T_CASE constant T_COLON { label(T_CASE, NULL, $2); ftflg = 1; } | T_DEFAULT T_COLON { label(T_DEFAULT, NULL, NULL); ftflg = 1; } ; comp_stmnt: compstmnt_lbrace declaration_list opt_stmnt_list compstmnt_rbrace | compstmnt_lbrace opt_stmnt_list compstmnt_rbrace ; compstmnt_lbrace: T_LBRACE { blklev++; mblklev++; pushdecl(AUTO); } ; compstmnt_rbrace: T_RBRACE { popdecl(); freeblk(); mblklev--; blklev--; ftflg = 0; } ; opt_stmnt_list: /* empty */ | stmnt_list ; stmnt_list: stmnt { clrwflgs(); } | stmnt_list stmnt { clrwflgs(); } | stmnt_list error T_SEMI { clrwflgs(); } ; expr_stmnt: expr T_SEMI { expr($1, 0, 0); ftflg = 0; } | T_SEMI { ftflg = 0; } ; selection_stmnt: if_without_else { if2(); if3(0); } | if_without_else T_ELSE { if2(); } stmnt { if3(1); } | if_without_else T_ELSE error { if3(0); } | switch_expr stmnt { switch2(); } | switch_expr error { switch2(); } ; if_without_else: if_expr stmnt | if_expr error ; if_expr: T_IF T_LPARN expr T_RPARN { if1($3); clrwflgs(); } ; switch_expr: T_SWITCH T_LPARN expr T_RPARN { switch1($3); clrwflgs(); } ; iteration_stmnt: while_expr stmnt { while2(); } | while_expr error { while2(); } | do stmnt do_while_expr { do2($3); ftflg = 0; } | do error { do2(NULL); } | for_exprs stmnt { for2(); } | for_exprs error { for2(); } ; while_expr: T_WHILE T_LPARN expr T_RPARN { while1($3); clrwflgs(); } ; do: T_DO { do1(); } ; do_while_expr: T_WHILE T_LPARN expr T_RPARN T_SEMI { $$ = $3; } ; for_exprs: T_FOR T_LPARN opt_expr T_SEMI opt_expr T_SEMI opt_expr T_RPARN { for1($3, $5, $7); clrwflgs(); } ; 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(); } | declaration_list declaration { clrwflgs(); } ; 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 realy neccessary? */ 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; } | 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_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_LPARN type_name T_RPARN term %prec T_UNOP { $$ = cast($4, $2); } ; 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; } ; identifier: T_NAME { $$ = $1; } | T_TYPENAME { $$ = $1; } ; %% /* ARGSUSED */ int yyerror(msg) char *msg; { error(249); if (++sytxerr >= 5) norecover(); return (0); } /* * 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(tn) tnode_t *tn; { int i; tspec_t t; val_t *v; v = constant(tn); /* * 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 ((u_quad_t)v->v_quad > UINT_MAX) { /* integral constant too large */ warning(56); } } else { #ifdef XXX_BROKEN_GCC if (v->v_quad > INT_MAX) { /* integral constant too large */ warning(56); } if (v->v_quad < INT_MIN) { /* integral constant too large */ warning(56); } #else if (v->v_quad > INT_MAX || v->v_quad < INT_MIN) { /* integral constant too large */ warning(56); } #endif } } free(v); return (i); } static void idecl(decl, initflg) sym_t *decl; int initflg; { initerr = 0; initsym = decl; switch (dcs->d_ctx) { case EXTERN: decl1ext(decl, initflg); break; case ARG: (void)decl1arg(decl, initflg); break; case AUTO: decl1loc(decl, initflg); break; default: lerror("idecl()"); } if (initflg && !initerr) prepinit(); } /* * Discard all input tokens up to and including the next * unmatched right paren */ void ignuptorp() { 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; }