/* parser.c -- derived from the `LALR' parser written by Paul Mann */

#define CLASS Parser

#include <oxbow.h>

object CLASS;

instanceVars {
	PG pg;
};
#define HOWBIG 1
#define DEBUG 1

#if HOWBIG
static int maxXX_parse;
static int maxSS_parse;
static int maxSS_lex;
static int maxNS_parse;
static int maxLS_parse;
static int maxRS_parse;
#endif


/* Built in standard actions */
static void nullaction();
static void classify();
static void require();
static void doline();
static void dyntoken();

static void LoadParserPointers();

/* some imported functions */
char *strchr();
char *strrchr();
int atol();
int _strcpy(void *dst, const void *src);
void *strcpy(void *dst, const void *src);
void *strcat(void *dst, const void *src);
void *memcpy(void *dst, const void *src, int len);


/* All chunk types have the same number of bytes */

#define ASTCHUNK (pg->astchunk)
#define SYMBOLCHUNK (pg->symbolchunk)
#define TEXTCHUNK (pg->textchunk)
#define ASTSIZE (pg->astsize)


typedef struct _key
{
	unsigned long key;
	unsigned long hv;
} KEY, *KEYP;

typedef struct _psym
{/* Fits in a minimal sized AST node */
	unsigned short dat[4];		/* 8 bytes */
	KEY cat;					/* 8 bytes */
	struct _psym *next;			/* 4 bytes */
} Psym, *PsymP;

typedef struct _pbuf
{/* parser symbol table struct */
	int	size;
	void *lastbin;	/* for DelLast */
	int headbin;	/* for seq access */
	PsymP headptr;	/* ditto */
	PG *wpg;
	PsymP bins[0];
} *PbufP;

void *
NewParserSymTable(PG *pg, int size)
{
PbufP buf = callocC(pg->category, 1, size*sizeof(PsymP) + sizeof(struct _pbuf));
	buf->size = size;
	buf->wpg = pg;
	return buf;
}

cmethod vobject
New(object self, char *language, int astsize)
{
void *lfile;
char lodname[200];
char filename[40];
char *cp;
PTABLE *pp;
LTABLE *lp;
long *symbase;
char *symptr;
char buf[100];
Item item;
int i;
object instance;
PG *pg;
int oldload = 0;
AstP fnode;

	if(IsaClass(self))
		instance  = cSuper(gNew)(self);
	else {
		instance = self;
	}
	pg = (PG *)ivPtr(instance);
	pg->category = NewMallocCategory();

	strcpy(pg->language, language);
	strcpy(filename, language);
	strcat(filename, ".lod");

	if((cf_find_file("oxlib.cff", lodname)))
	{/* The archive file */
		strcat(lodname, "/language/");
		strcat(lodname, filename);
	} else {
		pg->errors = 1;
		return instance;
	}
	if(cfqget(MEMTEMP, filename, strlen(filename), &item, 8) == FOUND)
	{/* Parser tables for this language have been loaded earlier */
		pg->lod_table = item.a1;
		oldload = 1;
	}
	else
	{/* Load the parser tables for this language */
		if((lfile = cfopen(lodname, F_STAT, NULL)))
		{
		int size;
			size = cfseek(lfile, 0, S_END);
			pg->lod_table = malloc(size);
			cfseek(lfile, 0, S_SET);
			cfread(lfile, pg->lod_table, size);
			cfclose(lfile);
			item.item = 0;
			item.a1 = pg->lod_table;
			cfinsert(MEMTEMP, filename, strlen(filename), &item);
		}
		else {
			pg->errors = 2;
			return instance;
		}
	}

	/* using the size of the caller's AST node, compute suitable chunks */

	if(astsize < sizeof(AST_NODE))
		astsize = sizeof(AST_NODE);

	if(astsize & 3)
		astsize += 4-(astsize&3);

	ASTCHUNK = 8184/astsize;
	TEXTCHUNK = ASTCHUNK * astsize;
	SYMBOLCHUNK = TEXTCHUNK / 4;
	ASTSIZE = astsize;
	
	/* Parser table init */
	pg->ptab = callocC(pg->category, 1, sizeof(PTABLE));	
	pp = pg->ptab;
	pp->pg = pg;
	LoadParserPointers(pp, pg->lod_table, 1);
	pp->linksize = SYMBOLCHUNK;
	pp->link = callocC(pg->category, 1, SYMBOLCHUNK*sizeof(void*));


	/* Lexer table init */
	pg->ltab = callocC(pg->category, 1, sizeof(LTABLE));
	lp = pg->ltab;
	lp->pg = pg;
	LoadParserPointers(lp, (LODTABLE *)(((char *)pg->lod_table) + pg->lod_table->next), 0);

	/* Action pointers, applies to both parser and lexer */
	pg->n_actions = lp->n_actions;	/* lexer has the best action count */
	pg->ACTIONS = (PACTIONS)lp->ACTIONS;
	pg->ACTIONSTRINGS = lp->ACTIONSTRINGS;

	/* Allocate the initial ast space */
	pg->pat.free = callocC(pg->category, 1, ASTCHUNK*astsize);	
	pg->pat.freecnt = pg->pat.cnt = ASTCHUNK;
	fnode = pg->pat.free;
	for(i = 0; i < ASTCHUNK-1; ++i)
	{
		fnode->down = (AstP)(((char*)fnode)+ASTSIZE);
		fnode = fnode->down;
	}

	/* Allocate and initialize the parser symbol table */
	PARSERSYMBOLS = callocC(pg->category, 1, SYMBOLCHUNK*sizeof(PARSER_SYMBOL));
	pg->symhandle = NewParserSymTable(pg, 2003);

	/* Allocate and initialize the first text accumulation chunk */
	pg->chunkbase = callocC(pg->category, 1, TEXTCHUNK);
	pg->chunkend = pg->chunkbase+TEXTCHUNK-1;
	pg->symbase = pg->chunkbase+4;
	pg->symend = pg->symbase;

	/* Enter all the terminal symbols of the grammar */
	symbase = pp->G_symbol;
	symptr = (char *)symbase;

	for(i = 0; i < pp->n_terms; ++i)
	{
		NewParserSymbol(pg, symptr+symbase[i]);
	}

	/* Set the values for the automatic node ids */
	{
	int	symnum = -1;
	int symval;
	int subsym = 0;
	char *cp = NULL;
		for(i = 0; i < pp->n_rules; ++i)
		{
		long *vp;	/* assume callers node ids are sizeof(long) */
			if(pp->PL[i] & PL_MAKENODE)
			{
				if(pp->Head[i] != symnum)
				{
					subsym = 0;
					symnum = pp->Head[i];
					cp = symptr + symbase[symnum];
				}
				/* e.g. _cInitDeclarator_0 */
				cfsprintf(buf, "_%s%s_%d", pg->language, cp, subsym);

				/* use dynamic linker here */
				symval = (symnum<<6) | subsym;
				if((vp = oxlink_find_bare_symb(buf)) != NULL)
				{/* vp is now the address of the symbol in caller memory */
					*vp = symval;  /* caller defined a global symbol he wants */
				}
				++subsym;
			}
		}
	}
	/* Set up the function pointers for actions */
	if(!oldload)
	{/* THIS WOULD WORK WITHOUT TESTING FOR 'oldload', just saving time */
	PACTIONS pa = pg->ACTIONS;
	char *cp;
	int mask;
	int j;

		for(i = 0; i < pg->n_actions; ++i, ++pa)
		{
		/* 
			pa->func is initially set to an offset into ACTIONSTRINGS 
					convert it into a function pointer
		*/
			cp = pg->ACTIONSTRINGS + (int)pa->func; /* funcname is first entry */
			if(!strcmp(cp, "classify"))
				pa->func = classify;
			else if(!strcmp(cp, "require"))
				pa->func = require;
			else if(!strcmp(cp, "doline"))
				pa->func = doline;
			else if(!strcmp(cp, "dyntoken"))
				pa->func = dyntoken;
			else
			{/* Concoct the real user action name and find the function */
				/* e.g. _dosomething_ada_ */
				cfsprintf(buf, "_%s_%s_", cp, pg->language);
				if((pa->func = oxlink_find_bare_func(buf)) == NULL)
				{/* Not already in core */
					if((pa->func = oxlink_load_bare_symb(buf, 1)) == NULL)
					{/* And can't load it from the archive, punt to nothing */
						pa->func = nullaction;
					}
				}
			}
			/* 
				Set up the args:
				arg[0] contains a bitmask in the high order 16 bits
					and argcnt in the low 16 bits 
			*/
			mask = (pa->args[0] & 0xffff0000) >> 16;
			pa->args[0] &= 0x0000ffff;
			for(j = 1; j <= pa->args[0]; ++j)
			{
				if(mask & 1)
				{/* This arg is really a pointer to a string */
					pa->args[j] += (int)pg->ACTIONSTRINGS;
				}
				mask >>= 1;
			}
		}
	}
	/* Set a few constants */
	pg->parsecnt = 2;
	pg->ROOT = 0;
	pg->root = 0;
	pg->line_numb = 1;
	pg->line_char = 0;
#if HOWBIG
	maxXX_parse = 
		maxSS_parse =
			maxSS_lex =
				maxNS_parse =
					maxLS_parse = 
						maxRS_parse = 0;
#endif
	return instance;
}
imethod void
Dispose(object self)
{
PG *pg = (PG *)ivPtr(self);

	freecat(pg->category);
	super(gDispose)(self);
}


