/* @(#)y2.c 1.8 */ # include "dextern" # define IDENTIFIER 257 # define MARK 258 # define TERM 259 # define LEFT 260 # define RIGHT 261 # define BINARY 262 # define PREC 263 # define LCURLY 264 # define C_IDENTIFIER 265 /* name followed by colon */ # define NUMBER 266 # define START 267 # define TYPEDEF 268 # define TYPENAME 269 # define UNION 270 # define ENDFILE 0 /* communication variables between various I/O routines */ char *infile; /* input file name */ int numbval; /* value of an input number */ char tokname[NAMESIZE]; /* input token name */ /* storage of names */ char cnames[CNAMSZ]; /* place where token and nonterminal names are stored */ int cnamsz = CNAMSZ; /* size of cnames */ char * cnamp = cnames; /* place where next name is to be put in */ int ndefout = 3; /* number of defined symbols output */ /* storage of types */ int ntypes; /* number of types defined */ char * typeset[NTYPES]; /* pointers to type tags */ /* symbol tables for tokens and nonterminals */ int ntokens = 0; struct toksymb tokset[NTERMS]; int toklev[NTERMS]; int nnonter = -1; struct ntsymb nontrst[NNONTERM]; int start; /* start symbol */ /* assigned token type values */ int extval = 0; /* input and output file descriptors */ FILE * finput; /* yacc input file */ FILE * faction; /* file for saving actions */ FILE * fdefine; /* file for # defines */ FILE * ftable; /* y.tab.c file */ FILE * ftemp; /* tempfile to pass 2 */ FILE * fdebug; /* where the strings for debugging are stored */ FILE * foutput; /* y.output file */ /* storage for grammar rules */ int mem0[MEMSIZE] ; /* production storage */ int *mem = mem0; int nprod= 1; /* number of productions */ int *prdptr[NPROD]; /* pointers to descriptions of productions */ int levprd[NPROD] ; /* precedence levels for the productions */ char had_act[NPROD]; /* set to 1 if the reduction has action code to do */ int gen_lines = 1; /* flag for generating the # line's default is yes */ int gen_testing = 0; /* flag for whether to include runtime debugging */ setup(argc,argv) int argc; char *argv[]; { int i,j,lev,t, ty; int c; int *p; char actname[8]; foutput = NULL; fdefine = NULL; i = 1; while( argc >= 2 && argv[1][0] == '-' ) { while( *++(argv[1]) ){ switch( *argv[1] ){ case 'v': case 'V': foutput = fopen(FILEU, "w" ); if( foutput == NULL ) error( "cannot open y.output" ); continue; case 'D': case 'd': fdefine = fopen( FILED, "w" ); if ( fdefine == NULL ) error( "cannot open y.tab.h" ); continue; case 'l': case 'L': gen_lines = !gen_lines; case 't': case 'T': gen_testing = !gen_testing; continue; case 'o': case 'O': fprintf( stderr, "`o' flag now default in yacc\n" ); continue; case 'r': case 'R': error( "Ratfor Yacc is dead: sorry...\n" ); default: error( "illegal option: %c", *argv[1]); } } argv++; argc--; } fdebug = fopen( DEBUGNAME, "w" ); if ( fdebug == NULL ) error( "cannot open yacc.debug" ); ftable = fopen( OFILE, "w" ); if( ftable == NULL ) error( "cannot open y.tab.c" ); ftemp = fopen( TEMPNAME, "w" ); faction = fopen( ACTNAME, "w" ); if( ftemp==NULL || faction==NULL ) error( "cannot open temp file" ); if( argc < 2 || ((finput=fopen( infile=argv[1], "r" )) == NULL ) ){ error( "cannot open input file" ); } cnamp = cnames; defin(0,"$end"); extval = 0400; defin(0,"error"); defin(1,"$accept"); mem=mem0; lev = 0; ty = 0; i=0; beg_debug(); /* initialize fdebug file */ /* sorry -- no yacc parser here..... we must bootstrap somehow... */ for( t=gettok(); t!=MARK && t!= ENDFILE; ){ switch( t ){ case ';': t = gettok(); break; case START: if( (t=gettok()) != IDENTIFIER ){ error( "bad %%start construction" ); } start = chfind(1,tokname); t = gettok(); continue; case TYPEDEF: if( (t=gettok()) != TYPENAME ) error( "bad syntax in %%type" ); ty = numbval; for(;;){ t = gettok(); switch( t ){ case IDENTIFIER: if( (t=chfind( 1, tokname ) ) < NTBASE ) { j = TYPE( toklev[t] ); if( j!= 0 && j != ty ){ error( "type redeclaration of token %s", tokset[t].name ); } else SETTYPE( toklev[t],ty); } else { j = nontrst[t-NTBASE].tvalue; if( j != 0 && j != ty ){ error( "type redeclaration of nonterminal %s", nontrst[t-NTBASE].name ); } else nontrst[t-NTBASE].tvalue = ty; } case ',': continue; case ';': t = gettok(); break; default: break; } break; } continue; case UNION: /* copy the union declaration to the output */ cpyunion(); t = gettok(); continue; case LEFT: case BINARY: case RIGHT: ++i; case TERM: lev = t-TERM; /* nonzero means new prec. and assoc. */ ty = 0; /* get identifiers so defined */ t = gettok(); if( t == TYPENAME ){ /* there is a type defined */ ty = numbval; t = gettok(); } for(;;) { switch( t ){ case ',': t = gettok(); continue; case ';': break; case IDENTIFIER: j = chfind(0,tokname); if( lev ){ if( ASSOC(toklev[j]) ) error( "redeclaration of precedence of %s", tokname ); SETASC(toklev[j],lev); SETPLEV(toklev[j],i); } if( ty ){ if( TYPE(toklev[j]) ) error( "redeclaration of type of %s", tokname ); SETTYPE(toklev[j],ty); } if( (t=gettok()) == NUMBER ){ tokset[j].value = numbval; if( j < ndefout && j>2 ){ error( "please define type number of %s earlier", tokset[j].name ); } t=gettok(); } continue; } break; } continue; case LCURLY: defout(); cpycode(); t = gettok(); continue; default: error( "syntax error" ); } } if( t == ENDFILE ){ error( "unexpected EOF before %%" ); } /* t is MARK */ defout(); end_toks(); /* all tokens dumped - get ready for reductions */ fprintf( ftable, "#define yyclearin yychar = -1\n" ); fprintf( ftable, "#define yyerrok yyerrflag = 0\n" ); fprintf( ftable, "extern int yychar;\nextern int yyerrflag;\n" ); fprintf( ftable, "#ifndef YYMAXDEPTH\n#define YYMAXDEPTH 150\n#endif\n" ); if( !ntypes ) fprintf( ftable, "#ifndef YYSTYPE\n#define YYSTYPE int\n#endif\n" ); fprintf( ftable, "YYSTYPE yylval, yyval;\n" ); fprintf( ftable, "typedef int yytabelem;\n" ); prdptr[0]=mem; /* added production */ *mem++ = NTBASE; *mem++ = start; /* if start is 0, we will overwrite with the lhs of the first rule */ *mem++ = 1; *mem++ = 0; prdptr[1]=mem; while( (t=gettok()) == LCURLY ) cpycode(); if( t != C_IDENTIFIER ) error( "bad syntax on first rule" ); if( !start ) prdptr[0][1] = chfind(1,tokname); /* read rules */ while( t!=MARK && t!=ENDFILE ){ /* process a rule */ if( t == '|' ){ rhsfill( (char *) 0 ); /* restart fill of rhs */ *mem++ = *prdptr[nprod-1]; } else if( t == C_IDENTIFIER ){ *mem = chfind(1,tokname); if( *mem < NTBASE ) error( "token illegal on LHS of grammar rule" ); ++mem; lhsfill( tokname ); /* new rule: restart strings */ } else error( "illegal rule: missing semicolon or | ?" ); /* read rule body */ t = gettok(); more_rule: while( t == IDENTIFIER ) { *mem = chfind(1,tokname); if( *mem<NTBASE ) levprd[nprod] = toklev[*mem]; ++mem; rhsfill( tokname ); /* add to rhs string */ t = gettok(); } if( t == PREC ){ if( gettok()!=IDENTIFIER) error( "illegal %%prec syntax" ); j = chfind(2,tokname); if( j>=NTBASE)error("nonterminal %s illegal after %%prec", nontrst[j-NTBASE].name); levprd[nprod]=toklev[j]; t = gettok(); } if( t == '=' ){ had_act[nprod] = 1; levprd[nprod] |= ACTFLAG; fprintf( faction, "\ncase %d:", nprod ); cpyact( mem-prdptr[nprod]-1 ); fprintf( faction, " break;" ); if( (t=gettok()) == IDENTIFIER ){ /* action within rule... */ lrprnt(); /* dump lhs, rhs */ sprintf( actname, "$$%d", nprod ); j = chfind(1,actname); /* make it a nonterminal */ /* the current rule will become rule number nprod+1 */ /* move the contents down, and make room for the null */ for( p=mem; p>=prdptr[nprod]; --p ) p[2] = *p; mem += 2; /* enter null production for action */ p = prdptr[nprod]; *p++ = j; *p++ = -nprod; /* update the production information */ levprd[nprod+1] = levprd[nprod] & ~ACTFLAG; levprd[nprod] = ACTFLAG; if( ++nprod >= NPROD ) error( "more than %d rules", NPROD ); prdptr[nprod] = p; /* make the action appear in the original rule */ *mem++ = j; /* get some more of the rule */ goto more_rule; } } while( t == ';' ) t = gettok(); *mem++ = -nprod; /* check that default action is reasonable */ if( ntypes && !(levprd[nprod]&ACTFLAG) && nontrst[*prdptr[nprod]-NTBASE].tvalue ){ /* no explicit action, LHS has value */ register tempty; tempty = prdptr[nprod][1]; if( tempty < 0 ) error( "must return a value, since LHS has a type" ); else if( tempty >= NTBASE ) tempty = nontrst[tempty-NTBASE].tvalue; else tempty = TYPE( toklev[tempty] ); if( tempty != nontrst[*prdptr[nprod]-NTBASE].tvalue ){ error( "default action causes potential type clash" ); } } if( ++nprod >= NPROD ) error( "more than %d rules", NPROD ); prdptr[nprod] = mem; levprd[nprod]=0; } /* end of all rules */ end_debug(); /* finish fdebug file's input */ finact(); if( t == MARK ){ if ( gen_lines ) fprintf( ftable, "\n# line %d \"%s\"\n", lineno, infile ); while( (c=getc(finput)) != EOF ) putc( c, ftable ); } fclose( finput ); } finact(){ /* finish action routine */ fclose(faction); fprintf( ftable, "# define YYERRCODE %d\n", tokset[2].value ); } defin( t, s ) register char *s; { /* define s to be a terminal if t=0 or a nonterminal if t=1 */ register val; if (t) { if( ++nnonter >= NNONTERM ) error("too many nonterminals, limit %d",NNONTERM); nontrst[nnonter].name = cstash(s); return( NTBASE + nnonter ); } /* must be a token */ if( ++ntokens >= NTERMS ) error("too many terminals, limit %d",NTERMS ); tokset[ntokens].name = cstash(s); /* establish value for token */ if( s[0]==' ' && s[2]=='\0' ) /* single character literal */ val = s[1]; else if ( s[0]==' ' && s[1]=='\\' ) { /* escape sequence */ if( s[3] == '\0' ){ /* single character escape sequence */ switch ( s[2] ){ /* character which is escaped */ case 'n': val = '\n'; break; case 'r': val = '\r'; break; case 'b': val = '\b'; break; case 't': val = '\t'; break; case 'f': val = '\f'; break; case '\'': val = '\''; break; case '"': val = '"'; break; case '\\': val = '\\'; break; default: error( "invalid escape" ); } } else if( s[2] <= '7' && s[2]>='0' ){ /* \nnn sequence */ if( s[3]<'0' || s[3] > '7' || s[4]<'0' || s[4]>'7' || s[5] != '\0' ) error("illegal \\nnn construction" ); val = 64*s[2] + 8*s[3] + s[4] - 73*'0'; if( val == 0 ) error( "'\\000' is illegal" ); } } else { val = extval++; } tokset[ntokens].value = val; toklev[ntokens] = 0; return( ntokens ); } defout(){ /* write out the defines (at the end of the declaration section) */ register int i, c; register char *cp; for( i=ndefout; i<=ntokens; ++i ){ cp = tokset[i].name; if( *cp == ' ' ) /* literals */ { fprintf( fdebug, "\t\"%s\",\t%d,\n", tokset[i].name + 1, tokset[i].value ); cp++; /* in my opinion, this should be continue */ } for( ; (c= *cp)!='\0'; ++cp ){ if( islower(c) || isupper(c) || isdigit(c) || c=='_' ); /* VOID */ else goto nodef; } if ( tokset[i].name[0] != ' ' ) fprintf( fdebug, "\t\"%s\",\t%d,\n", tokset[i].name, tokset[i].value ); fprintf( ftable, "# define %s %d\n", tokset[i].name, tokset[i].value ); if( fdefine != NULL ) fprintf( fdefine, "# define %s %d\n", tokset[i].name, tokset[i].value ); nodef: ; } ndefout = ntokens+1; } char * cstash( s ) register char *s; { char *temp; temp = cnamp; do { if( cnamp >= &cnames[cnamsz] ) error("too many characters in id's and literals" ); else *cnamp++ = *s; } while ( *s++ ); return( temp ); } gettok() { register i, base; static int peekline; /* number of '\n' seen in lookahead */ register c, match, reserve; begin: reserve = 0; lineno += peekline; peekline = 0; c = getc(finput); while( c==' ' || c=='\n' || c=='\t' || c=='\f' ){ if( c == '\n' ) ++lineno; c=getc(finput); } if( c == '/' ){ /* skip comment */ lineno += skipcom(); goto begin; } switch(c){ case EOF: return(ENDFILE); case '{': ungetc( c, finput ); return( '=' ); /* action ... */ case '<': /* get, and look up, a type name (union member name) */ i = 0; while( (c=getc(finput)) != '>' && c>=0 && c!= '\n' ){ tokname[i] = c; if( ++i >= NAMESIZE ) --i; } if( c != '>' ) error( "unterminated < ... > clause" ); tokname[i] = '\0'; for( i=1; i<=ntypes; ++i ){ if( !strcmp( typeset[i], tokname ) ){ numbval = i; return( TYPENAME ); } } typeset[numbval = ++ntypes] = cstash( tokname ); return( TYPENAME ); case '"': case '\'': match = c; tokname[0] = ' '; i = 1; for(;;){ c = getc(finput); if( c == '\n' || c == EOF ) error("illegal or missing ' or \"" ); if( c == '\\' ){ c = getc(finput); tokname[i] = '\\'; if( ++i >= NAMESIZE ) --i; } else if( c == match ) break; tokname[i] = c; if( ++i >= NAMESIZE ) --i; } break; case '%': case '\\': switch(c=getc(finput)) { case '0': return(TERM); case '<': return(LEFT); case '2': return(BINARY); case '>': return(RIGHT); case '%': case '\\': return(MARK); case '=': return(PREC); case '{': return(LCURLY); default: reserve = 1; } default: if( isdigit(c) ){ /* number */ numbval = c-'0' ; base = (c=='0') ? 8 : 10 ; for( c=getc(finput); isdigit(c) ; c=getc(finput) ){ numbval = numbval*base + c - '0'; } ungetc( c, finput ); return(NUMBER); } else if( islower(c) || isupper(c) || c=='_' || c=='.' || c=='$' ){ i = 0; while( islower(c) || isupper(c) || isdigit(c) || c=='_' || c=='.' || c=='$' ){ tokname[i] = c; if( reserve && isupper(c) ) tokname[i] += 'a'-'A'; if( ++i >= NAMESIZE ) --i; c = getc(finput); } } else return(c); ungetc( c, finput ); } tokname[i] = '\0'; if( reserve ){ /* find a reserved word */ if( !strcmp(tokname,"term")) return( TERM ); if( !strcmp(tokname,"token")) return( TERM ); if( !strcmp(tokname,"left")) return( LEFT ); if( !strcmp(tokname,"nonassoc")) return( BINARY ); if( !strcmp(tokname,"binary")) return( BINARY ); if( !strcmp(tokname,"right")) return( RIGHT ); if( !strcmp(tokname,"prec")) return( PREC ); if( !strcmp(tokname,"start")) return( START ); if( !strcmp(tokname,"type")) return( TYPEDEF ); if( !strcmp(tokname,"union")) return( UNION ); error("invalid escape, or illegal reserved word: %s", tokname ); } /* look ahead to distinguish IDENTIFIER from C_IDENTIFIER */ c = getc(finput); while( c==' ' || c=='\t'|| c=='\n' || c=='\f' || c== '/' ) { if( c == '\n' ) ++peekline; else if( c == '/' ){ /* look for comments */ peekline += skipcom(); } c = getc(finput); } if( c == ':' ) return( C_IDENTIFIER ); ungetc( c, finput ); return( IDENTIFIER ); } fdtype( t ){ /* determine the type of a symbol */ register v; if( t >= NTBASE ) v = nontrst[t-NTBASE].tvalue; else v = TYPE( toklev[t] ); if( v <= 0 ) error( "must specify type for %s", (t>=NTBASE)?nontrst[t-NTBASE].name: tokset[t].name ); return( v ); } chfind( t, s ) register char *s; { int i; if (s[0]==' ')t=0; TLOOP(i){ if(!strcmp(s,tokset[i].name)){ return( i ); } } NTLOOP(i){ if(!strcmp(s,nontrst[i].name)) { return( i+NTBASE ); } } /* cannot find name */ if( t>1 ) error( "%s should have been defined earlier", s ); return( defin( t, s ) ); } cpyunion(){ /* copy the union declaration to the output, and the define file if present */ int level, c; if ( gen_lines ) fprintf( ftable, "\n# line %d \"%s\"\n", lineno, infile ); fprintf( ftable, "typedef union " ); if( fdefine ) fprintf( fdefine, "\ntypedef union " ); level = 0; for(;;){ if( (c=getc(finput)) < 0 ) error( "EOF encountered while processing %%union" ); putc( c, ftable ); if( fdefine ) putc( c, fdefine ); switch( c ){ case '\n': ++lineno; break; case '{': ++level; break; case '}': --level; if( level == 0 ) { /* we are finished copying */ fprintf( ftable, " YYSTYPE;\n" ); if( fdefine ) fprintf( fdefine, " YYSTYPE;\nextern YYSTYPE yylval;\n" ); return; } } } } cpycode(){ /* copies code between \{ and \} */ int c; c = getc(finput); if( c == '\n' ) { c = getc(finput); lineno++; } if ( gen_lines ) fprintf( ftable, "\n# line %d \"%s\"\n", lineno, infile ); while( c>=0 ){ if( c=='\\' ) if( (c=getc(finput)) == '}' ) return; else putc('\\', ftable ); if( c=='%' ) if( (c=getc(finput)) == '}' ) return; else putc('%', ftable ); putc( c , ftable ); if( c == '\n' ) ++lineno; c = getc(finput); } error("eof before %%}" ); } skipcom(){ /* skip over comments */ register c, i=0; /* i is the number of lines skipped */ /* skipcom is called after reading a / */ if( getc(finput) != '*' ) error( "illegal comment" ); c = getc(finput); while( c != EOF ){ while( c == '*' ){ if( (c=getc(finput)) == '/' ) return( i ); } if( c == '\n' ) ++i; c = getc(finput); } error( "EOF inside comment" ); /* NOTREACHED */ } cpyact(offset){ /* copy C action to the next ; or closing } */ int brac, c, match, j, s, tok; if ( gen_lines ) fprintf( faction, "\n# line %d \"%s\"\n", lineno, infile ); brac = 0; loop: c = getc(finput); swt: switch( c ){ case ';': if( brac == 0 ){ putc( c , faction ); return; } goto lcopy; case '{': brac++; goto lcopy; case '$': s = 1; tok = -1; c = getc(finput); if( c == '<' ){ /* type description */ ungetc( c, finput ); if( gettok() != TYPENAME ) error( "bad syntax on $<ident> clause" ); tok = numbval; c = getc(finput); } if( c == '$' ){ fprintf( faction, "yyval"); if( ntypes ){ /* put out the proper tag... */ if( tok < 0 ) tok = fdtype( *prdptr[nprod] ); fprintf( faction, ".%s", typeset[tok] ); } goto loop; } if( c == '-' ){ s = -s; c = getc(finput); } if( isdigit(c) ){ j=0; while( isdigit(c) ){ j= j*10+c-'0'; c = getc(finput); } j = j*s - offset; if( j > 0 ){ error( "Illegal use of $%d", j+offset ); } fprintf( faction, "yypvt[-%d]", -j ); if( ntypes ){ /* put out the proper tag */ if( j+offset <= 0 && tok < 0 ) error( "must specify type of $%d", j+offset ); if( tok < 0 ) tok = fdtype( prdptr[nprod][j+offset] ); fprintf( faction, ".%s", typeset[tok] ); } goto swt; } putc( '$' , faction ); if( s<0 ) putc('-', faction ); goto swt; case '}': if( --brac ) goto lcopy; putc( c, faction ); return; case '/': /* look for comments */ putc( c , faction ); c = getc(finput); if( c != '*' ) goto swt; /* it really is a comment */ putc( c , faction ); c = getc(finput); while( c != EOF ){ while( c=='*' ){ putc( c , faction ); if( (c=getc(finput)) == '/' ) goto lcopy; } putc( c , faction ); if( c == '\n' )++lineno; c = getc(finput); } error( "EOF inside comment" ); case '\'': /* character constant */ match = '\''; goto string; case '"': /* character string */ match = '"'; string: putc( c , faction ); while( c=getc(finput) ){ if( c=='\\' ){ putc( c , faction ); c=getc(finput); if( c == '\n' ) ++lineno; } else if( c==match ) goto lcopy; else if( c=='\n' ) error( "newline in string or char. const." ); putc( c , faction ); } error( "EOF in string or character constant" ); case EOF: error("action does not terminate" ); case '\n': ++lineno; goto lcopy; } lcopy: putc( c , faction ); goto loop; } #define RHS_TEXT_LEN ( BUFSIZ * 4 ) /* length of rhstext */ char lhstext[ BUFSIZ ]; /* store current lhs (non-terminal) name */ char rhstext[ RHS_TEXT_LEN ]; /* store current rhs list */ lhsfill( s ) /* new rule, dump old (if exists), restart strings */ char *s; { rhsfill( (char *) 0 ); strcpy( lhstext, s ); /* don't worry about too long of a name */ } rhsfill( s ) char *s; /* either name or 0 */ { static char *loc = rhstext; /* next free location in rhstext */ register char *p; if ( !s ) /* print out and erase old text */ { if ( *lhstext ) /* there was an old rule - dump it */ lrprnt(); ( loc = rhstext )[0] = '\0'; return; } /* add to stuff in rhstext */ p = s; *loc++ = ' '; if ( *s == ' ' ) /* special quoted symbol */ { *loc++ = '\''; /* add first quote */ p++; } while ( *loc = *p++ ) if ( loc++ > &rhstext[ RHS_TEXT_LEN ] - 2 ) break; if ( *s == ' ' ) *loc++ = '\''; *loc = '\0'; /* terminate the string */ } lrprnt() /* print out the left and right hand sides */ { char *rhs; if ( !*rhstext ) /* empty rhs - print usual comment */ rhs = " /* empty */"; else rhs = rhstext; fprintf( fdebug, " \"%s :%s\",\n", lhstext, rhs ); } beg_debug() /* dump initial sequence for fdebug file */ { fprintf( fdebug, "typedef struct { char *t_name; int t_val; } yytoktype;\n" ); fprintf( fdebug, "#ifndef YYDEBUG\n#\tdefine YYDEBUG\t%d", gen_testing ); fprintf( fdebug, "\t/*%sallow debugging */\n#endif\n\n", gen_testing ? " " : " don't " ); fprintf( fdebug, "#if YYDEBUG\n\nyytoktype yytoks[] =\n{\n" ); } end_toks() /* finish yytoks array, get ready for yyred's strings */ { fprintf( fdebug, "\t\"-unknown-\",\t-1\t/* ends search */\n" ); fprintf( fdebug, "};\n\nchar * yyreds[] =\n{\n" ); fprintf( fdebug, "\t\"-no such reduction-\",\n" ); } end_debug() /* finish yyred array, close file */ { lrprnt(); /* dump last lhs, rhs */ fprintf( fdebug, "};\n#endif /* YYDEBUG */\n" ); fclose( fdebug ); }