diff options
Diffstat (limited to 'usr.bin/pcc/cc/ccom/cgram.y')
-rw-r--r-- | usr.bin/pcc/cc/ccom/cgram.y | 1399 |
1 files changed, 1399 insertions, 0 deletions
diff --git a/usr.bin/pcc/cc/ccom/cgram.y b/usr.bin/pcc/cc/ccom/cgram.y new file mode 100644 index 00000000000..3393bf6e13b --- /dev/null +++ b/usr.bin/pcc/cc/ccom/cgram.y @@ -0,0 +1,1399 @@ +/* $Id: cgram.y,v 1.1 2007/09/15 18:12:33 otto Exp $ */ + +/* + * Copyright (c) 2003 Anders Magnusson (ragge@ludd.luth.se). + * 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. 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. + */ + +/* + * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code and documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 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. + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed or owned by Caldera + * International, Inc. + * Neither the name of Caldera International, Inc. nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA + * INTERNATIONAL, INC. AND CONTRIBUTORS ``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 CALDERA INTERNATIONAL, INC. 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 OFLIABILITY, 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. + */ + +/* + * Comments for this grammar file. Ragge 021123 + * + * ANSI support required rewrite of the function header and declaration + * rules almost totally. + * + * The lex/yacc shared keywords are now split from the keywords used + * in the rest of the compiler, to simplify use of other frontends. + */ + +/* + * At last count, there were 3 shift/reduce and no reduce/reduce conflicts + * Two was funct_idn and the third was "dangling else". + */ + +/* + * Token used in C lex/yacc communications. + */ +%token C_WSTRING /* a wide string constant */ +%token C_STRING /* a string constant */ +%token C_ICON /* an integer constant */ +%token C_FCON /* a floating point constant */ +%token C_NAME /* an identifier */ +%token C_TYPENAME /* a typedef'd name */ +%token C_ANDAND /* && */ +%token C_OROR /* || */ +%token C_GOTO /* unconditional goto */ +%token C_RETURN /* return from function */ +%token C_TYPE /* a type */ +%token C_CLASS /* a storage class */ +%token C_ASOP /* assignment ops */ +%token C_RELOP /* <=, <, >=, > */ +%token C_EQUOP /* ==, != */ +%token C_DIVOP /* /, % */ +%token C_SHIFTOP /* <<, >> */ +%token C_INCOP /* ++, -- */ +%token C_UNOP /* !, ~ */ +%token C_STROP /* ., -> */ +%token C_STRUCT +%token C_IF +%token C_ELSE +%token C_SWITCH +%token C_BREAK +%token C_CONTINUE +%token C_WHILE +%token C_DO +%token C_FOR +%token C_DEFAULT +%token C_CASE +%token C_SIZEOF +%token C_ENUM +%token C_ELLIPSIS +%token C_QUALIFIER +%token C_FUNSPEC +%token C_ASM + +/* + * Precedence + */ +%left ',' +%right '=' C_ASOP +%right '?' ':' +%left C_OROR +%left C_ANDAND +%left '|' +%left '^' +%left '&' +%left C_EQUOP +%left C_RELOP +%left C_SHIFTOP +%left '+' '-' +%left '*' C_DIVOP +%right C_UNOP +%right C_INCOP C_SIZEOF +%left '[' '(' C_STROP +%{ +# include "pass1.h" +# include <stdarg.h> +# include <string.h> + +static int fun_inline; /* Reading an inline function */ +int oldstyle; /* Current function being defined */ +int noretype; +static struct symtab *xnf; +#ifdef GCC_COMPAT +char *renname; /* for renaming of variables */ +#endif + + +static NODE *bdty(int op, ...); +static void fend(void); +static void fundef(NODE *tp, NODE *p); +static void olddecl(NODE *p); +static struct symtab *init_declarator(NODE *tn, NODE *p, int assign); +static void resetbc(int mask); +static void swend(void); +static void addcase(NODE *p); +static void adddef(void); +static void savebc(void); +static void swstart(int); +static NODE * structref(NODE *p, int f, char *name); +static char *mkpstr(char *str); +static struct symtab *clbrace(NODE *); + +/* + * State for saving current switch state (when nested switches). + */ +struct savbc { + struct savbc *next; + int brklab; + int contlab; + int flostat; + int swx; +} *savbc, *savctx; + +%} + +%union { + int intval; + NODE *nodep; + struct symtab *symp; + struct rstack *rp; + char *strp; +} + + /* define types */ +%start ext_def_list + +%type <intval> con_e ifelprefix ifprefix whprefix forprefix doprefix switchpart + type_qualifier_list +%type <nodep> e .e term enum_dcl struct_dcl cast_type funct_idn declarator + direct_declarator elist type_specifier merge_attribs + parameter_declaration abstract_declarator initializer + parameter_type_list parameter_list addrlbl + declaration_specifiers pointer direct_abstract_declarator + specifier_qualifier_list merge_specifiers nocon_e + identifier_list arg_param_list arg_declaration arg_dcl_list + designator_list designator +%type <strp> string wstring C_STRING C_WSTRING +%type <rp> enum_head str_head +%type <symp> xnfdeclarator clbrace + +%type <intval> C_CLASS C_STRUCT C_RELOP C_DIVOP C_SHIFTOP + C_ANDAND C_OROR C_STROP C_INCOP C_UNOP C_ASOP C_EQUOP +%type <nodep> C_TYPE C_QUALIFIER C_ICON C_FCON +%type <strp> C_NAME C_TYPENAME + +%% + +ext_def_list: ext_def_list external_def + | { ftnend(); } + ; + +external_def: function_definition { blevel = 0; } + | declaration { blevel = 0; symclear(0); } + | asmstatement ';' + | ';' + | error { blevel = 0; } + ; + +function_definition: + /* Ansi (or K&R header without parameter types) */ + declaration_specifiers declarator { + fundef($1, $2); + } compoundstmt { fend(); } + /* Same as above but without declaring function type */ + | declarator { + noretype = 1; + fundef(mkty(INT, 0, MKSUE(INT)), $1); + } compoundstmt { fend(); noretype = 0; } + /* K&R function without type declaration */ + | declarator { + noretype = 1; + if (oldstyle == 0) + uerror("bad declaration in ansi function"); + fundef(mkty(INT, 0, MKSUE(INT)), $1); + } arg_dcl_list compoundstmt { fend(); noretype = 0; } + /* K&R function with type declaration */ + | declaration_specifiers declarator { + if (oldstyle == 0) + uerror("bad declaration in ansi function"); + fundef($1, $2); + } arg_dcl_list compoundstmt { fend(); } + ; + +/* + * Returns a node pointer or NULL, if no types at all given. + * Type trees are checked for correctness and merged into one + * type node in typenode(). + */ +declaration_specifiers: + merge_attribs { $$ = typenode($1); } + ; + +merge_attribs: C_CLASS { $$ = block(CLASS, NIL, NIL, $1, 0, 0); } + | C_CLASS merge_attribs { $$ = block(CLASS, $2, NIL, $1,0,0);} + | type_specifier { $$ = $1; } + | type_specifier merge_attribs { $1->n_left = $2; $$ = $1; } + | C_QUALIFIER { $$ = $1; } + | C_QUALIFIER merge_attribs { $1->n_left = $2; $$ = $1; } + | function_specifiers { $$ = NIL; } + | function_specifiers merge_attribs { $$ = $2; } + ; + +function_specifiers: + C_FUNSPEC { + if (fun_inline) + uerror("too many inline"); + fun_inline = 1; + } + ; + +type_specifier: C_TYPE { $$ = $1; } + | C_TYPENAME { + struct symtab *sp = lookup($1, 0); + $$ = mkty(sp->stype, sp->sdf, sp->ssue); + $$->n_sp = sp; + } + | struct_dcl { $$ = $1; } + | enum_dcl { $$ = $1; } + ; + +/* + * Adds a pointer list to front of the declarators. + * Note the UMUL right node pointer usage. + */ +declarator: pointer direct_declarator { + $$ = $1; $1->n_right->n_left = $2; + } + | direct_declarator { $$ = $1; } + ; + +/* + * Return an UMUL node type linked list of indirections. + */ +pointer: '*' { $$ = bdty(UMUL, NIL); $$->n_right = $$; } + | '*' type_qualifier_list { + $$ = bdty(UMUL, NIL); + $$->n_qual = $2; + $$->n_right = $$; + } + | '*' pointer { + $$ = bdty(UMUL, $2); + $$->n_right = $2->n_right; + } + | '*' type_qualifier_list pointer { + $$ = bdty(UMUL, $3); + $$->n_qual = $2; + $$->n_right = $3->n_right; + } + ; + +type_qualifier_list: + C_QUALIFIER { $$ = $1->n_type; nfree($1); } + | type_qualifier_list C_QUALIFIER { + $$ = $1 | $2->n_type; nfree($2); + } + ; + +/* + * Sets up a function declarator. The call node will have its parameters + * connected to its right node pointer. + */ +direct_declarator: C_NAME { $$ = bdty(NAME, $1); } + | '(' declarator ')' { $$ = $2; } + | direct_declarator '[' nocon_e ']' { + $$ = block(LB, $1, $3, INT, 0, MKSUE(INT)); + } + | direct_declarator '[' ']' { $$ = bdty(LB, $1, 0); } + | direct_declarator '(' notype parameter_type_list ')' { + $$ = bdty(CALL, $1, $4); + } + | direct_declarator '(' notype identifier_list ')' { + $$ = bdty(CALL, $1, $4); + if (blevel != 0) + uerror("function declaration in bad context"); + oldstyle = 1; + } + | direct_declarator '(' ')' { $$ = bdty(UCALL, $1); } + ; + +notype: { /* extern int notype, doproto; notype = 0; doproto=1; printf("notype\n"); */ } + ; + +identifier_list: C_NAME { $$ = bdty(NAME, $1); $$->n_type = FARG; } + | identifier_list ',' C_NAME { + $$ = bdty(NAME, $3); + $$->n_type = FARG; + $$ = block(CM, $1, $$, 0, 0, 0); + } + ; + +/* + * Returns as parameter_list, but can add an additional ELLIPSIS node. + * Calls revert() to get the parameter list in the forward order. + */ +parameter_type_list: + parameter_list { $$ = $1; } + | parameter_list ',' C_ELLIPSIS { + $$ = block(CM, $1, block(ELLIPSIS, NIL, NIL, 0, 0, 0), + 0, 0, 0); + } + ; + +/* + * Returns a linked lists of nodes of op CM with parameters on + * its right and additional CM nodes of its left pointer. + * No CM nodes if only one parameter. + */ +parameter_list: parameter_declaration { $$ = $1; } + | parameter_list ',' parameter_declaration { + $$ = block(CM, $1, $3, 0, 0, 0); + } + ; + +/* + * Returns a node pointer to the declaration. + */ +parameter_declaration: + declaration_specifiers declarator { + $$ = tymerge($1, $2); + nfree($1); + } + | declaration_specifiers abstract_declarator { + $$ = tymerge($1, $2); + nfree($1); + } + | declaration_specifiers { + $$ = tymerge($1, bdty(NAME, NULL)); + nfree($1); + } + ; + +abstract_declarator: + pointer { $$ = $1; $1->n_right->n_left = bdty(NAME, NULL); } + | direct_abstract_declarator { $$ = $1; } + | pointer direct_abstract_declarator { + $$ = $1; $1->n_right->n_left = $2; + } + ; + +direct_abstract_declarator: + '(' abstract_declarator ')' { $$ = $2; } + | '[' ']' { $$ = bdty(LB, bdty(NAME, NULL), 0); } + | '[' con_e ']' { $$ = bdty(LB, bdty(NAME, NULL), $2); } + | direct_abstract_declarator '[' ']' { $$ = bdty(LB, $1, 0); } + | direct_abstract_declarator '[' con_e ']' { + $$ = bdty(LB, $1, $3); + } + | '(' ')' { $$ = bdty(UCALL, bdty(NAME, NULL)); } + | '(' notype parameter_type_list ')' { + $$ = bdty(CALL, bdty(NAME, NULL), $3); + } + | direct_abstract_declarator '(' ')' { + $$ = bdty(UCALL, $1); + } + | direct_abstract_declarator '(' notype parameter_type_list ')' { + $$ = bdty(CALL, $1, $4); + } + ; + +/* + * K&R arg declaration, between ) and { + */ +arg_dcl_list: arg_declaration + | arg_dcl_list arg_declaration + ; + + +arg_declaration: declaration_specifiers arg_param_list ';' { + nfree($1); + } + ; + +arg_param_list: declarator { olddecl(tymerge($<nodep>0, $1)); } + | arg_param_list ',' declarator { + olddecl(tymerge($<nodep>0, $3)); + } + ; + +/* + * Declarations in beginning of blocks. + */ +declaration_list: declaration + | declaration_list declaration + ; + +/* + * Here starts the old YACC code. + */ + +stmt_list: stmt_list statement + | { bccode(); } + ; + +/* + * Variables are declared in init_declarator. + */ +declaration: declaration_specifiers ';' { nfree($1); goto inl; } + | declaration_specifiers init_declarator_list ';' { + nfree($1); + inl: + fun_inline = 0; + } + ; + +/* + * Normal declaration of variables. curtype contains the current type node. + * Returns nothing, variables are declared in init_declarator. + */ +init_declarator_list: + init_declarator + | init_declarator_list ',' { $<nodep>$ = $<nodep>0; } init_declarator + ; + +enum_dcl: enum_head '{' moe_list optcomma '}' { $$ = dclstruct($1); } + | C_ENUM C_NAME { $$ = rstruct($2,0); } + | C_ENUM C_TYPENAME { $$ = rstruct($2,0); } + ; + +enum_head: C_ENUM { $$ = bstruct(NULL,0); } + | C_ENUM C_NAME { $$ = bstruct($2,0); } + | C_ENUM C_TYPENAME { $$ = bstruct($2,0); } + ; + +moe_list: moe + | moe_list ',' moe + ; + +moe: C_NAME { moedef( $1 ); } + | C_NAME '=' con_e { strucoff = $3; moedef( $1 ); } + ; + +struct_dcl: str_head '{' struct_dcl_list '}' { $$ = dclstruct($1); } + | C_STRUCT C_NAME { $$ = rstruct($2,$1); } + | C_STRUCT C_TYPENAME { $$ = rstruct($2,$1); } + | str_head '{' '}' { +#ifndef GCC_COMPAT + werror("gcc extension"); +#endif + $$ = dclstruct($1); + } + ; + +str_head: C_STRUCT { $$ = bstruct(NULL, $1); } + | C_STRUCT C_NAME { $$ = bstruct($2,$1); } + | C_STRUCT C_TYPENAME { $$ = bstruct($2,$1); } + ; + +struct_dcl_list: struct_declaration + | struct_dcl_list struct_declaration + ; + +struct_declaration: + specifier_qualifier_list struct_declarator_list ';' { + nfree($1); + } + ; + +specifier_qualifier_list: + merge_specifiers { $$ = typenode($1); } + ; + +merge_specifiers: type_specifier merge_specifiers { $1->n_left = $2;$$ = $1; } + | type_specifier { $$ = $1; } + | C_QUALIFIER merge_specifiers { $1->n_left = $2; $$ = $1; } + | C_QUALIFIER { $$ = $1; } + ; + +struct_declarator_list: + struct_declarator { } + | struct_declarator_list ',' { $<nodep>$=$<nodep>0; } + struct_declarator { } + ; + +struct_declarator: declarator { + tymerge($<nodep>0, $1); + $1->n_sp = getsymtab((char *)$1->n_sp, SMOSNAME); /* XXX */ + defid($1, $<nodep>0->n_lval); + nfree($1); + } + | ':' con_e { + if (!(instruct&INSTRUCT)) + uerror( "field outside of structure" ); + falloc(NULL, $2, -1, $<nodep>0); + } + | declarator ':' con_e { + if (!(instruct&INSTRUCT)) + uerror( "field outside of structure" ); + if( $3<0 || $3 >= FIELD ){ + uerror( "illegal field size" ); + $3 = 1; + } + if ($1->n_op == NAME) { + tymerge($<nodep>0, $1); + $1->n_sp = getsymtab((char *)$1->n_sp,SMOSNAME); + defid($1, FIELD|$3); + nfree($1); + } else + uerror("illegal declarator"); + } + ; + + /* always preceeded by attributes */ +xnfdeclarator: declarator { $$ = xnf = init_declarator($<nodep>0, $1, 1); } + ; + +/* + * Handles declarations and assignments. + * Returns nothing. + */ +init_declarator: declarator { init_declarator($<nodep>0, $1, 0); } + | declarator C_ASM '(' string ')' { +#ifdef GCC_COMPAT + renname = $4; + init_declarator($<nodep>0, $1, 0); +#else + werror("gcc extension"); + init_declarator($<nodep>0, $1, 0); +#endif + } + | xnfdeclarator '=' e { simpleinit($1, $3); xnf = NULL; } + | xnfdeclarator '=' begbr init_list optcomma '}' { + endinit(); + xnf = NULL; + } + | xnfdeclarator '=' addrlbl { simpleinit($1, $3); xnf = NULL; } + ; + +begbr: '{' { beginit($<symp>-1); } + ; + +initializer: e %prec ',' { $$ = $1; } + | addrlbl { $$ = $1; } + | ibrace init_list optcomma '}' { $$ = NULL; } + ; + +init_list: designation initializer { asginit($2); } + | init_list ',' designation initializer { asginit($4); } + ; + +designation: designator_list '=' { desinit($1); } + | { /* empty */ } + ; + +designator_list: designator { $$ = $1; } + | designator_list designator { $$ = $2; $$->n_left = $1; } + ; + +designator: '[' con_e ']' { $$ = bdty(LB, NULL, $2); } + | C_STROP C_NAME { $$ = bdty(NAME, $2); } + ; + +optcomma : /* VOID */ + | ',' + ; + +ibrace: '{' { ilbrace(); } + ; + +/* STATEMENTS */ + +compoundstmt: begin declaration_list stmt_list '}' { +#ifdef STABS + if (gflag && blevel > 2) + stabs_rbrac(blevel); +#endif + --blevel; + if( blevel == 1 ) + blevel = 0; + symclear(blevel); /* Clean ut the symbol table */ + if (autooff > maxautooff) + maxautooff = autooff; + autooff = savctx->contlab; + savctx = savctx->next; + } + | begin stmt_list '}' { +#ifdef STABS + if (gflag && blevel > 2) + stabs_rbrac(blevel); +#endif + --blevel; + if( blevel == 1 ) + blevel = 0; + symclear(blevel); /* Clean ut the symbol table */ + if (autooff > maxautooff) + maxautooff = autooff; + autooff = savctx->contlab; + savctx = savctx->next; + } + ; + +begin: '{' { + struct savbc *bc = tmpalloc(sizeof(struct savbc)); + if (blevel == 1) { +#ifdef STABS + if (gflag) + stabs_line(lineno); +#endif + dclargs(); + } +#ifdef STABS + if (gflag && blevel > 1) + stabs_lbrac(blevel+1); +#endif + ++blevel; + oldstyle = 0; + bc->contlab = autooff; + bc->next = savctx; + savctx = bc; + } + ; + +statement: e ';' { ecomp( $1 ); } + | compoundstmt + | ifprefix statement { plabel($1); reached = 1; } + | ifelprefix statement { + if ($1 != NOLAB) { + plabel( $1); + reached = 1; + } + } + | whprefix statement { + branch(contlab); + plabel( brklab ); + if( (flostat&FBRK) || !(flostat&FLOOP)) + reached = 1; + else + reached = 0; + resetbc(0); + } + | doprefix statement C_WHILE '(' e ')' ';' { + plabel(contlab); + if (flostat & FCONT) + reached = 1; + if (reached) + cbranch($5, bcon($1)); + else + tfree($5); + plabel( brklab); + reached = 1; + resetbc(0); + } + | forprefix .e ')' statement + { plabel( contlab ); + if( flostat&FCONT ) reached = 1; + if( $2 ) ecomp( $2 ); + branch($1); + plabel( brklab ); + if( (flostat&FBRK) || !(flostat&FLOOP) ) reached = 1; + else reached = 0; + resetbc(0); + } + | switchpart statement + { if( reached ) branch( brklab ); + plabel( $1 ); + swend(); + plabel( brklab); + if( (flostat&FBRK) || !(flostat&FDEF) ) reached = 1; + resetbc(FCONT); + } + | C_BREAK ';' { + if (brklab == NOLAB) + uerror("illegal break"); + else if (reached) + branch(brklab); + flostat |= FBRK; + reached = 0; + } + | C_CONTINUE ';' { + if (contlab == NOLAB) + uerror("illegal continue"); + else + branch(contlab); + flostat |= FCONT; + goto rch; + } + | C_RETURN ';' { + branch(retlab); + if (cftnsp->stype != VOID && noretype && + cftnsp->stype != VOID+FTN) + uerror("return value required"); + rch: + if (!reached) + werror( "statement is not reached"); + reached = 0; + } + | C_RETURN e ';' { + register NODE *temp; + + spname = cftnsp; + temp = buildtree( NAME, NIL, NIL ); + temp->n_type = DECREF(temp->n_type); + temp = buildtree(RETURN, temp, $2); + + if (temp->n_type == VOID) + ecomp(temp->n_right); + else + ecomp(buildtree(FORCE, temp->n_right, NIL)); + nfree(temp->n_left); + nfree(temp); + branch(retlab); + reached = 0; + } + | C_GOTO C_NAME ';' { gotolabel($2); goto rch; } + | C_GOTO '*' e ';' { + ecomp(block(GOTO, $3, NIL, INT, 0, 0)); + } + | asmstatement ';' + | ';' + | error ';' + | error '}' + | label statement + ; + +asmstatement: C_ASM '(' string ')' { send_passt(IP_ASM, mkpstr($3)); } + ; + +label: C_NAME ':' { deflabel($1); reached = 1; } + | C_CASE e ':' { addcase($2); reached = 1; } + | C_DEFAULT ':' { reached = 1; adddef(); flostat |= FDEF; } + ; + +doprefix: C_DO { + savebc(); + if (!reached) + werror("loop not entered at top"); + brklab = getlab(); + contlab = getlab(); + plabel( $$ = getlab()); + reached = 1; + } + ; +ifprefix: C_IF '(' e ')' { + cbranch(buildtree(NOT, $3, NIL), bcon($$ = getlab())); + reached = 1; + } + ; +ifelprefix: ifprefix statement C_ELSE { + if (reached) + branch($$ = getlab()); + else + $$ = NOLAB; + plabel( $1); + reached = 1; + } + ; + +whprefix: C_WHILE '(' e ')' { + savebc(); + if (!reached) + werror("loop not entered at top"); + if ($3->n_op == ICON && $3->n_lval != 0) + flostat = FLOOP; + plabel( contlab = getlab()); + reached = 1; + brklab = getlab(); + if (flostat == FLOOP) + tfree($3); + else + cbranch(buildtree(NOT, $3, NIL), bcon(brklab)); + } + ; +forprefix: C_FOR '(' .e ';' .e ';' { + if ($3) + ecomp($3); + else if (!reached) + werror("loop not entered at top"); + savebc(); + contlab = getlab(); + brklab = getlab(); + plabel( $$ = getlab()); + reached = 1; + if ($5) + cbranch(buildtree(NOT, $5, NIL), bcon(brklab)); + else + flostat |= FLOOP; + } + ; +switchpart: C_SWITCH '(' e ')' { + NODE *p; + int num; + + savebc(); + brklab = getlab(); + if ($3->n_type != INT) { + /* must cast to integer */ + p = block(NAME, NIL, NIL, INT, 0, MKSUE(INT)); + p = buildtree(CAST, p, $3); + $3 = p->n_right; + nfree(p->n_left); + nfree(p); + } +// ecomp( buildtree( FORCE, $3, NIL ) ); + p = tempnode(0, INT, 0, MKSUE(INT)); + num = p->n_lval; + ecomp(buildtree(ASSIGN, p, $3)); + branch( $$ = getlab()); + swstart(num); + reached = 0; + } + ; +/* EXPRESSIONS */ +con_e: { $<intval>$=instruct; instruct=0; } e %prec ',' { + $$ = icons( $2 ); + instruct=$<intval>1; + } + ; + +nocon_e: { $<intval>$=instruct; instruct=0; } e %prec ',' { + instruct=$<intval>1; + $$ = $2; + } + ; + +.e: e + | { $$=0; } + ; + +elist: e %prec ',' + | elist ',' e { $$ = buildtree(CM, $1, $3); } + ; + +/* + * Precedence order of operators. + */ +e: e ',' e { $$ = buildtree(COMOP, $1, $3); } + | e '=' e { $$ = buildtree(ASSIGN, $1, $3); } + | e C_ASOP e { $$ = buildtree($2, $1, $3); } + | e '?' e ':' e { + $$=buildtree(QUEST, $1, buildtree(COLON, $3, $5)); + } + | e C_OROR e { $$ = buildtree($2, $1, $3); } + | e C_ANDAND e { $$ = buildtree($2, $1, $3); } + | e '|' e { $$ = buildtree(OR, $1, $3); } + | e '^' e { $$ = buildtree(ER, $1, $3); } + | e '&' e { $$ = buildtree(AND, $1, $3); } + | e C_EQUOP e { $$ = buildtree($2, $1, $3); } + | e C_RELOP e { $$ = buildtree($2, $1, $3); } + | e C_SHIFTOP e { $$ = buildtree($2, $1, $3); } + | e '+' e { $$ = buildtree(PLUS, $1, $3); } + | e '-' e { $$ = buildtree(MINUS, $1, $3); } + | e C_DIVOP e { $$ = buildtree($2, $1, $3); } + | e '*' e { $$ = buildtree(MUL, $1, $3); } + | e '=' addrlbl { $$ = buildtree(ASSIGN, $1, $3); } + | term + ; + +addrlbl: C_ANDAND C_NAME { +#ifdef GCC_COMPAT + struct symtab *s = lookup($2, SLBLNAME); + if (s->soffset == 0) + s->soffset = -getlab(); + spname = s; + $$ = buildtree(ADDROF, buildtree(NAME, NIL, NIL), NIL); +#else + uerror("gcc extension"); +#endif + } + ; + +term: term C_INCOP { $$ = buildtree( $2, $1, bcon(1) ); } + | '*' term { $$ = buildtree(UMUL, $2, NIL); } + | '&' term { + if( ISFTN($2->n_type) || ISARY($2->n_type) ){ +#ifdef notdef + werror( "& before array or function: ignored" ); +#endif + $$ = $2; + } else + $$ = buildtree(ADDROF, $2, NIL); + } + | '-' term { $$ = buildtree(UMINUS, $2, NIL ); } + | '+' term { $$ = $2; } + | C_UNOP term { $$ = buildtree( $1, $2, NIL ); } + | C_INCOP term { + $$ = buildtree($1 == INCR ? PLUSEQ : MINUSEQ, + $2, bcon(1)); + } + | C_SIZEOF term { $$ = doszof($2); } + | '(' cast_type ')' term %prec C_INCOP { + $$ = buildtree(CAST, $2, $4); + nfree($$->n_left); + nfree($$); + $$ = $$->n_right; + } + | C_SIZEOF '(' cast_type ')' %prec C_SIZEOF { + $$ = doszof($3); + } + | '(' cast_type ')' clbrace init_list '}' { + endinit(); + spname = $4; + $$ = buildtree(NAME, NIL, NIL); + } + | term '[' e ']' { + $$ = buildtree( UMUL, + buildtree( PLUS, $1, $3 ), NIL ); + } + | funct_idn ')' { $$ = doacall($1, NIL); } + | funct_idn elist ')' { $$ = doacall($1, $2); } + | term C_STROP C_NAME { $$ = structref($1, $2, $3); } + | term C_STROP C_TYPENAME { $$ = structref($1, $2, $3); } + | C_NAME { + spname = lookup($1, 0); + /* recognize identifiers in initializations */ + if (blevel==0 && spname->stype == UNDEF) { + register NODE *q; + werror("undeclared initializer name %s", + spname->sname); + q = block(NAME, NIL, NIL, INT, 0, MKSUE(INT)); + q->n_sp = spname; + defid(q, EXTERN); + nfree(q); + } + if (spname->sflags & SINLINE) + inline_ref($1); + $$ = buildtree(NAME, NIL, NIL); + spname->suse = -lineno; + if (spname->sflags & SDYNARRAY) + $$ = buildtree(UMUL, $$, NIL); + } + | C_ICON { $$ = $1; } + | C_FCON { $$ = $1; } + | string { $$ = strend($1); /* get string contents */ } + | wstring { $$ = wstrend($1); } + | '(' e ')' { $$=$2; } + ; + +clbrace: '{' { $$ = clbrace($<nodep>-1); } + ; + +string: C_STRING { + int len = strlen($1) + 1; + $$ = tmpalloc(len); + strlcpy($$, $1, len); + } + | string C_STRING { + int len = strlen($1) + strlen($2) + 1; + $$ = tmpalloc(len); + strlcpy($$, $1, len); + strlcat($$, $2, len); + } + ; + +wstring: C_WSTRING { + int len = strlen($1) + 1; + $$ = tmpalloc(len); + strlcpy($$, $1, len); + } + | string C_WSTRING { + int len = strlen($1) + strlen($2) + 1; + $$ = tmpalloc(len); + strlcpy($$, $1, len); + strlcat($$, $2, len); + } + ; + +cast_type: specifier_qualifier_list { + $$ = tymerge($1, bdty(NAME, NULL)); + nfree($1); + } + | specifier_qualifier_list abstract_declarator { + $$ = tymerge($1, $2); + nfree($1); + } + ; + +funct_idn: C_NAME '(' { + struct symtab *s = lookup($1, 0); + if (s->stype == UNDEF) { + register NODE *q; + q = block(NAME, NIL, NIL, FTN|INT, 0, MKSUE(INT)); + q->n_sp = s; + defid(q, EXTERN); + nfree(q); + } + if (s->sflags & SINLINE) + inline_ref($1); + spname = s; + $$ = buildtree(NAME, NIL, NIL); + s->suse = -lineno; + } + | term '(' + ; +%% + +NODE * +mkty(TWORD t, union dimfun *d, struct suedef *sue) +{ + return block(TYPE, NIL, NIL, t, d, sue); +} + +static NODE * +bdty(int op, ...) +{ + va_list ap; + register NODE *q; + + va_start(ap, op); + q = block(op, NIL, NIL, INT, 0, MKSUE(INT)); + + switch (op) { + case UMUL: + case UCALL: + q->n_left = va_arg(ap, NODE *); + q->n_rval = 0; + break; + + case CALL: + q->n_left = va_arg(ap, NODE *); + q->n_right = va_arg(ap, NODE *); + break; + + case LB: + q->n_left = va_arg(ap, NODE *); + q->n_right = bcon(va_arg(ap, int)); + break; + + case NAME: + q->n_sp = va_arg(ap, struct symtab *); /* XXX survive tymerge */ + break; + + default: + cerror("bad bdty"); + } + va_end(ap); + + return q; +} + +static void +savebc(void) +{ + struct savbc *bc = tmpalloc(sizeof(struct savbc)); + + bc->brklab = brklab; + bc->contlab = contlab; + bc->flostat = flostat; + bc->next = savbc; + savbc = bc; + flostat = 0; +} + +static void +resetbc(int mask) +{ + flostat = savbc->flostat | (flostat&mask); + contlab = savbc->contlab; + brklab = savbc->brklab; + savbc = savbc->next; +} + +struct swdef { + struct swdef *next; /* Next in list */ + int deflbl; /* Label for "default" */ + struct swents *ents; /* Linked sorted list of case entries */ + int nents; /* # of entries in list */ + int num; /* Node value will end up in */ +} *swpole; + +/* + * add case to switch + */ +static void +addcase(NODE *p) +{ + struct swents *w, *sw = tmpalloc(sizeof(struct swents)); + + p = optim(p); /* change enum to ints */ + if (p->n_op != ICON || p->n_sp != NULL) { + uerror( "non-constant case expression"); + return; + } + if (swpole == NULL) { + uerror("case not in switch"); + return; + } + + sw->sval = p->n_lval; + plabel( sw->slab = getlab()); + w = swpole->ents; + if (swpole->ents == NULL) { + sw->next = NULL; + swpole->ents = sw; + } else if (swpole->ents->next == NULL) { + if (swpole->ents->sval == sw->sval) { + uerror("duplicate case in switch"); + } else if (swpole->ents->sval < sw->sval) { + sw->next = NULL; + swpole->ents->next = sw; + } else { + sw->next = swpole->ents; + swpole->ents = sw; + } + } else { + while (w->next->next != NULL && w->next->sval < sw->sval) { + w = w->next; + } + if (w->next->sval == sw->sval) { + uerror("duplicate case in switch"); + } else if (w->next->sval > sw->sval) { + sw->next = w->next; + w->next = sw; + } else { + sw->next = NULL; + w->next->next = sw; + } + } + swpole->nents++; + tfree(p); +} + +/* + * add default case to switch + */ +static void +adddef(void) +{ + if (swpole == NULL) + uerror("default not inside switch"); + else if (swpole->deflbl != 0) + uerror("duplicate default in switch"); + else + plabel( swpole->deflbl = getlab()); +} + +static void +swstart(int num) +{ + struct swdef *sw = tmpalloc(sizeof(struct swdef)); + + sw->deflbl = sw->nents = 0; + sw->ents = NULL; + sw->next = swpole; + sw->num = num; + swpole = sw; +} + +/* + * end a switch block + */ +static void +swend(void) +{ + struct swents *sw, **swp; + int i; + + sw = tmpalloc(sizeof(struct swents)); + swp = tmpalloc(sizeof(struct swents *) * (swpole->nents+1)); + + sw->slab = swpole->deflbl; + swp[0] = sw; + + for (i = 1; i <= swpole->nents; i++) { + swp[i] = swpole->ents; + swpole->ents = swpole->ents->next; + } + genswitch(swpole->num, swp, swpole->nents); + + swpole = swpole->next; +} + +/* + * Declare a variable or prototype. + */ +static struct symtab * +init_declarator(NODE *tn, NODE *p, int assign) +{ + int class = tn->n_lval; + NODE *typ; + + typ = tymerge(tn, p); + typ->n_sp = lookup((char *)typ->n_sp, 0); /* XXX */ + + if (fun_inline && ISFTN(typ->n_type)) + typ->n_sp->sflags |= SINLINE; + + if (ISFTN(typ->n_type) == 0) { + setloc1(DATA); + if (assign) { + defid(typ, class); + typ->n_sp->sflags |= SASG; + lcommdel(typ->n_sp); + } else { + nidcl(typ, class); + } + } else { + if (assign) + uerror("cannot initialise function"); + defid(typ, uclass(class)); + } + nfree(p); + return typ->n_sp; +} + +/* + * Declare a function. + */ +static void +fundef(NODE *tp, NODE *p) +{ + extern int prolab; + struct symtab *s; + int class = tp->n_lval, oclass; + char *c; + + setloc1(PROG); + /* Enter function args before they are clobbered in tymerge() */ + /* Typecheck against prototype will be done in defid(). */ + ftnarg(p); + + tymerge(tp, p); + s = p->n_sp = lookup((char *)p->n_sp, 0); /* XXX */ + + oclass = s->sclass; + if (class == STATIC && oclass == EXTERN) + werror("%s was first declared extern, then static", s->sname); + + if ((oclass == SNULL || oclass == USTATIC) && + class == STATIC && fun_inline) { + /* Unreferenced, store it for (eventual) later use */ + /* Ignore it if it not declared static */ + s->sflags |= SINLINE; + inline_start(s->sname); + } + if (class == EXTERN) + class = SNULL; /* same result */ + + cftnsp = s; + defid(p, class); + prolab = getlab(); + c = cftnsp->sname; +#ifdef GCC_COMPAT + c = gcc_findname(cftnsp); +#endif + send_passt(IP_PROLOG, -1, -1, c, cftnsp->stype, + cftnsp->sclass == EXTDEF, prolab); + blevel = 1; +#ifdef STABS + if (gflag) + stabs_func(s); +#endif + nfree(tp); + nfree(p); + +} + +static void +fend(void) +{ + if (blevel) + cerror("function level error"); + ftnend(); + fun_inline = 0; + cftnsp = NULL; +} + +static NODE * +structref(NODE *p, int f, char *name) +{ + NODE *r; + + if (f == DOT) + p = buildtree(ADDROF, p, NIL); + r = block(NAME, NIL, NIL, INT, 0, MKSUE(INT)); + r->n_name = name; + return buildtree(STREF, p, r); +} + +static void +olddecl(NODE *p) +{ + struct symtab *s; + + s = lookup((char *)p->n_sp, 0); + if (s->slevel != 1 || s->stype == UNDEF) + uerror("parameter '%s' not defined", s->sname); + else if (s->stype != FARG) + uerror("parameter '%s' redefined", s->sname); + s->stype = p->n_type; + s->sdf = p->n_df; + s->ssue = p->n_sue; + nfree(p); +} + +void +branch(int lbl) +{ + int r = reached++; + ecomp(block(GOTO, bcon(lbl), NIL, INT, 0, 0)); + reached = r; +} + +/* + * Create a printable string based on an encoded string. + */ +static char * +mkpstr(char *str) +{ + char *s, *os; + int v, l = strlen(str)+1; + + os = s = isinlining ? permalloc(l) : tmpalloc(l); + for (; *str; ) { + if (*str++ == '\\') + v = esccon(&str); + else + v = str[-1]; + *s++ = v; + } + *s = 0; + return os; +} + +static struct symtab * +clbrace(NODE *p) +{ + struct symtab *sp; + + if (blevel == 0 && xnf != NULL) + cerror("no level0 compound literals"); + + sp = getsymtab("cl", STEMP); + sp->stype = p->n_type; + sp->squal = p->n_qual; + sp->sdf = p->n_df; + sp->ssue = p->n_sue; + sp->sclass = blevel ? AUTO : STATIC; + if (!ISARY(sp->stype) || sp->sdf->ddim != 0) { + sp->soffset = NOOFFSET; + oalloc(sp, &autooff); + } + tfree(p); + beginit(sp); + return sp; +} |