static unsigned short bitlist[16] = {
			0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
			0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000
	};
#define BITSET(a,b,c) ( a[b+(c>>4)] & bitlist[c&0xf] )

static int
do_parse (PTABLE *ptab)
{
short i, x;
short *r;
short rule;
unsigned base;
short state;
int token;
short stacktop, *SS;
PG *pg;
int rulesize;
int debug;
int xx;
AstP *link;
int lexindx;

/* NESTED SUBROUTINE Make AST node and/or call semantic action. */

static void attach ()
{
int k, i, j;
AstP np, lp, p;
int rulesize;
AstP newnode;
int savxx;
short *pxx;

	rulesize = ptab->PL[rule] & 0x000f;
	if(rulesize == 15)
		rulesize = -1;

	ptab->NS -= rulesize;         /* Reduce the stack.        */
	ptab->LS -= rulesize;

	for (k=0,np=0,i=rulesize; i >= 0; i--) /* Look at each tail spot.  */
	{
		if (ptab->NS[i])    /* If node pointer there.   */
		{
		int xxx;
			k++;							/* Active node marker.  */
			p = ptab->NS[i];				/* Last node in chain */
			xxx = ptab->LS[i];

			if (np)							/* If one waiting.          */
			{
				lp = link[xxx];
				lp->down = np;				/* Connect last to next.    */
				link[xxx] = link[savxx];	/* Move link */
#if DEBUG
if(debug)cfprintf("Parse: Set DOWN at %x to %x\n", lp, np);
#endif
			}
			np = p;							/* Get this ones address.    */
			savxx = xxx;
		}
	}
	if (ptab->PL [rule] & PL_MAKENODE)
	{
	  newnode = NewAstNode(pg,(ptab->Head[rule]<<6)|(rule-ptab->rBase[rule]),0);
		if(!pg->ROOT)
			pg->ROOT = newnode;

		if (k > 0)
		{/* Make branch */
			newnode->right = np;            /* Pointer to child.        */
			newnode->fileno = (unsigned char)pg->line_file; /* current file */
			newnode->colno = (unsigned char)pg->line_char; /* column of text */
			newnode->lineno = (unsigned short)pg->line_numb;/* Line number. */
			if (np == pg->ROOT) {
				pg->ROOT = newnode; /* Root points to parent.   */
#if DEBUG
if(debug)cfprintf("Parse: Reset ROOT to %x\n", newnode);
#endif
			}
#if DEBUG
if(debug)cfprintf("Parse: New Branch at %x %s_%d  right=%x down=%x\n",
	newnode,GetH_symbol(pg, ptab->Head[rule]),rule-ptab->rBase[rule],np, newnode->down);
#endif
		}
		else
		{/* Make leaf */
		int lookback = (pg->LSP-lexindx)&3;
			newnode->Tindx = pg->L_stack[lookback+1];	/* Terminal type */
			newnode->symb = pg->L_stack[lookback];		/* Symnum */
			newnode->fileno = (unsigned char)pg->line_file; /* current file */
			newnode->colno = (unsigned char)pg->line_char; /* column of text */
			newnode->lineno = (unsigned short)pg->line_numb;/* Line number. */
#if DEBUG
if(debug)cfprintf("Parse: New Leaf at %x %s_%d right=%x down=%x\n", 
	newnode,GetH_symbol(pg, ptab->Head[rule]),rule-ptab->rBase[rule], newnode->right, newnode->down);
#endif
		}

		/* Prevent the unconstrained growth of the link array */
		for(pxx = ptab->LStop, xx = 0; pxx <= ptab->LS; ++pxx)
		{
			if(*pxx > xx)
				xx = *pxx;
		}

		if(++xx == ptab->linksize)
		{/* Extend the link array */
			ptab->linksize += SYMBOLCHUNK;
			link = reallocC(pg->category, link, ptab->linksize*sizeof(int));
			ptab->link = link;
		}
		np = newnode; 
		link[xx] = np;
	}
	*(ptab->NS) = np;
	*(ptab->LS) = xx;

#if HOWBIG
	if(xx > maxXX_parse)
		maxXX_parse = xx;
#endif

/* Call actions for a rule */
	if (ptab->PL [rule] & PL_ACTION)        /* Call action?             */
	{
	PACTIONS ap = &pg->ACTIONS[(ptab->PL[rule]>>7)&511];
#if DEBUG
if(debug)cfprintf("Parse: Call Action for rule:%d\n", rule);
#endif
		ptab->node = np;
		pg->actionerr = NULL;
		(*ap->func) (pg, ap->args); /* Call action.             */

		if (ptab->PL [rule] & PL_DELTREE)     /* Delete the subtree?      */
		{
#if DEBUG
if(debug)cfprintf("Parse: Prune Tree at rule:%d\n",rule);
#endif
			*(ptab->NS) = 0;  /* Reset np to 0.           */
			PruneAstTree(pg, np, 0, 0); /* Get rid of all nodes including root */
			pg->ROOT = np;		/* will be next node allocated */
		}
	}
}/* END attach() */
/* NESTED SUBROUTINE */
static int procrules()
{
	/* Attach all currently known rules to the ast */
	ptab->rule = rule;
	for (r = ptab->RStop; r < ptab->Rs; )
	{
		rule = *r++;
		if(rule < 0)
		{/* Shift occured */
			*(++(ptab->NS)) = (AstP)0; /* Set node pointer to zero. */
			*(++(ptab->LS)) = 0;
			lexindx -= 2;	/* lex symbol is one token closer */
		}
		else
		{
			attach ();                    /* Attach node to AST.      */
			if(pg->actionerr) {
#if DEBUG
if(debug)cfprintf("PARSE action error exit\n");
#endif
				return 1;
			}
		}
	}
	ptab->Rs = ptab->RStop;			/* Reset reduction stk ptr. */

#if HOWBIG
	if((ptab->NS - ptab->NStop) > maxNS_parse)
			maxNS_parse = ptab->NS - ptab->NStop;
	if((ptab->LS - ptab->LStop) > maxLS_parse)
		maxLS_parse = ptab->LS - ptab->LStop;
#endif
	return 0;
} /* END: procrules */

/* do_parse */
	pg = ptab->pg;
	SS = ptab->SS;
	state = ptab->state;
	token = ptab->token;
	link = ptab->link;
	xx = ptab->xx;
	debug = pg->debug & 1;
	lexindx = 4;
#if DEBUG
if(debug)cfprintf("ParseSTART: state=%d token=%d\n", state, token);
#endif
Scan:
	if(token < 0)
	{
		if(procrules())
			return -1;
		/* get new token */
		ptab->state = state;
		ptab->SS = SS;
		ptab->xx = xx;
		return(0);
	}

 /* Test for Shift, and Shift-Reduce */

	base = state * ptab->bitwords;
	if(BITSET(ptab->M_bits, base, token))
	{
		x = ptab->MT_tran[ ptab->MT_beg[state] + ptab->token];
		*++(SS) = state;                 /* Put state on parse stack.*/
#if HOWBIG
		if((SS - ptab->SStop) > maxSS_parse)
			maxSS_parse = SS - ptab->SStop;
#endif
		if (SS >= ptab->SSmax)     /* If parse stack too large.*/
		{
			cfprintf("Parse: stack overflow \n");
			OXPORT_crash("");
		}
		*ptab->Rs++ = -1;				/* Mark reduction stack as shifted */
		token = -1;						/* Indicate token consumed   */
		if(x > 0)
		{
			state = x;
#if DEBUG
if(debug)cfprintf("Parse: shift to state %d\n", state);
#endif
			goto Scan;		/* Shift only */
		}
		/* --- REDUCE -----------------------*/

Neg:	rule = -x;                              /* Make positive.           */
Reduce:
		rulesize = ptab->PL [rule] & 0x000f;
		if(rulesize == 15)
			rulesize = -1;
		SS -= rulesize;
		if(rulesize == -1)
		{
			*SS = state;                       /* Stack current state.     */
		}
		stacktop = *SS;
		*ptab->Rs++ = rule;
#if HOWBIG
		if((ptab->Rs - ptab->RStop) > maxRS_parse)
			maxRS_parse = ptab->Rs - ptab->RStop;
#endif
#if DEBUG
if(debug)cfprintf("Parse: stack rule %d, new state is %d head=%d\n",
								rule, stacktop, ptab->Head[rule]);
#endif
		/* Check for goal */
		if(rule == 0)
		{
			if(procrules())
				return -1;
			pg->root = pg->ROOT;
#if DEBUG
if(debug)cfprintf("PARSE DONE rootnode=%x\n", pg->root);
#endif
			return 1;
		}

		/* Check non terminal transitions for current state */

		base = stacktop * ptab->bitwords;
		if(BITSET(ptab->M_bits, base, ptab->Head[rule]))
		{
			x = ptab->MN_tran[ptab->MN_beg[stacktop]+(ptab->Head[rule]-ptab->n_terms)];
			if(x > 0)
			{
				state = x;
#if DEBUG
if(debug)cfprintf("Parse: NT trans to state %d\n", state);
#endif
				goto Scan;
			}
#if DEBUG
if(debug)cfprintf("Parse: NT reduce to rule %d, state=%d\n",-x, stacktop);
#endif
				goto Neg;
		}
#if DEBUG
if(debug)cfprintf("Parse: NO nt tran for state %d\n",stacktop);
#endif
		goto Scan;

	}/* END of shift/reduce test */

	/* Check for pure reductions */
#if DEBUG
if(debug)cfprintf("Parse: check reductions for state %d\n", state);
#endif
	rule = ptab->D_red [state];
	if(rule >= 0)	{
#if DEBUG
if(debug)cfprintf("Parse: use default reduction to rule %d\n", rule);
#endif
		goto Reduce;   /* Default reduction       */
	}
	if (rule == -32767)
	{
		ptab->state = state;
		ptab->token = token;
		ptab->SS = SS;
		ptab->xx = xx;
#if DEBUG
if(debug)cfprintf("PARSE error exit\n");
#endif
		return(-1);
	}

	  /* Check multiple reductions */
#if DEBUG
if(debug)cfprintf("Parse: check multiple reductions in state %d\n", state);
#endif
	for (i = ptab->R_start [state]; i < ptab->R_start [state+1]; i++)
	{
		if (ptab->R_symbol [i] == token)            /* Found?     */
		{
			rule = ptab->R_prod [i];
#if DEBUG
if(debug)cfprintf("Parse: multiple reduction to rule %d token=%d\n", rule, token);
#endif
			goto Reduce;
		}  
	}
	rule = -rule;                                /* Multiple default.      */
#if DEBUG
if(debug)cfprintf("Parse: use multiple default rule %d\n", rule);
#endif
	goto Reduce;

}/* END OF do_parse() */

