sources-request@mirror.UUCP (12/10/86)
Submitted by: linus!gatech!emory!arnold (Arnold D. Robbins {EUCC}) Mod.sources: Volume 7, Issue 86 Archive-name: yacchacks [ The tools here provide a way to restart YACC parses to handle conditional-compilation directives, include directives, and from error states. Good luck. The "kclose" routine herein can probably be more portable written with a dup()/fclose/fdopen set of calls. --r$ ] Arnold Robbins CSNET: arnold@emory BITNET: arnold@emoryu1 ARPA: arnold%emory.csnet@csnet-relay.arpa UUCP: { akgua, decvax, gatech, sb1, sb6, sunatl }!emory!arnold "All this digital stuff is just a fad. Analog is the way to go." -- William M. Robbins, 1984 echo extracting 'kludge.parser' sed 's/^X//' << \EOF > kludge.parser X1a X/* X** kludge.parser X** X** editor command file to make internal yacc variables X** available on the global level, so that the conditional X** compile handling can restart the parse in the middle. X** to do this requires adding longjmp stuff. X*/ X X#include <setjmp.h> X. X/yyparse/i Xshort yys[YYMAXDEPTH]; XYYSTYPE *yypv; Xshort yystate, *yyps; Xjmp_buf restart; X. X/yyparse/ X/short yys/d X/yystate/s/yystate, \*yyps, // X/yypv/d X/yynewstate/a X setjmp(restart); X. Xw perqgram.c Xq EOF echo extracting 'makefile' sed 's/^X//' << \EOF > makefile X# ... beginning of the makefile X X# the handling of conditional compiles requires that we X# be able to jump into the middle of the parser to restart X# it. therefore we kludge it to allow this with an editor X# command file. (yuch) X Xperqgram.c: perqgram.y kludge.parser X yacc $(YFLAGS) perqgram.y X ed y.tab.c < kludge.parser X rm -f y.tab.c X : parser now kludged X X# ... the rest of the makefile EOF echo extracting 'pushpop.c' sed 's/^X//' << \EOF > pushpop.c X/* X** pushpop.c X** X** programs to do file include processing X** and do handling of the conditional compilation X** viz. {$ifc ...} {$elsec} {$endc} X*/ X X#include "perqref.h" X#include <setjmp.h> X X/* this should be defined on the command line by the makefile */ X/* but if it is not, do it here. the pathname below is a reasonable guess */ X X#ifndef DFSFILE X#define DFSFILE "/ics/src/cmd/perqref/qcodes.dfs" X#endif X X#define YYMAXDEPTH 150 X Xstatic int including = FALSE; X Xyywrap() /* wrap up for lex */ X{ X if(including) X { X popfile(); X return(0); X } X else X return(1); X} X Xmapdown(str) Xchar *str; X{ X for (; *str != '\0'; str++) X *str = isupper(*str) ? tolower(*str) : *str; X} X X/* X** routines to handle file inclusion X*/ X X#define MAXINCLS 15 X#define MAXIDLEN 33 X#define MAXNAMES 300 X Xstatic char incl_names[MAXNAMES][MAXIDLEN]; Xstatic int incl_index = -1; X Xstatic char *save_names[MAXINCLS]; Xstatic int save_lines[MAXINCLS]; Xstatic FILE incl_files[MAXINCLS]; Xstatic int level = -1; X Xpushfile(n) /* handle include files if possible */ Xint n; X{ X FILE *fp; X char name[MAXIDLEN]; X int i; X char *namep; X char *index(); /* use strchr() for Unix 3.0 or later */ X X debug(fprintf(stderr,"yytext == '%s'\n", yytext)); X X while(isspace(yytext[n])) X n++; /* skip leading blanks or tabs */ X X strcpy(name, &yytext[n]); X for(i = strlen(name)-1; ! isalnum(name[i]) && name[i] != '.'; i--) X name[i] = '\0'; /* get rid of } and spaces */ X mapdown(name); X X /* special case various types of suffixes */ X i = strlen(name); X X if(strcmp(&name[i-4], ".pas") == 0) X name[i-2] = name[i-1] = '\0'; /* make into ".p" */ X else if(strcmp(&name[i-2], ".p") == 0 X || strcmp(&name[i-4],".dfs") == 0 X || index(name, '.') != NULL) /* special file name */ X /* do nothing */ ; /* e.g. oil.keyhdr */ X else X strcat(name, ".p"); X X /* let name be the name found, but the file will be */ X /* the kludged .dfs file */ X /* this way, the listing will show the .dfs file */ X if (strcmp(&name[i-4],".dfs") == 0) X namep = DFSFILE; X else X namep = name; X X debug(fprintf(stderr,"Trying to include file %s\n", namep)); X X if((fp = fopen(namep, "r")) == NULL) X { X fprintf(stderr,"in %s, couldn't open include file '%s'\n", X fname, namep); X return; X } X X if(++level >= MAXINCLS) X { X fprintf(stderr,"Includes nested too deep, in file %s, line %d\n" , X fname, line_no); X exit(1); X } X else X { X if(++incl_index >= MAXNAMES) X { X fprintf(stderr,"Over %d files included%s\n", X MAXNAMES, " altogether"); X exit(15); X } X strcpy(incl_names[incl_index], name); X save_names[level] = fname; X save_lines[level] = line_no; X incl_files[level] = *stdin; X *stdin = *fp; X line_no = 1; X fname = incl_names[incl_index]; X including = TRUE; X kclose(fp); /* free stdio FILE table element */ X } X} X Xpopfile() X{ X line_no = save_lines[level]; X fname = save_names[level]; X fclose(stdin); X *stdin = incl_files[level]; X if(--level < 0) X { X including = FALSE; X level = -1; /* just to be sure */ X } X /* else X still including */ X} X X X/* X** routines to handle conditional compiles X** Basic strategy is to save those variables X** that define the yacc parse state, when an ifc is seen. X** then continue the parse. when an elsec is seen, restore X** the saved values. When and endc is seen, pop the stack of X** saved parse states. X** This REQUIRES that the yacc produced C code be X** appropriately munged to make some local variables global X** so that we can get to them. Isn't that neato??? X** However, the makefile takes care of it with an editor script. X*/ X X#define MAXSTACK 100 X Xextern short yys[]; Xextern short yystate; Xextern short *yyps; Xextern YYSTYPE *yypv; Xextern YYSTYPE yyv[]; Xextern YYSTYPE yyval; X Xstruct save_it { X short s_yys[YYMAXDEPTH]; X short s_yystate; X short *s_yyps; X YYSTYPE *s_yypv; X YYSTYPE s_yyv[YYMAXDEPTH]; X YYSTYPE s_yyval; X } kludge_stack[MAXSTACK]; X Xstatic int cond_index = -1; X Xifc() X{ X int i; X X if(++cond_index >= MAXSTACK) X { X fprintf(stderr,"conditional compiles nested more than %d deep, in file %s, at line %d\n", MAXSTACK, fname, line_no); X exit(35); X } X X kludge_stack[cond_index].s_yystate = yystate; X kludge_stack[cond_index].s_yyps = yyps; X kludge_stack[cond_index].s_yypv = yypv; X kludge_stack[cond_index].s_yyval = yyval; X X for(i = 0; &yys[i] <= yyps; i++) X kludge_stack[cond_index].s_yys[i] = yys[i]; X for(i = 0; &yyv[i] <= yypv; i++) X kludge_stack[cond_index].s_yyv[i] = yyv[i]; X} X Xelsec() X{ X int i; X extern jmp_buf restart; /* in modified yacc C code */ X X if(cond_index < 0) X { X fprintf(stderr,"unmatched elsec in file %s, at line %d\n", fname, line_no); X exit(53); X } X X yystate = kludge_stack[cond_index].s_yystate; X yyps = kludge_stack[cond_index].s_yyps; X yypv = kludge_stack[cond_index].s_yypv; X yyval = kludge_stack[cond_index].s_yyval; X X for(i = 0; &yys[i] <= yyps; i++) X yys[i] = kludge_stack[cond_index].s_yys[i]; X X for(i = 0; &yyv[i] <= yypv; i++) X yyv[i] = kludge_stack[cond_index].s_yyv[i]; X X longjmp(restart, 0); X} X Xendc() X{ X if(cond_index < 0) X { X fprintf(stderr,"unmatched endc in file %s, at line %d\n", fname, line_no); X exit(54); X } X X cond_index--; X} X Xresetcond() X{ X cond_index = -1; X} X X/* kclose -- kludge close a FILE */ X X/* X** the purpose of this routine is to free the stdio X** FILE table, without actually closing the file descriptor. X** X** This is necessary so that we can include files, and X** process files in the looping through argv. X** X** (this routine stolen from the standard i/o library.) X*/ X Xstatic kclose(iop) /* static since only used here */ Xregister struct _iobuf *iop; X{ X register r; X X r = EOF; X if (iop->_flag&(_IOREAD|_IOWRT|_IORW) && (iop->_flag&_IOSTRG)==0) { X r = fflush(iop); X/* X** DON'T CLOSE THE FILE !!!!! X if (close(fileno(iop)) < 0) X r = EOF; X*/ X if (iop->_flag&_IOMYBUF) X free(iop->_base); X if (iop->_flag&(_IOMYBUF|_IONBF|_IOLBF)) X iop->_base = NULL; X } X iop->_flag &= ~(_IOREAD|_IOWRT|_IOLBF|_IONBF|_IOMYBUF|_IOERR|_IOEOF|_IOSTRG|_IORW); X iop->_cnt = 0; X return(r); X} EOF echo extracting 'scan.l' sed 's/^X//' << \EOF > scan.l X/* X * this is a chunk of a lex file to be used for handling conditional X * compilation by saving and restoring the yacc parser state, and X * also handling include files. X * X * The particular language this was for allowed {$i[nclude] file} X * to do file inclusion. Case did not matter, and the only part of the X * word "include" needed was the "i". The letters in {}s below are from X * lex class definitions to get case independace. X * X * Conditional compilation was done with {$ifc <something>}, {$else} and X * {$endc} X */ X X%{ X#include "y.tab.h" X%} X X%% X"{$"{i}" "+({letter}|{digit}|".")+[^\}\n]*"}" pushfile(4); X"{$"{i}{n}" "+({letter}|{digit}|".")+[^\}\n]*"}" pushfile(5); X"{$"{i}{n}{c}" "+({letter}|{digit}|".")+[^\}\n]*"}" pushfile(6); X"{$"{i}{n}{c}{l}" "+({letter}|{digit}|".")+[^\}\n]*"}" pushfile(7); X"{$"{i}{n}{c}{l}{u}" "+({letter}|{digit}|".")+[^\}\n]*"}" pushfile(8); X"{$"{i}{n}{c}{l}{u}{d}" "+({letter}|{digit}|".")+[^\}\n]*"}" pushfile(9); X"{$"{i}{n}{c}{l}{u}{d}{e}" "+({letter}|{digit}|".")+[^\}\n]*"}" pushfile(10); X X X"{$"{i}{f}{c}[^\}]*"}" ifc(); /* conditional compile */ X X"{$"{e}{l}{s}{e}[^\}]*"}" elsec(); X X"{$"{e}{n}{d}{c}[^\}]*"}" endc(); EOF