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