/* THIS LEXER USES PARSER STYLE TABLES [Improve me NDC] */
static void
new_chunk(PG *pg, char tok)
{
long *tempbase;
int chunksize;

	if(pg->symbase == pg->chunkbase+4)
	{/* The whole current chunk is worthless (monster symbol spans a chunk) */
		tempbase = *(long**)pg->chunkbase; /* pointer to prev chunk */
		freeC(pg->category, pg->chunkbase);
		pg->chunkbase = (char *)tempbase;
	}
	/* Compute size of new chunk */
	if(pg->symspot >= TEXTCHUNK-5)
		 chunksize = pg->symspot+TEXTCHUNK+5;
	else chunksize = TEXTCHUNK;

 	tempbase = mallocC(pg->category, chunksize);
	*tempbase = (long)pg->chunkbase;	/* pointer to prev chunk */
	pg->chunkbase = (char*)tempbase;
	pg->chunkend = pg->chunkbase+chunksize-1;
	memcpy(pg->chunkbase+4, pg->symbase, pg->symspot);
	pg->symbase = pg->chunkbase+4;
	pg->symend = pg->symbase+pg->symspot;
	*pg->symend++ = tok;
	*pg->symend = 0;
	((char*)&pg->symhash)[pg->symspot++ & 3] ^= tok; /* running hash */
}
static inline void
add_token(PG *pg, char tok)
{
	if(pg->symend < pg->chunkend) {
		*pg->symend++ = tok;
		*pg->symend = 0;
		((char*)&pg->symhash)[pg->symspot++ & 3] ^= tok; /* running hash */
	}
	else new_chunk(pg, tok);
}
static int
do_lex (LTABLE *ltab)
{
short i, x;
short rule;
unsigned base;
short state;
int token;
short stacktop;
short *SS;
PG *pg;
int rulesize;
int debug;

	pg = ltab->pg;
	SS = ltab->SS;
	state = ltab->state;
	token = ltab->token;
	debug = pg->debug & 2;
#if DEBUG
if(debug)cfprintf("LexSTART: char %c (0x%x)\n", token, token);
#endif
Scan:
	if(token < 0)
	{/* get new token */
		ltab->state = state;
		ltab->token = token;
		ltab->SS = SS;
#if DEBUG
if(debug)cfprintf("Lex: return for next char\n");
#endif
		return(0);
	}

	/* Test for Shift, and Shift-Reduce */

	base = state * ltab->bitwords;
	if(BITSET(ltab->M_bits, base, token))
	{
		x = ltab->MT_tran[ ltab->MT_beg[state] + ltab->token];
		*++(SS) = state;                 /* Put state on parse stack.*/
#if 0
#if HOWBIG
		if((SS - ltab->SStop) > maxSS_lex)
			maxSS_lex = SS - ltab->SStop;
#endif
		if (SS >= ltab->SSmax)     /* If parse stack too large.*/
		{
			cfprintf("Lex: stack overflow \n");
			OXPORT_crash("");
		}
#endif
		/* Put char in textchunk */

		add_token(pg, token);
#if DEBUG
if(debug)cfprintf("Lex consume char\n");
#endif
		token = -1;                          /* Indicate token consumed   */
		if(x > 0)
		{
			state = x;
#if DEBUG
if(debug)cfprintf("Lex: shift to state %d\n", state);
#endif
			goto Scan;		/* Shift only */
		}
		/* --- REDUCE -----------------------*/

Neg:	rule = -x;                              /* Make positive.           */
Reduce:
		rulesize = ltab->PL [rule] & 0x000f;
		if(rulesize == 15)
			rulesize = -1;
		SS -= rulesize;
		if(rulesize == -1)
		{
			*SS = state;                       /* Stack current state.     */
		}
		stacktop = *SS;
#if DEBUG
if(debug)cfprintf("Lex: reduce to rule %d, new state is %d head=%d\n",
								rule, stacktop, ltab->Head[rule]);
#endif
		if(rule <= ltab->TM)
		{
			ltab->lextoken = ltab->LGT[rule];
			state = 0;
			SS = ltab->SStop;
			if(ltab->lextoken > 0)
			{
#if DEBUG
if(debug)cfprintf("Lex: add symbol %s\n", pg->symbase);
#endif
				pg->L_stack[pg->LSP] = NewParserSymbol(pg, pg->symbase);

				ltab->state = state;
				ltab->SS = SS;
				ltab->token = token;
				ltab->rule = rule;
				if(ltab->PL [rule] & PL_ACTION)
				{
				PACTIONS ap = &pg->ACTIONS[(ltab->PL[rule]>>7)&511];  
					(*ap->func) (pg, ap->args);
				}

				if(ltab->lextoken > 0) {
#if DEBUG
if(debug)cfprintf("Lex: exit with token=%d\n", ltab->lextoken);
#endif
					pg->LSP = (pg->LSP+1) & 3;
					pg->L_stack[pg->LSP] = ltab->lextoken;
					pg->LSP = (pg->LSP+1) & 3;
					
					return	ltab->lextoken;
				}
				else 
				{/* Token was cancelled by action */
					goto Scan;
				}
			}
			else
			{/* IGNORE rule */
#if DEBUG
if(debug)cfprintf("Lex: ignore rule %d\n", rule);
#endif
				pg->symend = pg->symbase;
				pg->symhash = 0;
				pg->symspot = 0;
				goto Scan;
			}
		}
		/* Check non terminal transitions for current state */

		base = stacktop * ltab->bitwords;
		if(BITSET(ltab->M_bits, base, ltab->Head[rule]))
		{
			x = ltab->MN_tran[ltab->MN_beg[stacktop]+(ltab->Head[rule]-ltab->n_terms)];
			if(x > 0)
			{
				state = x;
#if DEBUG
if(debug)cfprintf("Lex: NT trans to state %d\n", state);
#endif
				goto Scan;
			}
#if DEBUG
if(debug)cfprintf("Lex: NT reduce to rule %d, state=%d\n",-x, stacktop);
#endif
				goto Neg;
		}
#if DEBUG
if(debug)cfprintf("Lex: NO nt tran for state %d\n",stacktop);
#endif
		goto Scan;

	}/* END of shift/reduce test */

	/* Check for pure reductions */
#if DEBUG
if(debug)cfprintf("Lex: check reductions for state %d\n", state);
#endif
	rule = ltab->D_red [state];
	if(rule >= 0)	goto Reduce;   /* Default reduction       */
	if (rule == -32767)
	{
		pg->symspot = 0;
		pg->symhash = 0;
		pg->symend = pg->symbase;
		ltab->state = 0;
		ltab->SS = ltab->SStop;
#if DEBUG
if(debug)cfprintf("Lex: return ERROR\n");
#endif
		return(-1);
	}
#if DEBUG
if(debug)cfprintf("Lex: Check multiple reductions in state %d\n", state);
#endif
	  /* Check multiple reductions */
	for (i = ltab->R_start [state]; i < ltab->R_start [state+1]; i++)
	{
		if (ltab->R_symbol [i] == token)            /* Found?              */
		{
			rule = ltab->R_prod [i];
#if DEBUG
if(debug)cfprintf("Lex: multiple reduction to rule %d token=%d\n", rule, token);
#endif
			goto Reduce;
		}  
	}
	rule = -rule;                                /* Multiple default.      */
#if DEBUG
if(debug)cfprintf("Lex: use multiple default rule %d\n", rule);
#endif
	goto Reduce;

}/* END OF do_lex() */

