%{
static char *RcsId = 
    "$Header: /src/bin/make/m/gram.y,v 4.3 84/04/24 21:19:03 crl Exp $";

/* static char *sccsid = "@(#)gram.y	4.1 (Berkeley) 81/02/28"; */

/*
 * $Log:	/src/bin/make/m/gram.y,v $
 * Revision 4.3  84/04/24  21:19:03  crl
 * Cleaned up error mesgs.
 * 
 * Revision 4.2  83/07/22  14:12:59  crl
 * Prettied up indention.
 * 
 * Revision 4.1  83/03/01  00:44:51  crl
 * Vanilla 4.1 version
 */

#include "defs.h"

%}

%term NAME SHELLINE START MACRODEF COLON DOUBLECOLON GREATER
%union
{
	struct shblock *yshblock;
	struct depblock *ydepblock;
	struct nameblock *ynameblock;
}

%type <yshblock> SHELLINE, shlist, shellist
%type <ynameblock> NAME, namelist
%type <ydepblock> deplist, dlist


%%

%{
	struct depblock *pp;
	FSTATIC struct shblock *prevshp;

	FSTATIC struct nameblock *lefts[NLEFTS];
	struct nameblock *leftp;
	FSTATIC int nlefts;

	struct lineblock *lp, *lpp;
	FSTATIC struct depblock *prevdep;
	FSTATIC int sepc;
%}


file:
| file comline
;

comline:  
START
| MACRODEF
| START namelist deplist shellist = {
	while( --nlefts >= 0) {
		leftp = lefts[nlefts];
		if(leftp->septype == 0)
			leftp->septype = sepc;
		else if(leftp->septype != sepc)
			fprintf(stderr, "%s: Inconsistent rules lines for `%s'\n",
					progname, leftp->namep);
		else if(sepc == ALLDEPS && *(leftp->namep) != '.' && $4 != 0) {
			for(lp=leftp->linep; lp->nxtlineblock!=0; lp=lp->nxtlineblock)
				if(lp->shp)
					fprintf(stderr, "%s: Multiple rules lines for `%s'\n",
						progname, leftp->namep);
		}

		lp = ALLOC(lineblock);
		lp->nxtlineblock = NULL;
		lp->depp = $3;
		lp->shp = $4;

		if(! unequal(leftp->namep, ".SUFFIXES") && $3 == 0)
			leftp->linep = 0;
		else if(leftp->linep == 0)
			leftp->linep = lp;
		else {
			for(lpp = leftp->linep; lpp->nxtlineblock;
						lpp = lpp->nxtlineblock)
				;
			if(sepc == ALLDEPS && leftp->namep[0] == '.')
				lpp->shp = 0;
			lpp->nxtlineblock = lp;
		}
	}
}
| error
;

namelist: 
NAME	= { 
	lefts[0] = $1; 
	nlefts = 1; 
}
| namelist NAME	= { 
	lefts[nlefts++] = $2;
	if(nlefts >= NLEFTS)
		fatal("Too many lefts"); 
}
;

deplist:
{
	char junk[10];
	sprintf(junk, "%d", yylineno);
	fatal1("Must be a separator on rules line %s", junk);
}
| dlist
;

dlist:  
sepchar	= { 
	prevdep = 0;  
	$$ = 0; 
}
| dlist NAME	= {
	pp = ALLOC(depblock);
	pp->nxtdepblock = NULL;
	pp->depname = $2;
	if(prevdep == 0)
		$$ = pp;
	else
		prevdep->nxtdepblock = pp;
	prevdep = pp;
}
;

sepchar:  
COLON 	= { 
	sepc = ALLDEPS; 
}
| DOUBLECOLON	= { 
	sepc = SOMEDEPS; 
}
;

shellist:	
= {
	$$ = 0; 
}
| shlist = { 
	$$ = $1; 
}
;

