%{ static char SCCSID[] = "~(#) cgram.y: 2.1 83/07/08"; %} %term NAME 2 %term STRING 3 %term ICON 4 %term FCON 5 %term PLUS 6 %term MINUS 8 %term MUL 11 %term AND 14 %term OR 17 %term ER 19 %term QUEST 21 %term COLON 22 %term ANDAND 23 %term OROR 24 /* special interfaces for yacc alone */ /* These serve as abbreviations of 2 or more ops: ASOP =, = ops RELOP LE,LT,GE,GT EQUOP EQ,NE DIVOP DIV,MOD SHIFTOP LS,RS ICOP ICR,DECR UNOP NOT,COMPL STROP DOT,STREF */ %term ASOP 25 %term RELOP 26 %term EQUOP 27 %term DIVOP 28 %term SHIFTOP 29 %term INCOP 30 %term UNOP 31 %term STROP 32 /* reserved words, etc */ %term TYPE 33 %term CLASS 34 %term STRUCT 35 %term RETURN 36 %term GOTO 37 %term IF 38 %term ELSE 39 %term SWITCH 40 %term BREAK 41 %term CONTINUE 42 %term WHILE 43 %term DO 44 %term FOR 45 %term DEFAULT 46 %term CASE 47 %term SIZEOF 48 %term ENUM 49 /* little symbols, etc. */ /* namely, LP ( RP ) LC { RC } LB [ RB ] CM , SM ; */ %term LP 50 %term RP 51 %term LC 52 %term RC 53 %term LB 54 %term RB 55 %term CM 56 %term SM 57 %term ASSIGN 58 %term ASM 59 /* at last count, there were 7 shift/reduce, 1 reduce/reduce conflicts /* these involved: if/else recognizing functions in various contexts, including declarations error recovery */ %left CM %right ASOP ASSIGN %right QUEST COLON %left OROR %left ANDAND %left OR %left ER %left AND %left EQUOP %left RELOP %left SHIFTOP %left PLUS MINUS %left MUL DIVOP %right UNOP %right INCOP SIZEOF %left LB LP STROP %{ # include "mfile1.h" %} /* define types */ %start ext_def_list %type con_e ifelprefix ifprefix whprefix forprefix doprefix switchpart enum_head str_head name_lp %type e .e term attributes oattributes type enum_dcl struct_dcl cast_type null_decl funct_idn declarator fdeclarator nfdeclarator elist %token CLASS NAME STRUCT RELOP CM DIVOP PLUS MINUS SHIFTOP MUL AND OR ER ANDAND OROR ASSIGN STROP INCOP UNOP ICON %token TYPE %% %{ static int fake = 0; static char fakename[NCHNAM+1]; %} ext_def_list: ext_def_list external_def | /* EMPTY */ { #ifndef LINT beg_file(); #endif ftnend(); } ; external_def: data_def { curclass = SNULL; blevel = 0; } | error { curclass = SNULL; blevel = 0; } | ASM SM { asmout(); curclass = SNULL; blevel = 0; } ; data_def: oattributes SM { $1->in.op = FREE; } | oattributes init_dcl_list SM { $1->in.op = FREE; } | oattributes fdeclarator { defid( tymerge($1,$2), curclass==STATIC?STATIC:EXTDEF ); } function_body { if( blevel ) cerror( "function level error" ); if( reached ) retstat |= NRETVAL; $1->in.op = FREE; ftnend(); } ; function_body: arg_dcl_list compoundstmt { regvar = 0; } /* clear out arguments */ ; arg_dcl_list: arg_dcl_list attributes declarator_list SM { curclass = SNULL; $2->in.op = FREE; } | arg_dcl_list attributes SM /* to permit structs in decl. lists */ { curclass = SNULL; $2->in.op = FREE; } | /* EMPTY */ { blevel = 1; } ; stmt_list: stmt_list statement | /* EMPTY */ { bccode(); locctr(PROG); } ; dcl_stat_list : dcl_stat_list attributes SM { $2->in.op = FREE; } | dcl_stat_list attributes init_dcl_list SM { $2->in.op = FREE; } | /* EMPTY */ ; oattributes: attributes | /* empty */ { $$ = mkty(INT,0,INT); curclass = SNULL; } ; attributes: class type { $$ = $2; } | type class | class { $$ = mkty(INT,0,INT); } | type { curclass = SNULL ; } | type class type { $1->in.type = types( $1->in.type, $3->in.type, UNDEF ); $3->in.op = FREE; } ; class: CLASS { curclass = $1; } ; type: TYPE | TYPE TYPE { $1->in.type = types( $1->in.type, $2->in.type, UNDEF ); $2->in.op = FREE; } | TYPE TYPE TYPE { $1->in.type = types( $1->in.type, $2->in.type, $3->in.type ); $2->in.op = $3->in.op = FREE; } | struct_dcl | enum_dcl ; enum_dcl: enum_head LC moe_list optcomma RC { $$ = dclstruct($1); } | ENUM NAME { $$ = rstruct($2,0); stwart = instruct; } ; enum_head: ENUM { $$ = bstruct(-1,0); stwart = SEENAME; } | ENUM NAME { $$ = bstruct($2,0); stwart = SEENAME; } ; moe_list: moe | moe_list CM moe ; moe: NAME { moedef( $1 ); } | NAME ASSIGN con_e { strucoff = $3; moedef( $1 ); } ; struct_dcl: str_head LC type_dcl_list optsemi RC { $$ = dclstruct($1); } | STRUCT NAME { $$ = rstruct($2,$1); } ; str_head: STRUCT { $$ = bstruct(-1,$1); stwart=0; } | STRUCT NAME { $$ = bstruct($2,$1); stwart=0; } ; type_dcl_list: type_declaration | type_dcl_list SM type_declaration ; type_declaration: type declarator_list { curclass = SNULL; stwart=0; $1->in.op = FREE; } | type { if( curclass != MOU ){ curclass = SNULL; } else { sprintf( fakename, "$%dFAKE", fake++ ); defid( tymerge($1, bdty(NAME,NIL, lookup( fakename, SMOS ))), curclass ); werror("union member must be named"); } stwart = 0; $1->in.op = FREE; } ; declarator_list: declarator { defid( tymerge($0,$1), curclass); stwart = instruct; } | declarator_list CM {$$=$0;} declarator { defid( tymerge($0,$4), curclass); stwart = instruct; } ; declarator: nfdeclarator | nfdeclarator COLON con_e %prec CM { if( !(instruct&INSTRUCT) ) uerror( "field outside of structure" ); if( $3<0 || $3 >= FIELD ){ uerror( "illegal field size" ); $3 = 1; } defid( tymerge($0,$1), FIELD|$3 ); $$ = NIL; } | COLON con_e %prec CM { if( !(instruct&INSTRUCT) ) uerror( "field outside of structure" ); /* alignment or hole */ falloc( stab, $2, -1, $0 ); $$ = NIL; } | error { $$ = NIL; } ; /* int (a)(); is not a function --- sorry! */ nfdeclarator: MUL nfdeclarator { umul: $$ = bdty( UNARY MUL, $2, 0 ); } | nfdeclarator LP RP { uftn: $$ = bdty( UNARY CALL, $1, 0 ); } | nfdeclarator LB RB { uary: $$ = bdty( LB, $1, 0 ); } | nfdeclarator LB con_e RB { bary: if( (int)$3 <= 0 ) werror( "zero or negative subscript" ); $$ = bdty( LB, $1, $3 ); } | NAME { $$ = bdty( NAME, NIL, $1 ); } | LP nfdeclarator RP { $$=$2; } ; fdeclarator: MUL fdeclarator { goto umul; } | fdeclarator LP RP { goto uftn; } | fdeclarator LB RB { goto uary; } | fdeclarator LB con_e RB { goto bary; } | LP fdeclarator RP { $$ = $2; } | name_lp name_list RP { if( blevel!=0 ) uerror( "function declaration in bad context"); $$ = bdty( UNARY CALL, bdty(NAME,NIL,$1), 0 ); stwart = 0; } | name_lp RP { $$ = bdty( UNARY CALL, bdty(NAME,NIL,$1), 0 ); stwart = 0; } ; name_lp: NAME LP { /* turn off typedefs for argument names */ stwart = SEENAME; if( stab[$1].sclass == SNULL ) stab[$1].stype = FTN; } ; name_list: NAME { ftnarg( $1 ); stwart = SEENAME; } | name_list CM NAME { ftnarg( $3 ); stwart = SEENAME; } | error ; /* always preceeded by attributes: thus the $0's */ init_dcl_list: init_declarator %prec CM | init_dcl_list CM {$$=$0;} init_declarator ; /* always preceeded by attributes */ xnfdeclarator: nfdeclarator { defid( $1 = tymerge($0,$1), curclass); beginit($1->tn.rval); } | error ; /* always preceeded by attributes */ init_declarator: nfdeclarator { nidcl( tymerge($0,$1) ); } | fdeclarator { defid( tymerge($0,$1), uclass(curclass) ); } | xnfdeclarator ASSIGN e %prec CM { doinit( $3 ); endinit(); } | xnfdeclarator ASSIGN LC init_list optcomma RC { endinit(); } | error ; init_list: initializer %prec CM | init_list CM initializer ; initializer: e %prec CM { doinit( $1 ); } | ibrace init_list optcomma RC { irbrace(); } ; optcomma : /* empty */ | CM ; optsemi : /* empty */ | SM ; ibrace : LC { ilbrace(); } ; /* STATEMENTS */ compoundstmt: begin dcl_stat_list stmt_list RC { clearst(blevel); if (--blevel == 1) { clearst(blevel); blevel = 0; } checkst(blevel); autooff = *--psavbc; regvar = *--psavbc; } ; begin: LC { if( blevel == 1 ) dclargs(); ++blevel; if( psavbc > &asavbc[BCSZ-2] ) cerror( "nesting too deep" ); *psavbc++ = regvar; *psavbc++ = autooff; } ; statement: e SM { ecomp( $1 ); } | ASM SM { asmout(); } | compoundstmt | ifprefix statement { deflab($1); reached = 1; } | ifelprefix statement { if( $1 != NOLAB ){ deflab( $1 ); reached = 1; } } | whprefix statement { branch((int)( contlab)); deflab( brklab ); if( (flostat&FBRK) || !(flostat&FLOOP)) reached = 1; else reached = 0; resetbc(0); } | doprefix statement WHILE LP e RP SM { deflab( contlab ); if( flostat & FCONT ) reached = 1; ecomp( buildtree( CBRANCH, buildtree( NOT, $5, NIL ), bcon( $1 ) ) ); deflab( brklab ); reached = 1; resetbc(0); } | forprefix .e RP statement { deflab( contlab ); if( flostat&FCONT ) reached = 1; if( $2 ) ecomp( $2 ); branch((int)($1)); deflab( brklab ); if( (flostat&FBRK) || !(flostat&FLOOP) ) reached=1; else reached=0; resetbc(0); } | switchpart statement { if( reached ) branch((int)(brklab)); deflab( $1 ); swend(); deflab(brklab); if( (flostat&FBRK) || !(flostat&FDEF) ) reached=1; resetbc(FCONT); } | BREAK SM { if( brklab == NOLAB ) uerror( "illegal break"); else if(reached) branch((int)(brklab)); flostat |= FBRK; if( brkflag ) goto rch; reached = 0; } | CONTINUE SM { if( contlab == NOLAB ) uerror( "illegal continue"); else branch((int)(contlab)); flostat |= FCONT; goto rch; } | RETURN SM { retstat |= NRETVAL; branch((int)(retlab)); rch: if( !reached ) werror( "statement not reached"); reached = 0; } | RETURN e SM { register NODE *temp; TWORD indtype(); idname = curftn; temp = buildtree( NAME, NIL, NIL ); if(temp->in.type == (FTN|VOID)) lerror( "void function %s cannot return value", stab[idname].sname); temp->in.type = DECREF( temp->in.type ); temp->in.type = indtype( temp->in.type ); temp->tn.op = RNODE; /* node for return */ temp = buildtree( ASSIGN, temp, $2 ); ecomp( temp ); retstat |= RETVAL; branch((int)(retlab)); reached = 0; } | GOTO NAME SM { register NODE *q; q = block( FREE, NIL, NIL, INT|ARY, 0, INT ); q->tn.rval = idname = $2; defid( q, ULABEL ); stab[idname].suse = -lineno; branch((int)(stab[idname].offset)); goto rch; } | SM | error SM | error RC | label statement ; label: NAME COLON { register NODE *q; q = block( FREE, NIL, NIL, INT|ARY, 0, LABEL ); q->tn.rval = $1; defid( q, LABEL ); reached = 1; } | CASE e COLON { addcase($2); reached = 1; } | DEFAULT COLON { reached = 1; adddef(); flostat |= FDEF; } ; doprefix: DO { savebc(); if( !reached ) werror( "loop not entered at top"); brklab = getlab(); contlab = getlab(); deflab( $$ = getlab() ); reached = 1; } ; ifprefix: IF LP e RP { ecomp( buildtree( CBRANCH, $3, bcon( $$=getlab()) ) ) ; reached = 1; } ; ifelprefix: ifprefix statement ELSE { if( reached ) branch((int)($$ = getlab())); else $$ = NOLAB; deflab( $1 ); reached = 1; } ; whprefix: WHILE LP e RP { savebc(); if( !reached ) werror( "loop not entered at top"); if( $3->in.op == ICON && $3->tn.lval != 0 ) flostat = FLOOP; deflab( contlab = getlab() ); reached = 1; brklab = getlab(); if( flostat == FLOOP ) tfree( $3 ); else ecomp( buildtree( CBRANCH, $3, bcon( brklab) ) ); } ; forprefix: FOR LP .e SM .e SM { if( $3 ) ecomp( $3 ); else if( !reached ) werror( "loop not entered at top"); savebc(); contlab = getlab(); brklab = getlab(); deflab( $$ = getlab() ); reached = 1; if( $5 ) ecomp( buildtree( CBRANCH, $5, bcon( brklab) )); else flostat |= FLOOP; } ; switchpart: SWITCH LP e RP { register NODE *temp; savebc(); temp = block( SNODE, NIL, NIL, INT, 0, INT ); temp = buildtree( ASSIGN, temp, $3 ); brklab = getlab(); ecomp( temp ); branch((int)($$ = getlab())); swstart(); reached = 0; } ; /* EXPRESSIONS */ con_e: { $$=instruct; stwart=instruct=0; } e %prec CM { $$ = icons( $2 ); instruct=$1; } ; .e: e | { $$=0; } ; elist: e %prec CM | elist CM e { goto bop; } ; e: e RELOP e { preconf: if( yychar==RELOP || yychar==EQUOP || yychar==AND || yychar==OR || yychar==ER ){ precplaint: if( hflag ) werror( "precedence confusion possible: parenthesize!" ); } bop: $$ = buildtree( $2, $1, $3 ); } | e CM e { $2 = COMOP; goto bop; } | e DIVOP e { goto bop; } | e PLUS e { if(yychar==SHIFTOP) goto precplaint; else goto bop; } | e MINUS e { if(yychar==SHIFTOP ) goto precplaint; else goto bop; } | e SHIFTOP e { if(yychar==PLUS||yychar==MINUS) goto precplaint; else goto bop; } | e MUL e { goto bop; } | e EQUOP e { goto preconf; } | e AND e { if( yychar==RELOP||yychar==EQUOP ) goto preconf; else goto bop; } | e OR e { if(yychar==RELOP||yychar==EQUOP) goto preconf; else goto bop; } | e ER e { if(yychar==RELOP||yychar==EQUOP) goto preconf; else goto bop; } | e ANDAND e { goto bop; } | e OROR e { goto bop; } | e MUL ASSIGN e { abop: $$ = buildtree( ASG $2, $1, $4 ); } | e DIVOP ASSIGN e { goto abop; } | e PLUS ASSIGN e { goto abop; } | e MINUS ASSIGN e { goto abop; } | e SHIFTOP ASSIGN e { goto abop; } | e AND ASSIGN e { goto abop; } | e OR ASSIGN e { goto abop; } | e ER ASSIGN e { goto abop; } | e QUEST e COLON e { $$=buildtree(QUEST, $1, buildtree( COLON, $3, $5 ) ); } | e ASOP e { werror( "old-fashioned assignment operator" ); goto bop; } | e ASSIGN e { goto bop; } | term ; term: term INCOP { $$ = buildtree( $2, $1, bcon(1) ); } | MUL term { ubop: $$ = buildtree( UNARY $1, $2, NIL ); } | AND term { if( ISFTN($2->in.type) || ISARY($2->in.type) ){ werror( "& before array or function: ignored" ); $$ = $2; } else goto ubop; } | MINUS term { goto ubop; } | UNOP term { $$ = buildtree( $1, $2, NIL ); } | INCOP term { $$ = buildtree( $1==INCR ? ASG PLUS : ASG MINUS, $2, bcon(1) ); } | SIZEOF term { $$ = doszof( $2 ); } | LP cast_type RP term %prec INCOP { $$ = buildtree( CAST, $2, $4 ); $$->in.left->in.op = FREE; $$->in.op = FREE; $$ = $$->in.right; } | SIZEOF LP cast_type RP %prec SIZEOF { $$ = doszof( $3 ); } | term LB e RB { $$ = buildtree( LB, $1, $3 ); } | term LB e COLON e RB { $$ = xicolon( $1, $3, $5 ); } | funct_idn RP { $$=buildtree(UNARY CALL,$1,NIL); } | funct_idn elist RP { $$=buildtree(CALL,$1,$2); } | term STROP NAME { if( $2 == DOT ){ if( notlval( $1 ) )uerror( "structure reference must be addressable" ); $1 = buildtree( UNARY AND, $1, NIL ); } idname = $3; $$ = buildtree( STREF, $1, buildtree( NAME, NIL, NIL ) ); } | NAME { idname = $1; /* recognize identifiers in initializations */ if( blevel==0 && stab[idname].stype == UNDEF ) { register NODE *q; werror( "undeclared initializer name %s", stab[idname].sname ); q = block( FREE, NIL, NIL, INT, 0, INT ); q->tn.rval = idname; defid( q, EXTERN ); } $$=buildtree(NAME,NIL,NIL); stab[$1].suse = -lineno; } | ICON { $$=bcon(0); $$->tn.lval = lastcon; $$->tn.rval = NONAME; if( $1 ) $$->fn.csiz = $$->in.type = ctype(LONG); } | FCON { $$=buildtree(FCON,NIL,NIL); $$->fpn.dval = dcon; } | STRING { $$ = getstr(); /* get string contents */ } | LP e RP { $$=$2; } ; cast_type: type null_decl { $$ = tymerge( $1, $2 ); $$->in.op = NAME; $1->in.op = FREE; } ; null_decl: /* EMPTY */ { $$ = bdty( NAME, NIL, -1 ); } | LP RP { $$ = bdty( UNARY CALL, bdty(NAME,NIL,-1),0); } | LP null_decl RP LP RP { $$ = bdty( UNARY CALL, $2, 0 ); } | MUL null_decl { goto umul; } | null_decl LB RB { goto uary; } | null_decl LB con_e RB { goto bary; } | LP null_decl RP { $$ = $2; } ; funct_idn: NAME LP { if( stab[$1].stype == UNDEF ){ register NODE *q; q = block( FREE, NIL, NIL, FTN|INT, 0, INT ); q->tn.rval = $1; defid( q, EXTERN ); } idname = $1; $$=buildtree(NAME,NIL,NIL); stab[idname].suse = -lineno; } | term LP ; %% NODE * mkty( t, d, s ) unsigned t; { return( block( TYPE, NIL, NIL, t, d, s ) ); } NODE * bdty( op, p, v ) NODE *p; { register NODE *q; q = block( op, p, NIL, INT, 0, INT ); switch( op ){ case UNARY MUL: case UNARY CALL: break; case LB: q->in.right = bcon(v); break; case NAME: q->tn.rval = v; break; default: cerror( "bad bdty" ); } return( q ); } dstash( n ){ /* put n into the dimension table */ if( curdim >= DIMTABSZ-1 ){ cerror( "dimension table overflow"); } dimtab[ curdim++ ] = n; } savebc() { if( psavbc > & asavbc[BCSZ-4 ] ){ cerror( "whiles, fors, etc. too deeply nested"); } *psavbc++ = brklab; *psavbc++ = contlab; *psavbc++ = flostat; *psavbc++ = swx; flostat = 0; } resetbc(mask){ swx = *--psavbc; flostat = *--psavbc | (flostat&mask); contlab = *--psavbc; brklab = *--psavbc; } addcase(p) NODE *p; { /* add case to switch */ p = optim( p ); /* change enum to ints */ if( p->in.op != ICON ){ uerror( "non-constant case expression"); return; } if( swp == swtab ){ uerror( "case not in switch"); return; } if( swp >= &swtab[SWITSZ] ){ cerror( "switch table overflow"); } swp->sval = p->tn.lval; deflab( swp->slab = getlab() ); ++swp; tfree(p); } adddef(){ /* add default case to switch */ if( swtab[swx].slab >= 0 ){ uerror( "duplicate default in switch"); return; } if( swp == swtab ){ uerror( "default not inside switch"); return; } deflab( swtab[swx].slab = getlab() ); } swstart(){ /* begin a switch block */ if( swp >= &swtab[SWITSZ] ){ cerror( "switch table overflow"); } swx = swp - swtab; swp->slab = -1; ++swp; } swend(){ /* end a switch block */ register struct sw *swbeg, *p, *q, *r, *r1; CONSZ temp; int tempi; swbeg = &swtab[swx+1]; /* sort */ r1 = swbeg; r = swp-1; while( swbeg < r ){ /* bubble largest to end */ for( q=swbeg; qsval > (q+1)->sval ){ /* swap */ r1 = q+1; temp = q->sval; q->sval = r1->sval; r1->sval = temp; tempi = q->slab; q->slab = r1->slab; r1->slab = tempi; } } r = r1; r1 = swbeg; } /* it is now sorted */ for( p = swbeg+1; psval == (p-1)->sval ){ uerror( "duplicate case in switch, %d", tempi=(int)p->sval ); return; } } reached = 1; genswitch( swbeg-1, (int)(swp-swbeg) ); swp = swbeg-1; }