static short
read_object(void *obj, PG *pg)
{
	if(pg->obj_inbufcnt <= 0) {
		pg->obj_inbufsize = 
			pg->obj_inbufcnt = (*pg->readfunc)(obj, pg->obj_inbuf, 128);
	}
	if(pg->obj_inbufcnt > 0 )
		return pg->obj_inbuf[pg->obj_inbufsize - pg->obj_inbufcnt--];
	return -1;
}

imethod int
Parse(object self, void *is, char *filename)
{
PG *pg = (PG *)ivPtr(self);
LTABLE *lp = pg->ltab; 
PTABLE *pp = pg->ptab;
int	debug = pg->debug & 4;
int isobject;

	pg->in_file = is;
	if(IsObj((object)is))
	{
		isobject = 1;
		pg->readfunc = (void*)imiPointer((object)is, gRead);
	}
	else
		isobject = 0;

	for(;;)
	{
	short lextoken = 0;
	int result;
		/* Run the lexer */
		while(lextoken == 0)
		{
			if(lp->token < 0)
			{
newchr:
				if(isobject)
					lp->token = read_object(is, pg);
				else
					lp->token = cfgetc(((cfFILE*)is));

				if(lp->token == 0)
					goto newchr;
				if(lp->token < 0)
				{/* EOF */
					lp->token = 0;
				}
				else lp->token &= lp->maxtoken;
				if(lp->token == '\n')
				{
					++pg->line_numb;
					pg->line_char = 0;
if(debug)
cfeprintf("\n");
				}
				else if(lp->token == '\r')
				{
					goto newchr;
				}
				else
				{
					++pg->line_char;
if(debug)
cfeprintf("%c", lp->token);
				}
			}
			lextoken = do_lex(lp);
			if(lextoken < 0)
			{
				return(lextoken);
			}
		}
		/* Run the parser */
		pp->token = lextoken;
		result = do_parse(pp);
		if(result)
		{
			if(result < 0)
			{
				return(1);
			}
			else
			{
				if(pg->root == 0)
					return 2;
				return(0);
			}
		}
	}
}