shlist:	
SHELLINE   = { 
	$$ = $1;  
	prevshp = $1; 
}
| shlist SHELLINE = { 
	$$ = $1;
	prevshp->nxtshblock = $2;
	prevshp = $2;
}
;

%%

char *zznextc;	/* zero if need another line; otherwise points to next char */
int yylineno;
extern FILE * fin;

yylex()
{
	register char *p;
	register char *q;
	char word[INMAX];

	if (zznextc == 0)
		return(nextlin());

	while (isspace(*zznextc))
		++zznextc;

	if(*zznextc == '\0')
		return(nextlin());

	if(*zznextc == ':') {
		if(*++zznextc == ':') {
			++zznextc;
			return(DOUBLECOLON);
		}
		else
			return(COLON);
	}

	if(*zznextc == '>') {
		++zznextc;
		return(GREATER);
	}

	if(*zznextc == ';')
		return( retsh(zznextc) );

	p = zznextc;
	q = word;

	while( ! ( funny[*p] & TERMINAL) )
		*q++ = *p++;

	if(p != zznextc) {
		*q = '\0';
		if((yylval.ynameblock = srchname(word)) == 0)
			yylval.ynameblock = makename(word);
		zznextc = p;
		return(NAME);
	}

	else {
		fprintf(stderr,"%s: Bad character %c (octal %o), line %d",
				progname, *zznextc, *zznextc, yylineno);
		fatal((char *) NULL);
	}
	return(0);	/* never executed */
}

retsh(q)
char *q;
{
	register char *p;
	struct shblock *sp;

	for(p = q+1; *p==' ' || *p == '\t'; ++p)
		;

	sp = ALLOC(shblock);
	sp->nxtshblock = NULL;
	sp->shbp = (fin == NULL ? p : copys(p) );
	yylval.yshblock = sp;
	zznextc = 0;
	return(SHELLINE);
}

nextlin()
{
	static char yytext[INMAX];
	static char *yytextl	= yytext+INMAX;
	char *text, templin[INMAX];
	register char c;
	register char *p, *t;
	char lastch, *lastchp;
	extern char **linesptr;
	int incom;
	int kc;

again:

	incom = NO;
	zznextc = 0;

	if(fin == NULL)	{
		if( (text = *linesptr++) == 0)
			return(0);
		++yylineno;
	}

	else {
		for(p = text = yytext; p<yytextl; *p++ = kc)
			switch(kc = getc(fin)) {
			case '\t':
				if(p != yytext)
					break;
			case ';':
				incom = YES;
				break;
			case '#':
				if(! incom)
					kc = '\0';
				break;
			case '\n':
				++yylineno;
				if(p==yytext || p[-1]!='\\') {
					*p = '\0';
					goto endloop;
				}
				p[-1] = ' ';
				while((kc = getc(fin)) == '\t'
						|| kc == ' ' 
						|| kc=='\n')
					if(kc == '\n')
						++yylineno;
				if(kc != EOF)
					break;
			case EOF:
				*p = '\0';
				return(0);
			}

		fatal("line too long");
	}

endloop:

	if((c = text[0]) == '\t')
		return( retsh(text) );

	if(isalpha(c) || isdigit(c) || c==' ' || c=='.')
		for (p = text + 1; *p != '\0'; )
			if(*p == ':')
				break;
			else if(*p++ == '=') {
				eqsign(text);
				return(MACRODEF);
			}

	/* substitute for macros on dependency line up to the semi if any */

	for(t = yytext ; *t!='\0' && *t!=';' ; ++t)
		;

	lastchp = t;
	lastch = *t;
	*t = '\0';

	subst(yytext, templin);	/* Substitute for macros on dependency lines */

	if(lastch) {
		for(t = templin ; *t ; ++t)
			;
		*t = lastch;
		while( *++t = *++lastchp ) ;
	}

	p = templin;
	t = yytext;
	while( *t++ = *p++ )
		;

	for(p = zznextc = text ; *p ; ++p )
		if(*p!=' ' && *p!='\t')
			return(START);
	goto again;
}
