v07i086: Tools to restart YACC parses
sources-request at mirror.UUCP
sources-request at mirror.UUCP
Thu Dec 11 04:22:38 AEST 1986
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 at emory BITNET: arnold at emoryu1
ARPA: arnold%emory.csnet at 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
More information about the Mod.sources
mailing list