InitMethod()
{
	if (CLASS)
		return;
	CLASS = gNew(Class, cName, ivSize, 0, END);
	
	cMethod(New);
	iMethod(New);

	iMethod(Parse);
	iMethod(Dispose);
	iMethodFor(gDeepDispose, Dispose);
	iMethodFor(gGCDispose, Dispose);

	gDontCollect(CLASS);
}
/* COMMON STUFF */

static void
LoadParserPointers(void *yp, LODTABLE *lp, int notlex)
{
char *xp = (char *) lp;
PTABLE *pp = yp;
#define SPOINTER(a) ((short *)(xp + lp->a))
#define CPOINTER(a) ((char *)(xp + lp->a))
#define LPOINTER(a) ((long *)(xp + lp->a))
#define VALUE(a) ((short)(lp->a))

	pp->D_red = SPOINTER(t_D_red);
	pp->R_start = SPOINTER(t_R_start);
	pp->R_symbol = SPOINTER(t_R_symbol);
	pp->R_prod = SPOINTER(t_R_prod);
	pp->Head = SPOINTER(t_Head);
	pp->rBase = SPOINTER(t_rBase);
	pp->MT_beg = SPOINTER(t_MT_beg);
	pp->MT_tran = SPOINTER(t_MT_tran);
	pp->MN_beg = SPOINTER(t_MN_beg);
	pp->MN_tran = SPOINTER(t_MN_tran);
	pp->M_bits = SPOINTER(t_M_bits);
	pp->LGT = SPOINTER(t_LGT);

	pp->G_symbol = LPOINTER(t_G_symbol);
	pp->PL = SPOINTER(t_PL);
	pp->ACTIONS = LPOINTER(t_ACTIONS);
	pp->ACTIONSTRINGS = CPOINTER(t_ACTIONSTRINGS);
	pp->bitwords = VALUE(bitwords);
	pp->n_terms = VALUE(n_terms);
	pp->n_vars = VALUE(n_vars);
	pp->n_symbs = VALUE(n_symbs);
	pp->n_states = VALUE(n_states);
	pp->n_rules = VALUE(n_rules);
	pp->n_actions = VALUE(n_actions);
	pp->TM = VALUE(TM);
	pp->maxtoken = VALUE(maxtoken);
	pp->token = '\n';

	pp->SS = pp->SStop;
	pp->SSmax = pp->SStop + PARSER_STKSIZE - 2;
	if(notlex)
	{
		pp->NS = pp->NStop;
		pp->LS = pp->LStop;
		pp->Rs = pp->RStop;
	}
}

/* ========== Access to the parser file stack (nested includes) ==== */
void
ParserPush(PG *pg, int data)
{
	if(pg->fstack_depth < 64)
	{
		pg->fstack[pg->fstack_depth++] = data;
	}
}
int
ParserPop(PG *pg)
{
	if(pg->fstack_depth > 0)
	{
		return pg->fstack[--pg->fstack_depth];
	}
	return 0;
}
int
ParserStackdepth(PG *pg)
{
	return pg->fstack_depth;
}
/* ================ END access to the parser file stack ================= */

static void
hash(void *key, KEY *cat)
{
	cat->key = *((unsigned long *)key);
	cat->hv = (cat->key * 1103515245UL) + 12345;
}
static int
lookup(PG *pg, char *name, void *result)
{
char *cp = name;
unsigned long myhash = 0;
unsigned int symspot = 0;
PsymP sp;
KEY cat;
PbufP tbl = pg->symhandle;

	while(*cp) {
		((char*)&myhash)[symspot++ & 3] ^= *cp++;
	}
	hash(&myhash, &cat);
	if((sp = tbl->bins[cat.hv % tbl->size]))
	{
	  do
	  {
			if(		sp->cat.hv == cat.hv
				&&	sp->cat.key == cat.key)
			{
				/* Do final string check */
				if(	   sp->dat[1] != symspot
				    || strcmp(PARSERSYMBOLS[sp->dat[0]].name, name))
				{
					continue;
				}
				*((PsymP *)result) = sp;
				return 1;
			}
	  } while((sp = sp->next));
	}
	return 0;
}
int
NewParserSymbol(PG *pg, const char *name)
{
KEY cat;
PsymP *binp;
PsymP sp;
PbufP tbl = pg->symhandle;
int symnum;

	if(name != pg->symbase)
	{/* From outside, add this symbol to the textchunk */
	const char *cp = name;
		while(*cp)
			add_token(pg, *cp++);
	}
	hash(&pg->symhash, &cat);

	binp = &(tbl->bins[cat.hv % tbl->size]);

	if((sp = *binp))
	{
		do
		{
			if(		sp->cat.hv == cat.hv
				&&	sp->cat.key == cat.key)
			{
				/* Do final string check */
				if(	   sp->dat[1] != pg->symspot
					|| strcmp(PARSERSYMBOLS[sp->dat[0]].name, name))
				{
					continue;	/* NO MATCH */
				}

				/* FOUND */
				/* Flush the string which was built up in the text chunk */
				pg->symend = pg->symbase;
				pg->symspot = 0;
				pg->symhash = 0;

				/* Return data stored in the symbol table */
				pg->lastsymnum = sp->dat[0];
				pg->lastclass = sp->dat[2];
				return sp->dat[0];
			}
		} while((sp = sp->next));
	}

	/* NOT FOUND, ENTER A NEW SYMBOL */
	symnum = pg->pst.cnt;
	if(symnum && ((symnum % SYMBOLCHUNK) == 0))
	{/* Allocate more contiguous symbol string pointer space */
		PARSERSYMBOLS = reallocC(pg->category, PARSERSYMBOLS, 
		  (symnum*sizeof(PARSER_SYMBOL))+(SYMBOLCHUNK*sizeof(PARSER_SYMBOL)));
	}
	/* link new symbol to front of bin */
	sp = (PsymP)NewAstNode(pg, 0, 0);
	if((sp->next = *binp) != 0)
		++pg->hashdups;
	*binp = sp;
	tbl->lastbin = binp;	/* used for DelLastParserSymbol */

	/* Put data in symbol chunk */
	sp->dat[0] = symnum;		/* symbol number */
	sp->dat[1] = pg->symspot;	/* length of symbol */
	sp->cat = cat;				/* hashing info */

	/* Accept the string which was built up in the text chunk */
	PARSERSYMBOLS[symnum].name = pg->symbase; /* save pointer to the string */
	pg->pst.cnt += 1;
	pg->totsymlen += pg->symspot+1;

	pg->symbase = ++pg->symend;
	pg->symspot = 0;
	pg->symhash = 0;

	/* Return new data */
	pg->lastsymnum = symnum;
	pg->lastclass = 0;
	return symnum;
}
void
DelLastParserSymbol(PG *pg)
{
	if(pg->pst.cnt > 0)
	{
	void *binp;
		if((binp = ((PbufP)pg->symhandle)->lastbin))
		{
		PsymP p = *((PsymP *)binp);
			pg->totsymlen -= p->dat[1]+1;
			pg->pst.cnt -= 1;
			*((PsymP *)binp) = p->next;
			FreeAstNode(pg, (AstP)p);
			((PbufP)pg->symhandle)->lastbin = 0;
		}
	}
}
char *
GetH_symbol(PG *pg, int numb)
{
PTABLE *pp = pg->ptab;
long *symbase = pp->G_symbol;
char *symptr = (char *)symbase;

	return symptr + symbase[numb];
}
AstP
NewAstNode(PG *pg, int id, int symb)
{
AstP node;
	if(pg->pat.freecnt > 0)
	{
		node = pg->pat.free;
		pg->pat.free = node->down;
		node->down = 0;
	}
	else
	{
	int i;
	AstP fnode;
		node = callocC(pg->category, 1, ASTCHUNK*ASTSIZE);
		pg->pat.cnt += ASTCHUNK;
		pg->pat.freecnt = ASTCHUNK;
		pg->pat.free = (AstP)(((char*)node)+ASTSIZE);
		fnode = (AstP)(((char*)node)+ASTSIZE);
		for(i = 1; i < ASTCHUNK-1; ++i)
		{
			fnode->down = (AstP)(((char*)fnode)+ASTSIZE);
			fnode = fnode->down;
		}
	}
	node->id = id;
	node->symb = symb;
	--pg->pat.freecnt;
	return node;
}
void
FreeAstNode(PG *pg, AstP node)
{
	if(node > ASTMINADDR)
	{
		memclr(node, ASTSIZE);
		node->down = pg->pat.free;
		pg->pat.free = node;
		++pg->pat.freecnt;
	}
}
void
PruneAstTree(PG *pg, AstP root, int preserve_root, const AstP top)
{
ASTVARS(256);
int at_top = 1;

	if(root > ASTMINADDR)
	{
		if(top > ASTMINADDR)
			at_top = 0;
		curnode = root;
		MARK(stack);
		while(curnode = BOTTOMUP(curnode)) {
			if(!at_top)
			{
				if(curnode == top) {
					at_top = 1;
					continue;
				}
			}
			if(preserve_root && curnode == root)
				break;
			if(at_top)
				FreeAstNode(pg, curnode);
		}
	}
}


/*--- Print Abstract Syntax Tree - courtesy of Paul Mann ------------------ */

static void
prt_node (char *indent, AstP node, PG *pg, void *file, int ptrs)
{
char *nodename;

	if(node->id == 0)
		nodename = "NullNode";
	else
		nodename = GetH_symbol(pg, node->id>>6);

	if(ptrs) {
		if(node->right < ASTMINADDR)		
			(*pg->writefunc) (file, "  %6x  %6d  %6x %s%s_%d",
   		   	node, node->lineno, node->down, indent, nodename, node->id & 0x3f);
		else
			(*pg->writefunc) (file, "  %6x  %6x  %6x %s%s_%d",
   		   	node, node->right, node->down, indent, nodename, node->id & 0x3f);
	}
	else
		(*pg->writefunc) (file, "%s%s_%d", indent, nodename, node->id & 0x3f);
	
	if (node->symb > 0)
	{
	char *symbname = PARSERSYMBOLS [node->symb].name;

		if(symbname)
		{
			if(*symbname != EOF_MARK)
				(*pg->writefunc) (file, " %s", symbname);
			if(node->Tindx)
			{
				symbname = PARSERSYMBOLS [node->Tindx].name;
				(*pg->writefunc) (file, "   %s", symbname);
			}
		}
		else (*pg->writefunc) (file, " [%d]", node->symb);
	}
	(*pg->writefunc) (file, "\n");
}
static void
traverse (char *indent, AstP node, PG *pg, void *file, int ptrs)
{
      while (node->down)
      {
         strcat (indent, "ÃÄ");
         prt_node (indent, node, pg, file, ptrs);
         indent [strlen(indent)-2] = 0;		  /* BACK UP */
         if (node->right > ASTMINADDR)
         {
            strcat (indent, "³ ");
            traverse (indent, node->right, pg, file, ptrs);
            indent [strlen(indent)-2] = 0; /* BACK UP */
         }
         node = node->down;
      }

   /* Last node on this level. */
      strcat (indent, "ÀÄ");
      prt_node (indent, node, pg, file, ptrs);
      indent [strlen(indent)-2] = 0;
      if (node->right > ASTMINADDR)
      {
         strcat (indent, "ú ");
         traverse (indent, node->right, pg, file, ptrs);
         indent [strlen(indent)-2] = 0;
      }
}

void 
PrintAst (PG *pg, const AstP root, void *file, int ptrs)
{
char indent [512];
indent[0] = '\0';

	  if(IsObj(file))
	  {
		pg->writefunc = (void*)imiPointer((object)file, gPrintf);
	  }
	  else
	  {
		pg->writefunc = (void*)cffprintf;
	  }
      if (root)
      {
		if(ptrs) (*pg->writefunc) (file, "    numb   right    down\n");
         traverse (indent, root, pg, file, ptrs);
      }
      else (*pg->writefunc) (file, " No AST available.\n");
      (*pg->writefunc) (file, "\n");
}


#if HOWBIG
void
HowBigAst(PG *pg, void *file)
{
	if(IsObj(file))
	{
		pg->writefunc = (void*)imiPointer((object)file, gPrintf);
	}
	else pg->writefunc = (void*)cffprintf;

	(*pg->writefunc) (file,"xx=%d SSp=%d SSl=%d NS=%d LS=%d RS=%d\nn_nodes=%d nsyms=%d hdups=%d\n",
		maxXX_parse, maxSS_parse, maxSS_lex, maxNS_parse,
		maxLS_parse, maxRS_parse, pg->pat.cnt - pg->pat.freecnt,
		pg->pst.cnt - pg->ptab->n_terms, pg->hashdups);
}
#endif

/*---  Scan Within an AST from the top down ------------------------------ */
#define PUSH(sp) (*(++(*sp)))
#define POP(sp) (*((*sp)--))
AstP
AstTopDown(AstP **sp, const AstP curnode, PG *pg)
{
AstP temp;

	if(curnode <= ASTMINADDR)
	{
		if((temp = STACKTOP(sp)))
		{
			if(NODEDOWN(STACKTOP(sp)))
			  STACKTOP(sp) = NODEDOWN(STACKTOP(sp));
			else (void)POP(sp);
		}
		else (void)POP(sp);
	}
	else if(NODERIGHT(curnode) > ASTMINADDR)
	{
		if(NODEDOWN(NODERIGHT(curnode)))
		  PUSH(sp) = NODEDOWN(NODERIGHT(curnode));
		temp = NODERIGHT(curnode);
	}
	else 
	{
		if((temp = POP(sp))) {/* Keep these braces, compiler opti bug */
		  if(NODEDOWN(temp)) {
			PUSH(sp) = NODEDOWN(temp);
		  }
		}
	}
	if(temp > ASTMINADDR)
	{
		if(temp->id == pg->blockinID)
			pg->blocklevel++;
		else if(temp->id == pg->blockoutID)
			pg->blocklevel--;
	}
	return temp;
}
/*--- Scan Within an AST from the bottom up ------------------------------- */
/* Subroutine for bottom up */
static __inline__ AstP
bu_follow(AstP **sp, AstP curnode)
{
	if(NODEDOWN(curnode))
	  PUSH(sp) = NODEDOWN(curnode);
	if(NODERIGHT(curnode) > ASTMINADDR)
	  return NODERIGHT(curnode);
	return POP(sp);
}

AstP
AstBottomUp(AstP **sp, const AstP start_node, PG *pg)
{
AstP temp;
	if(STACKTOP(sp) == (AstP)0)
	{/* First entry from a MARK(sp), find the bottom of this thread */
	AstP *stack;
	AstP *nexts = calloc(1, 4000);
	AstP curnode = start_node;
	AstP stop_node = NODEDOWN(start_node);

		PUSH(sp) = (AstP)-1;	/* Mark the top */
		stack = nexts;
		*stack++ = (AstP)0;
		for(;;)
		{
			curnode = bu_follow(&stack, curnode);
			if(curnode == (AstP)0 || curnode == stop_node)
				break;
			PUSH(sp) = curnode;
		}
		free(nexts);
	}
	if(STACKTOP(sp) == (AstP)-1)
	{/* Hit the mark */
		(void)POP(sp);	/* Clear it -- next should (better!) be a 0 */
	}
	temp = POP(sp);
	if(temp > ASTMINADDR)
	{
		if(temp->id == pg->blockinID)
			pg->blocklevel--;
		else if(temp->id == pg->blockoutID)
			pg->blocklevel++;
	}
	return temp;
}
int
AstPackSize(PG *pg)
{
int	astsize = ASTSIZE*(pg->pat.cnt-pg->pat.freecnt);
int	symptrsize = pg->pst.cnt*sizeof(PARSER_SYMBOL);
int bufsize = sizeof(PG) + symptrsize + astsize + pg->totsymlen;

	bufsize += (bufsize&3) ? 4-(bufsize&3) : 0;
	return bufsize;
}
void *
PackAst(PG *pg, void *buf)
{
ASTVARS(256);
PG *pk;
int astsize;
int symptrsize;
AstP pnode;
PARSER_SYMBOL *pptr;
char *ptext; 
int i, xx, recovered;
AstP ddstack[256];
AstP *dstack = ddstack;

	astsize = ASTSIZE*(pg->pat.cnt-pg->pat.freecnt);
	symptrsize = pg->pst.cnt*sizeof(PARSER_SYMBOL);
	pk = buf;
	if(pk == NULL)
	{
	int bufsize = 
		sizeof(PG) + symptrsize + astsize + pg->totsymlen;

		bufsize += (bufsize&3) ? 4-(bufsize&3) : 0;
		pk = calloc(1,bufsize);
	}
	memcpy(pk, pg, sizeof(PG));
	pk->pat.freecnt = 0;
	pk->pat.free = 0;
	pnode = (AstP)((char*)pk + sizeof(PG));				/* ast nodes */
	pptr = (PARSER_SYMBOL*)((char *)pnode + astsize);	/* symbol pointers */
	ptext = (char *)pptr + symptrsize; 					/* symbol text */
	pk->pst.ptr = pptr;
	pk->root = pnode;

	/* MOVE THE SYMBOL POINTERS AND STRINGS */
	for(i = 0; i < pg->pst.cnt; ++i)
	{
		pptr[i].name = ptext;
		xx = _strcpy(ptext, pg->pst.ptr[i].name);
		ptext += xx + 1;
	}

	/* MOVE THE NODES */
	MARKAST;
	recovered = 0;
	curnode = pg->root;
	*pnode = *curnode;

	if(curnode->down)
		*(++dstack) = pnode;
	if(curnode->right > ASTMINADDR) {
		pnode->right = pnode+1;
		++pnode;
	}
	else {
		++pnode;
		recovered = 1;
	}
	while(DOWNAST)
	{
		*pnode = *curnode;
		if(recovered) {
		AstP prev = *dstack--;
			prev->down = pnode;
			recovered = 0;
		}
		if(curnode->down) {
			*(++dstack) = pnode;
		}
		if(curnode->right > ASTMINADDR) {
			pnode->right = pnode+1;
			++pnode;
		} else {
			++pnode;
			recovered = 1;
		}
	}
	return pk;
}

/* ----- PARSER ACTIONS AVAILABLE TO GRAMMAR WRITERS ----- */

/*
	Standard action which does nothing.
*/
static void
nullaction(PG *pg, long *args)
{
	return;
}
/*
	Standard action to classify a symbol -- called from the parser
	arg[0] is the argcnt;
	arg[1] is the type of branch to look for (terminal index)
	arg[2] is an optional branch(1) to the right (nodeid)
	arg[3] is an alternate optional branch(2) to the right (nodeid)
	arg[4] if branch(1) branch again if encountered (nodeid)
	arg[5] if branch(1) terminate here and check leaf (nodeid)
	arg[6] is the type of leaf to look for	(terminal index)
	arg[7] is the classification to apply
	arg[8] is the sub classification (a shift count)

	Apply the classification codes to the high 32 bits of the
	symbol item in the database.
*/
static void
classify(PG *pg, long *args)
{
AstP curnode = pg->ptab->node;
AstP inode;

	if(args[0] != 8)
	{
		pg->actionerr = "classify: incorrect number of args.";
		return;
	}
	if((curnode = curnode->right) < ASTMINADDR)
		return;
	if(curnode->Tindx != args[1])
		return;

	while(curnode && (curnode = curnode->down))
	{
	  if ((curnode->id & 0xffc0) == args[2])
	  {/* InitDeclarator */
	    inode = curnode->right;
		do {
		  if(inode->id == args[4])	/* ParenDeclarator */
		  	inode = inode->right;
		  if(inode->id == args[5])	/* DeclID */
		  {
		  unsigned short *item;
			if(lookup(pg, PARSERSYMBOLS[inode->symb].name, &item))
			{
			  if(item[2] == 0)
			  {	/* don't re-classify */
				item[2] = (unsigned short)args[7];/* class */
				item[3] |= (unsigned short)(1<<(args[8]-args[7]-1));/* sub-class */
			  }
			}
			else
			{
			  pg->actionerr = "classify: symbol not found.";
			}
		  }
		} while((inode = inode->down));
	  }
	  else if ((curnode->id & 0xffc0) == args[3])
	  {/* TypeAgain */
	 	inode = curnode->right;
		do {
		  if(inode->Tindx == args[6])	/* <identifier> */
		  {
		  unsigned short *item;
			if(lookup(pg, PARSERSYMBOLS[inode->symb].name, &item))
			{
			  if(item[2] == 0)
			  {	/* don't re-classify */
				item[2] = (unsigned short)args[7];/* class */
				item[3] |= (unsigned short)(1<<(args[8]-args[7]-1));/* sub-class */
			  }
			}
			else
			{
			  pg->actionerr = "classify: symbol not found.";
			}
		  }
		} while((inode = inode->down));
	  }
	  else continue;
	} 
}

/*
	Standard action to require a particular subclass of a classified symbol.
*/
static void
require(PG *pg, long *args)
{
AstP curnode = pg->ptab->node;
int ok = 0;

	if(args[0] != 2)
	{
		pg->actionerr = "require: incorrect number of args.";
		return;
	}

	if(curnode->Tindx == args[1])
	{
		ok = 1;
	}
	else
	{
		if((curnode = curnode->right) > ASTMINADDR)
			if(curnode->Tindx == args[1])
				ok = 1;
	}
	while(ok)
	{
	unsigned short *item;
		if(lookup(pg, PARSERSYMBOLS[curnode->symb].name, &item))
		{
			if(item[2] == (unsigned short)args[1])
			{
				if(!(item[3] & (unsigned short)(1<<(args[2]-args[1]-1))))
					pg->actionerr = "require: symbol not member of subclass.";
			}
			else
			{
				pg->actionerr = "require: symbol not classified correctly.";
			}
		}
		else
		{
			pg->actionerr = "require: symbol not found.";
		}
		break;
	}
	if(!ok)
	{
		pg->actionerr = "require: invalid ast configuration.";
	}
}
/*
	Standard lexical action to handle the '#line' directive.
	#line 123 "filename" [flagchar] [identchar]
*/
static void
doline(PG *pg, long *args)
{
char *lnbeg;
char *fnbeg;
char *fnend;
int newfile = 0;

	pg->ltab->lextoken = 0;	/* kill the token */
	lnbeg = strchr(PARSERSYMBOLS[pg->lastsymnum].name, 'e');
	DelLastParserSymbol(pg); /* The whole line was entered as a symbol */
	if(lnbeg)
	{
	  pg->line_numb = atol(++lnbeg);		/* Current line number */
	  if((fnbeg = strchr(lnbeg, '"')))		/* Check for filename */
	  {
		if((fnend = strrchr(fnbeg+1, '"')))
		{	  	
		char *flbeg = fnend+1;

		  while(*flbeg && !isdigit(*flbeg)) ++flbeg;
		  if(*flbeg)
		  {
			if(*flbeg == '1')
			{/* enter file */
				goto enter_file;
			}
			else if(*flbeg == '2')
			{/* leave file */
			  pg->line_file = ParserPop(pg);
			  return;
			}
		  }
		  /* '3' or 0 in the first flag field */
		  if(pg->line_file > 0)
			return ;
enter_file:
		  *fnbeg = '{';		/* Ensure uniqueness of the symbol */
		  *fnend++ = '}';
		  *fnend = 0;
		  pg->curfile = NewParserSymbol(pg, fnbeg);
		  if(pg->curfile > pg->maxfile)
		  {
			pg->maxfile = pg->curfile;
			pg->files[++pg->numfiles] = (short)pg->curfile;
			newfile = pg->numfiles;
		  }
		  else
		  {
		  int i;
			for(i = 1; i <= pg->numfiles; ++i) {
			  if(pg->curfile == pg->files[i]) {
				newfile = i;
				break;
			  }
			} 
		  }
		  if(pg->line_file)
			  ParserPush(pg, pg->line_file);
		  pg->line_file = newfile;
		} /* END: fnbeg == '"' */
	  }  /* END: fnend == '"' */
	} /* END: lnbeg */
}

/*
	Standard lexical action to convert symbols to terminals of the grammar.
	It is usually called when <identifier> or some such is detected.
	The symbol has already been entered in the symbol table (which was
	filled with the terminals of the grammar at init time). If the
	symbol number is less than the number of grammar terminals, then
	this symbol is a reserved word, the token for which is not <identifier>
	but the symbol number itself. Other <identifier> symbols may
	have been classified (such as {typedef-name} or {class-name}).
*/
static void
dyntoken(PG *pg, long *args )
{
int symnum = pg->L_stack[pg->LSP];

	if(symnum < pg->ptab->n_terms)
	{/* This symbol really a reserved word */
		pg->ltab->lextoken = symnum;
	}
	else if(pg->lastclass)
	{/* This symbol has been classified */
		pg->ltab->lextoken = pg->lastclass;
	}	
}
