Click here to Skip to main content
15,893,588 members
Articles / Desktop Programming / MFC

Another Enum Viewer

Rate me:
Please Sign up or sign in to vote.
4.50/5 (2 votes)
22 Oct 20015 min read 83K   1.3K   19  
An article on the usage and design of another Enum Viewer
/*
 * gen.c
 *
 * $Id: gen.c 1.2 1997/01/05 18:25:48 john Exp john $
 * $Revision: 1.2 $
 *
 * Generate C code (ANSI, K&R, C++)
 *
 * SOFTWARE RIGHTS
 *
 * We reserve no LEGAL rights to the Purdue Compiler Construction Tool
 * Set (PCCTS) -- PCCTS is in the public domain.  An individual or
 * company may do whatever they wish with source code distributed with
 * PCCTS or the code generated by PCCTS, including the incorporation of
 * PCCTS, or its output, into commerical software.
 *
 * We encourage users to develop software with PCCTS.  However, we do ask
 * that credit is given to us for developing PCCTS.  By "credit",
 * we mean that if you incorporate our source code into one of your
 * programs (commercial product, research project, or otherwise) that you
 * acknowledge this fact somewhere in the documentation, research report,
 * etc...  If you like PCCTS and have developed a nice tool with the
 * output, please mention that you developed it using PCCTS.  In
 * addition, we ask that this header remain intact in our source code.
 * As long as these guidelines are kept, we expect to continue enhancing
 * this system and expect to make other tools available as they are
 * completed.
 *
 * ANTLR 1.33
 * Terence Parr
 * Parr Research Corporation
 * with Purdue University and AHPCRC, University of Minnesota
 * 1989-1995
 */
#include <stdio.h>
#include <ctype.h>
#include "set.h"
#include "syn.h"
#include "hash.h"
#include "generic.h"
#include "dlgdef.h"

#define NumExprPerLine	4
static int on1line=0;
static set tokensRefdInBlock;

extern char *PRED_AND_LIST;
extern char *PRED_OR_LIST;


					/* T r a n s l a t i o n  T a b l e s */

/* C_Trans[node type] == pointer to function that knows how to translate that node. */
#ifdef __cplusplus
void (*C_Trans[NumNodeTypes+1])(...) = {
	NULL,
	NULL,					/* See next table.   
Junctions have many types */
	(void (*)(...)) genRuleRef,
	(void (*)(...)) genToken,
	(void (*)(...)) genAction
};
#else
void (*C_Trans[NumNodeTypes+1])() = {
	NULL,
	NULL,					/* See next table.   
Junctions have many types */
	genRuleRef,
	genToken,
	genAction
};
#endif

/* C_JTrans[Junction type] == pointer to function that knows how to translate that
 * kind of junction node.
 */
#ifdef __cplusplus
void (*C_JTrans[NumJuncTypes+1])(...) = {
	NULL,
	(void (*)(...)) genSubBlk,
	(void (*)(...)) genOptBlk,
	(void (*)(...)) genLoopBlk,
	(void (*)(...)) genEndBlk,
	(void (*)(...)) genRule,
	(void (*)(...)) genJunction,
	(void (*)(...)) genEndRule,
	(void (*)(...)) genPlusBlk,
	(void (*)(...)) genLoopBegin
};
#else
void (*C_JTrans[NumJuncTypes+1])() = {
	NULL,
	genSubBlk,
	genOptBlk,
	genLoopBlk,
	genEndBlk,
	genRule,
	genJunction,
	genEndRule,
	genPlusBlk,
	genLoopBegin
};
#endif

#define PastWhiteSpace(s)	while (*(s) == ' ' || *(s) == '\t') {s++;}

static int tabs = 0;

static void
tab( FILE *fpout )
{ 
   int i; 
   for (i=0; i<tabs; i++) fputc('\t', fpout); 
}

#define TAB() tab(output)

static char *tokenFollowSet(TokNode *);
static ActionNode *findImmedAction( Node * );
static void dumpRetValAssign(char *, char *);
static void dumpAfterActions(FILE *output);
static set ComputeErrorSet(Junction *, int);
static void makeErrorClause(Junction *, set, int);
static void DumpFuncHeader( Junction *, RuleEntry * );
static int has_guess_block_as_first_item(Junction *);
static int genExprSets(set *, int);
static void genExprTree( Tree *t, int k );

#define gen(s)			{TAB(); fprintf(output, s);}
#define gen1(s,a)		{TAB(); fprintf(output, s,a);}
#define gen2(s,a,b)		{TAB(); fprintf(output, s,a,b);}
#define gen3(s,a,b,c)	{TAB(); fprintf(output, s,a,b,c);}
#define gen4(s,a,b,c,d)	{TAB(); fprintf(output, s,a,b,c,d);}
#define gen5(s,a,b,c,d,e)	{TAB(); fprintf(output, s,a,b,c,d,e);}
#define gen6(s,a,b,c,d,e,f)	{TAB(); fprintf(output, s,a,b,c,d,e,f);}
#define gen7(s,a,b,c,d,e,f,g)	{TAB(); fprintf(output, s,a,b,c,d,e,f,g);}

#define _gen(s)			{fprintf(output, s);}
#define _gen1(s,a)		{fprintf(output, s,a);}
#define _gen2(s,a,b)	{fprintf(output, s,a,b);}
#define _gen3(s,a,b,c)	{fprintf(output, s,a,b,c);}
#define _gen4(s,a,b,c,d){fprintf(output, s,a,b,c,d);}
#define _gen5(s,a,b,c,d,e){fprintf(output, s,a,b,c,d,e);}
#define _gen6(s,a,b,c,d,e,f){fprintf(output, s,a,b,c,d,e,f);}
#define _gen7(s,a,b,c,d,e,f,g){fprintf(output, s,a,b,c,d,e,f,g);}

void
freeBlkFsets( Junction *q )
{
	int i;
	Junction *alt;
	require(q!=NULL, "freeBlkFsets: invalid node");

	for (alt=q; alt != NULL; alt= (Junction *) alt->p2 )
	{
		for (i=1; i<=CLL_k; i++) set_free(alt->fset[i]);
	}
}

/*
 * Generate a local variable allocation for each token referenced
 * in this block.
 */
static void
genTokenPointers( Junction *q )
{
	/* Rule refs are counted and can be referenced, but their
	 * value is not set to anything useful ever.
	 *
     * The ptrs are to be named _tij where i is the current level
	 * and j is the element number within an alternative.
	 */
	int first=1, t=0;
	set a;
	tokensRefdInBlock = q->tokrefs;

	if ( set_deg(q->tokrefs) == 0 ) return;
	a = set_dup(q->tokrefs);
	gen("ANTLRTokenPtr ");
	for (; !set_nil(a); set_rm(t, a))
	{
		t = set_int(a);
		if ( first ) first = 0;
		else _gen(",");
		_gen2("_t%d%d = NULL", BlkLevel, t);
	}
	_gen(";\n");
	set_free(a);
}

static void
DumpExceptionHeader(Junction *q)
{
   if (q->exception != NULL)
   {
      // Generate try for signal handling
      gen("// try block for exception handling\n");
      gen("try {\n");
      tabs++;
   }
}

static void
DumpExceptionTrailer(Junction *q)
{
   ExceptionHandler *eh = q->exception;
   if (eh != NULL)
   {
      // Generate catch for signal handling
      // If guessing, we just propagate the exception, because it isn't really
      // an error.  It will be caught by the syntactic predicate catch phrase.
      tabs--;
      gen("} catch(AParserEX& ex) {\n");
      tabs++;
      gen("// catch phrase for exception handling\n");
      gen("if (guessing) {\n");
      tabs++;
      gen("// Propagate the exception\n");
      gen("throw;\n");
      tabs--;
      gen("} else {\n");
	   dumpAction(eh->action, output, tabs+1, -1, 1, 1);
      gen("}\n");
      tabs--;
      gen("}\n");
   }
}

/* For each element label that is found in a rule, generate a unique
 * Attribute variable.
 */
void
genElementLabels(ListNode *list)
{
	int first=1;
	ListNode *p;

	gen("ANTLRTokenPtr");
	for (p = list->next; p!=NULL; p=p->next)
	{
		char *ep = (char *)p->elem;
		if ( first ) first = 0;
		else _gen(",");
		_gen1(" %s=NULL",ep);
	}
	_gen(";\n");
}


static void
BLOCK_Head( void )
{
	gen("{\n");
	tabs++;
}

static void
BLOCK_Tail( void )
{
	tabs--;
	gen("}\n");
}

static void
gen_guess_variables()
{
	gen("ANTLRParserState parserState;\n");
	gen("int _marker = 0;\n");
}

static void
BLOCK_Preamble( Junction *q )
{
	ActionNode *a;
	Junction *begin;

	BLOCK_Head();
	genTokenPointers(q);
	if ( q->jtype == aPlusBlk ) gen("int zzcnt=1;\n");
	if ( q->parm != NULL && !q->predparm ) 
      gen1("zzaPush(%s);\n", q->parm);
	if ( q->jtype == aLoopBegin ) 
      begin = (Junction *) ((Junction *)q->p1);
	else 
      begin = q;
	if ( has_guess_block_as_first_item(begin) )
	{
		gen_guess_variables();
	}
	if ( q->jtype == aLoopBegin )
		a = findImmedAction( ((Junction *)q->p1)->p1 );	/* look at aLoopBlk */
	else
		a = findImmedAction( q->p1 );
	if ( a!=NULL && !a->is_predicate ) {
		dumpAction(a->action, output, tabs, a->file, a->line, 1);
		a->done = 1;	/* remove action. We have already handled it */
	}
}

void
genCombinedPredTreeContext( Predicate *p )
{
	static set *ctx=NULL;		/* genExprSets() is destructive, make copy*/
	require(p!=NULL, "can't make context tree for NULL pred tree");

#ifdef DBG_PRED
	fprintf(stdout, "enter genCombinedPredTreeContext(%s,0x%x) with sets:\n", p->expr, p);
	s_fprT(stdout, p->scontext[1]);
	fprintf(stdout, "\n");
#endif
	if ( p->down == NULL )
	{
/*		if ( p->k>1 && p->tcontext!=NULL ) */
		if ( p->tcontext!=NULL )
		{
			_gen("(");
			genExprTree(p->tcontext, 1);
			_gen(")");
		}
/*		else if ( p->k==1 && set_deg(p->scontext[1])>0 )*/
		else if ( set_deg(p->scontext[1])>0 )
		{
			if ( ctx==NULL ) ctx = (set *)calloc(CLL_k+1, sizeof(set));
			require(ctx!=NULL, "ctx cannot allocate");
			ctx[0]=empty;
			ctx[1]=set_dup(p->scontext[1]);
			_gen("(");
			genExprSets(&(ctx[0]), p->k);
			_gen(")");
			set_free(ctx[1]);
		}
		else if ( p->expr==PRED_AND_LIST || p->expr==PRED_OR_LIST ) {
			fatal_internal("pred tree is orphan OR or AND list");
		}
		else {
			fatal_internal("pred tree context is empty");
		}
		return;
	}

	if ( p->expr == PRED_AND_LIST )
	{
		require(p->down!=NULL && p->down->right!=NULL, "pred tree is wacked");
		genCombinedPredTreeContext(p->down);
		_gen("||");
		genCombinedPredTreeContext(p->down->right);
		return;
	}

	if ( p->expr == PRED_OR_LIST )
	{
		Predicate *list = p->down;
		for (; list!=NULL; list=list->right)
		{
			genCombinedPredTreeContext(list);
			if ( list->right!=NULL ) _gen("||");
		}
		return;
	}

	fatal("pred tree is really wacked");
}

void
genPredTreeGate( Predicate *p, int in_and_expr )
{
	if ( in_and_expr )
	{
		_gen("!(");
		genCombinedPredTreeContext(p);
		_gen(")||");
		if ( p->down!=NULL ) _gen("\n");
	}
	else
	{
		_gen("(");
		genCombinedPredTreeContext(p);
		_gen(")&&");
		if ( p->down!=NULL ) _gen("\n");
	}
}

void
genPred(Predicate *p, Node *j)
{
	_gen("(");
	if ( GenLineInfo && j->file != -1 ) _gen("\n");
	dumpAction(p->expr, output, 0, -1 /*indicates no line info*/, j->line, 0);
	_gen(")");
}

void
genPredTree( Predicate *p, Node *j, int in_and_expr )
{
	if ( HoistPredicateContext )
	{
		_gen("(");
		genPredTreeGate(p, in_and_expr);
	}

	/* if leaf node, just gen predicate */
	if ( p->down==NULL )
	{
		genPred(p,j);
		if ( HoistPredicateContext ) _gen(")");
		return;
	}

	/* if AND list, do both preds (only two possible) */
	if ( p->expr == PRED_AND_LIST )
	{
		_gen("(");
		genPredTree(p->down, j, 1);
		_gen("&&");
		genPredTree(p->down->right, j, 1);
		_gen(")");
		if ( HoistPredicateContext ) _gen(")");
		return;
	}

	if ( p->expr == PRED_OR_LIST )
	{
		Predicate *list;
		_gen("(");
		list = p->down;
		for (; list!=NULL; list=list->right)
		{
			genPredTree(list, j, 0);
			if ( list->right!=NULL ) _gen("||");
		}
		_gen(")");
		if ( HoistPredicateContext ) _gen(")");
		return;
	}

	fatal_internal("predicate tree is wacked");
}

void
genPredTreeMain( Predicate *p, Node *j )
{
	genPredTree(p,j,1);
}

static void
genExprTree( Tree *t, int k )
{
	require(t!=NULL, "genExprTree: NULL tree");
	
	if ( t->token == ALT )
	{
		_gen("("); genExprTree(t->down, k); _gen(")");
		if ( t->right!=NULL )
		{
			_gen("||");
			on1line++;
			if ( on1line > NumExprPerLine ) { on1line=0; _gen("\n"); }
			_gen("("); genExprTree(t->right, k); _gen(")");
		}
		return;
	}
	if ( t->down!=NULL ) _gen("(");
	_gen1("LA(%d)==",k);
	if ( TokenString(t->token) == NULL ) _gen1("%d", t->token)
	else _gen1("%s", TokenString(t->token));
	if ( t->down!=NULL )
	{
		_gen("&&");
		on1line++;
		if ( on1line > NumExprPerLine ) { on1line=0; _gen("\n"); TAB(); }
		_gen("("); genExprTree(t->down, k+1); _gen(")");
	}
	if ( t->down!=NULL ) _gen(")");
	if ( t->right!=NULL )
	{
		_gen("||");
		on1line++;
		if ( on1line > NumExprPerLine ) { on1line=0; _gen("\n"); TAB(); }
		_gen("("); genExprTree(t->right, k); _gen(")");
	}
}

/*
 * Generate LL(k) type expressions of the form:
 *
 *		 (LA(1) == T1 || LA(1) == T2 || ... || LA(1) == Tn) &&
 *		 (LA(2) == T1 || LA(2) == T2 || ... || LA(2) == Tn) &&
 *			.....
 *		 (LA(k) == T1 || LA(k) == T2 || ... || LA(k) == Tn)
 *
 * If GenExprSets generate:
 *
 *		(setwdi[LA(1)]&(1<<j)) && (setwdi[LA(2)]&(1<<j)) ...
 *
 * where n is set_deg(expr) and Ti is some random token and k is the last nonempty
 * set in fset <=CLL_k.
 * k=1..CLL_k where CLL_k >= 1.
 *
 * This routine is visible only to this file and cannot answer a TRANS message.
 *
 */
static int
genExpr( Junction *j )
{
	int max_k;

	/* if full LL(k) is sufficient, then don't use approximate (-ck) lookahead
	 * from CLL_k..LL_k
	 */
	{
		int limit;
		if ( j->ftree!=NULL ) limit = LL_k;
		else limit = CLL_k;
		max_k = genExprSets(j->fset, limit);
	}

	/* Do tests for real tuples from other productions that conflict with
	 * artificial tuples generated by compression (using sets of tokens
	 * rather than k-trees).
	 */
	if ( j->ftree != NULL )
	{
		_gen(" && !("); genExprTree(j->ftree, 1); _gen(")");
	}

	if ( ParseWithPredicates && j->predicate!=NULL )
	{
		Predicate *p = j->predicate;
		_gen("&&");
		genPredTreeMain(p, (Node *)j);
	}

	return max_k;
}

static int
genExprSets( set *fset, int limit )
{
	int k = 1;
	int max_k = 0;
	unsigned *e, *g, firstTime=1;

	if ( GenExprSets )
	{
		while ( !set_nil(fset[k]) && k<=limit )
		{
			if ( set_deg(fset[k])==1 )	/* too simple for a set? */
			{
				int e;
				_gen1("(LA(%d)==",k);
				e = set_int(fset[k]);
				if ( TokenString(e) == NULL ) _gen1("%d)", e)
				else _gen1("%s)", TokenString(e));
			}
			else
			{
				NewSet();
				FillSet( fset[k] );
				_gen3("(setwd%d[LA(%d)]&0x%x)", wordnum, k, 1<<setnum);
			}
			if ( k>max_k ) max_k = k;
			if ( k == CLL_k ) break;
			k++;
			if ( !set_nil(fset[k]) && k<=limit ) _gen(" && ");
			on1line++;
			if ( on1line > NumExprPerLine ) { on1line=0; _gen("\n"); TAB(); }
		}
		return max_k;
	}

	while ( !set_nil(fset[k]) && k<=limit )
	{
		if ( (e=g=set_pdq(fset[k])) == NULL ) fatal_internal("genExpr: cannot allocate IF expr pdq set");
		for (; *e!=nil; e++)
		{
			if ( !firstTime ) _gen(" || ") else { _gen("("); firstTime = 0; }
			on1line++;
			if ( on1line > NumExprPerLine ) { on1line=0; _gen("\n"); TAB(); }
			_gen1("LA(%d)==",k);
			if ( TokenString(*e) == NULL ) _gen1("%d", *e)
			else _gen1("%s", TokenString(*e));
		}
		free( (char *)g );
		_gen(")");
		if ( k>max_k ) max_k = k;
		if ( k == CLL_k ) break;
		k++;
		if ( !set_nil(fset[k]) && k<=limit ) { firstTime=1; _gen(" && "); }
		on1line++;
		if ( on1line > NumExprPerLine ) { on1line=0; _gen("\n"); TAB(); }
	}
	return max_k;
}

/*
 * Generate code for any type of block.  If the last alternative in the block is
 * empty (not even an action) don't bother doing it.  This permits us to handle
 * optional and loop blocks as well.
 *
 * Only do this block, return after completing the block.
 * This routine is visible only to this file and cannot answer a TRANS message.
 */
static set
genBlk( Junction *q, int jtype, int *max_k, int *need_right_curly )
{
	set f;
	Junction *alt;
	int a_guess_in_block = 0;
   int more_than_one_alt;
	require(q!=NULL,				"genBlk: invalid node");
	require(q->ntype == nJunction,	"genBlk: not junction");
   more_than_one_alt = (q->p2 != NULL);
	*need_right_curly=0;

#if 0
	if ( q->p2 == NULL )	/* only one alternative?  Then don't need if */
	{	
		if ( first_item_is_guess_block((Junction *)q->p1)!=NULL )
		{
			warnFL("(...)? as only alternative of block is unnecessary -- no guess code generated", FileStr[q->file], q->line);
		}
		TRANS(q->p1);
		return empty;		/* no decision to be made-->no error set */
	}
#endif

   // Set success flag to false until an alternate succeeds
   gen("int _success = 0;\n");

	f = First(q, 1, jtype, max_k);
	for (alt=q; alt != NULL; alt= (Junction *) alt->p2 )
	{
		int make_guess_for_alt = (
         first_item_is_guess_block((Junction *)alt->p1) != NULL 
      );

		if ( alt->p2 == NULL )					/* chk for empty alt */
		{	
			Node *p = alt->p1;
			if ( p->ntype == nJunction )
			{
				/* we have empty alt */
				if ( ((Junction *)p)->p1 == (Node *)q->end )
				{
					break;						/* don't do this one, quit */
				}
			}
		}

      if (more_than_one_alt)
      {
         // generate if block for alternate
         on1line = 0;
		   gen("if (");
         if (alt != q)
         {
            _gen(" !_success &&");
         }
		   genExpr(alt);
		   _gen(" ) {\n");
		   tabs++;
      }

		if ( make_guess_for_alt )
		{
         // Generate try/catch for guess mode
         gen("try {\n");
         tabs++;
         gen("saveState(&parserState);\n");
         gen("guessing = 1;\n");
         gen("_marker = MarkInputTokens();\n");
		}

      // generate body of alternate
		TRANS(alt->p1);

      gen("_success = 1;\n");

      if (make_guess_for_alt)
      {
         // Generate code for guess success
         // We need to abort the token buffer mark, and restore guessing state.
         gen("UnmarkInputTokens(_marker);\n");
         gen("guessing = parserState.guessing;   // restore only the guessing state\n");

         // Generate catch for guess failure
         // On a guess fail, we need to rewind the token buffer and 
         // restore the entire parser state.
         tabs--;
         gen("} catch(AParserEX&) {\n");
         tabs++;
         gen("if (guessing) {\n");
         tabs++;
         gen("_success = 0;\n");
         gen("RewindInputTokens(_marker);\n");
         gen("restoreState(&parserState);\n");
         tabs--;
         gen("} else {\n");
         tabs++;
         gen("// CommitGuess() was called -- treat as an error\n");
         gen("UnmarkInputTokens(_marker);\n");
         gen("guessing = parserState.guessing;\n");
         gen("throw;\n");
         tabs--;
         gen("}\n");
         tabs--;
         gen("}\n");
      }
      
      if (more_than_one_alt)
      {
         // close body of alternate
		   --tabs;
		   gen("}\n");
      }
	}
	return f;
}

static int
has_guess_block_as_first_item( Junction *q )
{
	Junction *alt;

	for (alt=q; alt != NULL; alt= (Junction *) alt->p2 )
	{
		if ( first_item_is_guess_block((Junction *)alt->p1)!=NULL ) return 1;
	}
	return 0;
}

/* return NULL if 1st item of alt is (...)? block; else return ptr  
to aSubBlk node
 * of (...)?;  This function ignores actions and predicates.
 */
Junction *
first_item_is_guess_block( Junction *q )
{
	while ( q!=NULL && ((q->ntype==nJunction && q->jtype==Generic) || q->ntype==nAction) )
	{
		if ( q->ntype==nJunction ) q = (Junction *)q->p1;
		else q = (Junction *) ((ActionNode *)q)->next;
	}

	if ( q==NULL ) {
      return NULL;
   }
	if ( q->ntype!=nJunction ) {
      return NULL;
   }
	if ( q->jtype!=aSubBlk ) {
      return NULL;
   }
	if ( !q->guess ) {
      return NULL;
   }

	return q;
}

/* Generate an action.  Don't if action is NULL which means that it was already
 * handled as an init action.
 */
void
genAction( ActionNode *p )
{
	require(p!=NULL,			"genAction: invalid node and/or rule");
	require(p->ntype==nAction,	"genAction: not action");
	
	if ( !p->done )
	{
		if ( p->is_predicate )
		{
			if ( p->guardpred!=NULL )
			{
				gen("if (!");
				genPredTreeMain(p->guardpred, (Node *)p);
			}
			else
			{
				gen("if (!(");
				/* make sure that '#line n' is on front of line */
				if ( GenLineInfo && p->file != -1 ) _gen("\n");
				dumpAction(p->action, output, 0, p->file, p->line, 0);
				_gen(")");
			}
			if ( p->pred_fail != NULL )
			{
				_gen(")\n");
				tabs++;
				gen1("%s;\n", p->pred_fail);
				tabs--;
			}
			else _gen1(") {fail_predicate(\"%s\");}\n",p->action);
		}
		else
		{
			dumpAction(p->action, output, tabs, p->file, p->line, 1);
		}
	}
	TRANS(p->next)
}

/*
 *		if invoking rule has !noAST pass zzSTR to rule ref and zzlink it in
 *		else pass addr of temp root ptr (&_ast) (don't zzlink it in).
 *
 *		if ! modifies rule-ref, then never link it in and never pass zzSTR.
 *		Always pass address of temp root ptr.
 */
void
genRuleRef( RuleRefNode *p )
{
	Junction *q;
	RuleEntry *r, *r2;
	char *parm = "";
	require(p!=NULL,			"genRuleRef: invalid node and/or rule");
	require(p->ntype==nRuleRef, "genRuleRef: not rule reference");
	
	r = (RuleEntry *) hash_get(Rname, p->text);
	if ( r == NULL )
	{
		warnFL( eMsg1("rule %s not defined",
					  p->text), FileStr[p->file], p->line );
		return;
	}
	r2 = (RuleEntry *) hash_get(Rname, p->rname);
	if ( r2 == NULL ) {warnNoFL("Rule hash table is screwed up beyond belief"); return;}

	if ( GenLineInfo ) fprintf(output, LineInfoFormatStr, p->line, FileStr[p->file]);

	TAB();
	if ( p->assign!=NULL )
	{
		if ( !HasComma(p->assign) ) {_gen1("%s = ",p->assign);}
		else _gen1("{ struct _rv%d _trv; _trv = ", r->rulenum);
	}
	_gen2("%s(%s);", p->text, (p->parms!=NULL)?p->parms:"");
	if ( p->assign!=NULL ) _gen("\n");

	q = RulePtr[r->rulenum];	/* find definition of ref'd rule */
	if ( p->assign!=NULL ) {
		if ( HasComma(p->assign) )
		{
			_gen("\n");
			dumpRetValAssign(p->assign, q->ret);
			_gen("}");
		}
	}
	_gen("\n");

	TRANS(p->next)
}

/*
 * Generate code to match a token.
 *
 * Getting the next token is tricky.  We want to ensure that any action
 * following a token is executed before the next GetToken();
 */
void
genToken( TokNode *p )
{
	RuleEntry *r;
	ActionNode *a;
	char *set_name;
	require(p!=NULL,			"genToken: invalid node and/or rule");
	require(p->ntype==nToken,	"genToken: not token");
	
	r = (RuleEntry *) hash_get(Rname, p->rname);
	if ( r == NULL ) {warnNoFL("Rule hash table is screwed up beyond belief"); return;}

	if ( GenLineInfo ) fprintf(output, LineInfoFormatStr, p->line, FileStr[p->file]);

	if ( !set_nil(p->tset) )	/* implies '.', ~Tok, or tokenclass */
	{
		unsigned e;
		set b;
		b = set_dup(p->tset);
		if ( p->tclass!=NULL )			/* token class? */
		{
			static char buf[MaxRuleName+1];
			if ( p->tclass->dumped )
				e = p->tclass->setnum;
			else {
				e = DefErrSet(&b, 0, TokenString(p->token));
				p->tclass->dumped = 1;	/* indicate set has been created */
				p->tclass->setnum = e;
			}
			sprintf(buf, "%s_set", TokenString(p->token));
			set_name = buf;
		}
		else {					/* wild card to ~ operator */
			static char buf[sizeof("zzerr")+10];
			int n = DefErrSet( &b, 0, NULL );
			sprintf(buf, "err%d", n);
			set_name = buf;
		}

      gen1("_setmatch(%s);", set_name);
		set_free(b);
	}
	else if ( TokenString(p->token)!=NULL )
	{
		gen1("_match(%s);", TokenString(p->token));
	}
	else {
      gen1("_match(%d);", p->token);
	}

	a = findImmedAction( p->next );
	/* generate the token labels */
	if ( p->elnum>0 )
	{
		/* If building trees in C++, always gen the LT() assigns */
		if ( set_el(p->elnum, tokensRefdInBlock))
		{
			_gen2(" _t%d%d = (ANTLRTokenPtr)LT(1);\n", BlkLevel-1, p->elnum);
		}
		_gen("\n");
		TAB();
	}

	/* Handle element labels now */
	if ( p->el_label!=NULL )
	{
		_gen("\n");
		/* Do Attrib / Token ptr */
		if ( set_el(p->elnum, tokensRefdInBlock) )
		{
         gen3("%s = _t%d%d;\n", p->el_label, BlkLevel-1, p->elnum);
      } else {
			gen1("%s = (ANTLRTokenPtr)LT(1);\n", p->el_label);
		}
	}

	/* Handle any actions immediately following action */
	if ( a != NULL )
	{
		/* delay next token fetch until after action */
		_gen("\n");
		if ( a->is_predicate )
		{
			gen("if (!(");
			dumpAction(a->action, output, 0, a->file, a->line, 0);
			if ( a->pred_fail != NULL )
			{
				_gen(")) {\n");
				tabs++;
				gen1("%s;\n", a->pred_fail);
				tabs--;
				gen("}\n");
			}
			else _gen1(")) {fail_predicate(\"%s\");}\n",a->action);
		}
		else
		{
			dumpAction(a->action, output, tabs, a->file, a->line, 1);
		}
		a->done = 1;
		gen("consume();")
      _gen("\n");
		TRANS( a->next );
	}
	else
	{
		gen("consume();")
		_gen("\n");
		TRANS(p->next);
	}
}

void
genOptBlk( Junction *q )
{
	int max_k;
	set f;
	int need_right_curly;
	set savetkref;
	savetkref = tokensRefdInBlock;
	require(q!=NULL,				"genOptBlk: invalid node and/or rule");
	require(q->ntype == nJunction,	"genOptBlk: not junction");
	require(q->jtype == aOptBlk,	"genOptBlk: not optional block");

	if ( GenLineInfo ) fprintf(output, LineInfoFormatStr, q->line, FileStr[q->file]);
	BLOCK_Preamble(q);
	BlkLevel++;
	f = genBlk(q, aOptBlk, &max_k, &need_right_curly);
	set_free(f);
	freeBlkFsets(q);
	BlkLevel--;

/*  JEL doesn't think we need this at all with new guess structure
    if ( first_item_is_guess_block((Junction *)q->p1)!=NULL )
	{
		gen("if (!_success) zzGUESS_DONE;\n");
	}
*/
	{ int i; for (i=1; i<=need_right_curly; i++) {tabs--; gen("}\n");} }
	BLOCK_Tail();
	tokensRefdInBlock = savetkref;
	if (q->end->p1 != NULL) TRANS(q->end->p1);
}

/*
 * Generate code for a loop blk of form:
 *
 *				 |---|
 *				 v   |
 *			   --o-G-o-->o--
 */
void
genLoopBlk( Junction *begin, Junction *q, Junction *start, int max_k )
{
	set f;
	int need_right_curly;
	set savetkref;
	savetkref = tokensRefdInBlock;
	require(q->ntype == nJunction,	"genLoopBlk: not junction");
	require(q->jtype == aLoopBlk,	"genLoopBlk: not loop block");

	if ( q->visited ) return;
	q->visited = TRUE;

	gen("while ( 1 ) {\n");
	tabs++;
	if ( begin!=NULL )
	{
		/* The bypass arc of the (...)* predicts what to do when you fail, but
		 * ONLY after having tested the loop start expression.  To avoid this,
		 * we simply break out of the (...)* loop when we find something that
		 * is not in the prediction of the loop (all alts thereof).
		 */
		gen("if ( !(");

      /*	TJP says: It used to use the prediction expression for the bypass arc
 	      of the (...)*.  HOWEVER, if a non LL^1(k) decision was found, this
	      thing would miss the ftree stored in the aLoopBegin node and generate
	      an LL^1(k) decision anyway.

		      genExpr((Junction *)begin->p2);
      */

		genExpr((Junction *)begin);
		_gen("))\n");
      gen("\tbreak;\n");
	}

   DumpExceptionHeader(begin);

	f = genBlk(q, aLoopBlk, &max_k, &need_right_curly);
	set_free(f);
	freeBlkFsets(q);

	{ 
      // Close outstanding curly braces
      int i; 
      for (i=1; i<=need_right_curly; i++) 
      {
         tabs--; 
         gen("}\n");
      } 
   }

	/* generate code for terminating loop (this is optional branch) */
	gen("if ( !_success ) break; /* implied exit branch */\n");

   DumpExceptionTrailer(begin);

	--tabs;
	gen("}\n");
	q->visited = FALSE;
	tokensRefdInBlock = savetkref;
}

/*
 * Generate code for a loop blk of form:
 *
 * 				         |---|
 *					     v   |
 *			   --o-->o-->o-G-o-->o--
 *                   |           ^
 *                   v           |
 *					 o-----------o
 *
 * q->end points to the last node (far right) in the blk.  Note  
that q->end->jtype
 * must be 'EndBlk'.
 *
 * Generate code roughly of the following form:
 *
 *	do {
 *		... code for alternatives ...
 *  } while ( First Set of aLoopBlk );
 *
 *	OR if > 1 alternative
 *
 *	do {
 *		... code for alternatives ...
 *		else break;
 *  } while ( 1 );
 */
void
genLoopBegin( Junction *q )
{
	set f;
	int i;
	int max_k;
	set savetkref;
	savetkref = tokensRefdInBlock;
	require(q!=NULL,				"genLoopBegin: invalid node and/or rule");
	require(q->ntype == nJunction,	"genLoopBegin: not junction");
	require(q->jtype == aLoopBegin,	"genLoopBegin: not loop block");
	require(q->p2!=NULL,			"genLoopBegin: invalid Loop Graph");

	if ( GenLineInfo ) fprintf(output, LineInfoFormatStr, q->line, FileStr[q->file]);

	BLOCK_Preamble(q);
	BlkLevel++;
	f = First(q, 1, aLoopBegin, &max_k);
	/* If not simple LL(1), must specify to start at LoopBegin, not LoopBlk */
	if ( LL_k>1 && !set_nil(q->fset[2]) )
   {
		genLoopBlk( q, (Junction *)q->p1, q, max_k );
   } else {
      genLoopBlk( q, (Junction *)q->p1, NULL, max_k );
   }

	for (i=1; i<=CLL_k; i++) {
      set_free(q->fset[i]);
   }
	for (i=1; i<=CLL_k; i++) {
      set_free(((Junction *)q->p2)->fset[i]);
   }
	--BlkLevel;
	BLOCK_Tail();
	set_free(f);
	tokensRefdInBlock = savetkref;
	if (q->end->p1 != NULL) TRANS(q->end->p1);
}

/*
 * Generate code for a loop blk of form:
 *
 * 					 |---|
 *					 v   |
 *			       --o-G-o-->o--
 *
 * q->end points to the last node (far right) in the blk.
 * Note that q->end->jtype must be 'EndBlk'.
 *
 * Generate code roughly of the following form:
 *
 *	do {
 *		... code for alternatives ...
 *  } while ( First Set of aPlusBlk );
 *
 *	OR if > 1 alternative
 *
 *	do {
 *		... code for alternatives ...
 *		else if not 1st time through, break;
 *  } while ( 1 );
 */
void
genPlusBlk( Junction *q )
{
	int max_k;
	set f;
	int need_right_curly;
	set savetkref;
	savetkref = tokensRefdInBlock;
	require(q!=NULL,				"genPlusBlk: invalid node and/or rule");
	require(q->ntype == nJunction,	"genPlusBlk: not junction");
	require(q->jtype == aPlusBlk,	"genPlusBlk: not Plus block");
	require(q->p2 != NULL,			"genPlusBlk: not a valid Plus block");

	if ( q->visited ) return;
	q->visited = TRUE;
	if ( GenLineInfo ) fprintf(output, LineInfoFormatStr, q->line, FileStr[q->file]);
	BLOCK_Preamble(q);

	BlkLevel++;

	gen("do {\n");
	tabs++;
   DumpExceptionHeader(q);
	f = genBlk(q, aPlusBlk, &max_k, &need_right_curly);
	gen("if ( !_success && zzcnt>1 ) break; /* implied exit branch */\n");/* code for exiting loop */
	TAB();
	makeErrorClause(q,f,max_k);

   // Close outstanding braces
	{ 
      int i; 
      for (i=1; i<=need_right_curly; i++) {
         tabs--; 
         gen("}\n");
      } 
   }
	gen("zzcnt++;\n");

   DumpExceptionTrailer(q);

	freeBlkFsets(q);
	--tabs;
	if ( q->parm!=NULL && q->predparm ) {
      gen1("} while (%s);\n", q->parm);
   } else {
      gen("} while ( 1 );\n");
   }

	--BlkLevel;
	BLOCK_Tail();
	q->visited = FALSE;
	tokensRefdInBlock = savetkref;
	if (q->end->p1 != NULL) TRANS(q->end->p1);
}

/*
 * Generate code for a sub blk of alternatives of form:
 *
 *			       --o-G1--o--
 *					 |     ^
 *					 v    /|
 *			         o-G2-o|
 *					 |     ^
 *					 v     |
 *				   ..........
 *					 |     ^
 *					 v    /
 *			         o-Gn-o
 *
 * q points to the 1st junction of blk (upper-left).
 * q->end points to the last node (far right) in the blk.
 * Note that q->end->jtype must be 'EndBlk'.
 * The last node in every alt points to q->end.
 *
 * Generate code of the following form:
 *	if ( First(G1) ) {
 *		...code for G1...
 *	}
 *	else if ( First(G2) ) {
 *		...code for G2...
 *	}
 *	...
 *	else {
 *		...code for Gn...
 *	}
 */
void
genSubBlk( Junction *q )
{
	int max_k;
	set f;
	int need_right_curly;
	set savetkref;
   int i; 

	savetkref = tokensRefdInBlock;
	require(q->ntype == nJunction,	"genSubBlk: not junction");
	require(q->jtype == aSubBlk,	"genSubBlk: not subblock");

	if ( GenLineInfo ) fprintf(output, LineInfoFormatStr, q->line, FileStr[q->file]);
	BLOCK_Preamble(q);

   /* Generate try block if there is an exception handler */
   DumpExceptionHeader(q);

	BlkLevel++;
	f = genBlk(q, aSubBlk, &max_k, &need_right_curly);
	if ( q->p2 != NULL ) 
   {
      TAB(); 
      makeErrorClause(q,f,max_k);
   }
   for (i=1; i<=need_right_curly; i++) 
   {
      tabs--; 
      gen("}\n");
   } 
	freeBlkFsets(q);
	--BlkLevel;

   /* Generate catch phrase if there is an exception handler */
   DumpExceptionTrailer(q);

	BLOCK_Tail();

	tokensRefdInBlock = savetkref;
	if (q->end->p1 != NULL) {
      TRANS(q->end->p1);
   }
}

/*
 * Generate code for a rule.
 *
 *		rule--> o-->o-Alternatives-o-->o
 * Or,
 *		rule--> o-->o-Alternative-o-->o
 *
 * The 1st junction is a RuleBlk.  The second can be a SubBlk or just a junction
 * (one alternative--no block), the last is EndRule.
 * The second to last is EndBlk if more than one alternative exists in the rule.
 *
 * To get to the init-action for a rule, we must bypass the RuleBlk,
 * and possible SubBlk.
 * Mark any init-action as generated so genBlk() does not regenerate it.
 */
void
genRule( Junction *q )
{
	int max_k;
	set follow, rk, f;
	ActionNode *a;
	RuleEntry *r;
	static int file = -1;
	int need_right_curly;
	require(q->ntype == nJunction,	"genRule: not junction");
	require(q->jtype == RuleBlk,	"genRule: not rule");

	r = (RuleEntry *) hash_get(Rname, q->rname);
	if ( r == NULL ) warnNoFL("Rule hash table is screwed up beyond belief");
	if ( q->file != file )		/* open new output file if need to */
	{
		if ( output != NULL ) fclose( output );
		output = fopen(OutMetaName(outname(FileStr[q->file])), "w");
		require(output != NULL, "genRule: can't open output file");

		special_fopen_actions(OutMetaName(outname(FileStr[q->file])));

		if ( file == -1 ) genHdr1(q->file);
		else genHdr(q->file);
		file = q->file;
	}
	DumpFuncHeader(q,r);
	tabs++;
	if ( q->ret!=NULL )
	{
		/* Declare the return value */
		if ( HasComma(q->ret) )
		{
			gen1("struct _rv%d _retv;\n",r->rulenum);
			//gen1("PURIFY(_retv,sizeof(struct _rv%d))\n",r->rulenum);
		}
		else
		{
			TAB();
			DumpType(q->ret, output);
			gen(" _retv;\n");
			//gen("PURIFY(_retv,sizeof(");
			//DumpType(q->ret, output);
			//gen("))\n");
		}
	}

	if ( GenLineInfo )
	{
		fprintf(output, LineInfoFormatStr, q->line, FileStr[q->file]);
	}

	gen("zzRULE;\n");
	genTokenPointers(q);
	if ( q->el_labels!=NULL ) genElementLabels(q->el_labels);

	if ( has_guess_block_as_first_item((Junction *)q->p1) )
	{
		gen_guess_variables();
	}

	/* Generate Init  Action */
	if ( ((Junction *)q->p1)->jtype == aSubBlk )
		a = findImmedAction( ((Junction *)q->p1)->p1 );
	else
		a = findImmedAction( q->p1 );	/* only one alternative in rule */
	if ( a!=NULL && !a->is_predicate )
	{
		dumpAction(a->action, output, tabs, a->file, a->line, 1);
		a->done = 1;	/* ignore action. We have already handled it */
	}
	if ( TraceGen )
   {
		gen1("Tracer tracer(this, \"%s\");\n", q->rname);
   }

   /* Generate try block if there is an exception handler */
   DumpExceptionHeader(q);

   /* Generate the body of the rule */
	BlkLevel++;
   /* mark RULE as visited for FIRST/FOLLOW */
	q->visited = TRUE;
	f = genBlk((Junction *)q->p1, RuleBlk, &max_k, &need_right_curly);
	if ( q->p1 != NULL )
   {
		if ( ((Junction *)q->p1)->p2 != NULL )
	   {
         TAB(); 
         makeErrorClause((Junction *)q->p1,f,max_k);
      }
   }
	{ 
      int i; for (i=1; i<=need_right_curly; i++) {
         tabs--; gen("}\n");
      } 
   }
	freeBlkFsets((Junction *)q->p1);
	q->visited = FALSE;
	--BlkLevel;

   /* Generate catch phrase if there is an exception handler */
   DumpExceptionTrailer(q);

	if ( q->ret!=NULL ) 
   {
      gen("return _retv;\n") 
   } else {
      gen("return;\n");
   }

	tabs--;
	gen("}\n");

	if ( q->p2 != NULL ) {
      /* generate code for next rule too */
      TRANS(q->p2);
   } else {
      dumpAfterActions( output );
   }

}

static void
DumpFuncHeader( Junction *q, RuleEntry *r )
{
	/* A N S I */
	_gen("\n");
	if ( q->ret!=NULL )
	{
		if ( HasComma(q->ret) )
		{
			gen2("%s::_rv%d\n", CurrentClassName, r->rulenum);
		}
		else
		{
			DumpType(q->ret, output);
			gen("\n");
		}
	}
	else
	{
		_gen("void\n");
	}
	gen2("%s::%s(", CurrentClassName, q->rname);
	DumpANSIFunctionArgDef(output,q);
	_gen("\n");
	gen("{\n");
}

void
DumpANSIFunctionArgDef(FILE *f, Junction *q)
{
	if ( q->pdecl!=NULL ) 
   {
      fprintf(f,"%s", q->pdecl);
   } else {
      fprintf(f,"void");
   }
	fprintf(f,")");
}

void
genJunction( Junction *q )
{
	require(q->ntype == nJunction,	"genJunction: not junction");
	require(q->jtype == Generic,	"genJunction: not generic junction");

	if ( q->p1 != NULL ) TRANS(q->p1);
	if ( q->p2 != NULL ) TRANS(q->p2);
}

void
genEndBlk( Junction *q )
{
}

void
genEndRule( Junction *q )
{
}

void
genHdr( int file )
{
	_gen("/*\n");
	_gen(" * A n t l r  T r a n s l a t i o n  H e a d e r\n");
	_gen(" *\n");
	_gen(" * Terence Parr, Will Cohen, and Hank Dietz: 1989-1994\n");
	_gen(" * Purdue University Electrical Engineering\n");
	_gen(" * With AHPCRC, University of Minnesota\n");
	_gen1(" * ANTLR Version %s\n", Version);
	_gen(" */\n");
	_gen("#include <stdio.h>\n");
	_gen1("#define ANTLR_VERSION	%s\n", VersionDef);
	if ( strcmp(ParserName, DefaultParserName)!=0 )
		_gen2("#define %s %s\n", DefaultParserName, ParserName);
   	if ( strcmp(ParserName, DefaultParserName)!=0 )
		{_gen1("#include \"%s\"\n", RemapFileName);}
	if ( GenLineInfo ) _gen2(LineInfoFormatStr, 1, FileStr[file]);
	if ( UserTokenDefsFile != NULL )
		fprintf(output, "#include %s\n", UserTokenDefsFile);
	else
		fprintf(output, "#include \"%s\"\n", DefFileName);

	if ( HdrAction != NULL ) dumpAction( HdrAction, output, 0, -1, 0, 1);
	_gen1("#include \"%s\"\n", APARSER_H);
	_gen1("#include \"%s.h\"\n", CurrentClassName);
	if ( LexGen ) _gen1("#include \"%s\"\n", DLEXERBASE_H);
	_gen1("#include \"%s\"\n", ATOKPTR_H);
	_gen("#ifndef PURIFY\n#define PURIFY(r,s)\n#endif\n");
}

void
genHdr1( int file )
{
	ListNode *p;

	genHdr(file);
	if ( BeforeActions != NULL )
	{
		for (p = BeforeActions->next; p!=NULL; p=p->next)
		{
			UserAction *ua = (UserAction *)p->elem;
			dumpAction( ua->action, output, 0, ua->file, ua->line, 1);
		}
	}
}

void
genStdPCCTSIncludeFile( FILE *f )
{
	fprintf(f,"#ifndef STDPCCTS_H\n");
	fprintf(f,"#define STDPCCTS_H\n");
	fprintf(f,"/*\n");
	fprintf(f," * %s -- P C C T S  I n c l u d e\n", stdpccts);
	fprintf(f," *\n");
	fprintf(f," * Terence Parr, Will Cohen, and Hank Dietz: 1989-1994\n");
	fprintf(f," * Purdue University Electrical Engineering\n");
	fprintf(f," * With AHPCRC, University of Minnesota\n");
	fprintf(f," * ANTLR Version %s\n", Version);
	fprintf(f," */\n");
	fprintf(f,"#include <stdio.h>\n");
	fprintf(f,"#define ANTLR_VERSION	%s\n", VersionDef);

	if ( UserDefdTokens )
		fprintf(f, "#include %s\n", UserTokenDefsFile);
	else {
		fprintf(f, "#include \"%s\"\n", DefFileName);
	}

	fprintf(f, "#include \"%s\"\n", ATOKEN_H);

	if ( HdrAction != NULL ) dumpAction( HdrAction, f, 0, -1, 0, 1);

	fprintf(f, "#include \"%s\"\n", ATOKENBUFFER_H);

	if ( OutputLL_k > 1 ) fprintf(f,"static const unsigned LL_K=%d;\n", OutputLL_k);
	fprintf(f,"#include \"%s\"\n", APARSER_H);
	fprintf(f,"#include \"%s.h\"\n", CurrentClassName);
	if ( LexGen ) fprintf(f,"#include \"%s\"\n", DLEXERBASE_H);
	fprintf(f, "#endif\n");
}

/* dump action 's' to file 'output' starting at "local" tab 'tabs'
   Dump line information in front of action if GenLineInfo is set
   If file == -1 then GenLineInfo is ignored.
   The user may redefine the LineInfoFormatStr to his/her liking
   most compilers will like the default, however.

   June '93; changed so that empty lines are left alone so that
   line information is correct for the compiler/debuggers.
*/
void
dumpAction( char *s, FILE *output, int local_tabs, int file, int line,  
int final_newline )
{
   int inDQuote, inSQuote;
   int save_tabs = tabs;
   tabs = local_tabs;

   require(s!=NULL, 		"dumpAction: NULL action");
   require(output!=NULL,	eMsg1("dumpAction: output FILE is NULL for %s",s));

	if ( GenLineInfo && file != -1 )
	{
		fprintf(output, LineInfoFormatStr, line, FileStr[file]);
	}
   PastWhiteSpace( s );
	/* don't print a tab if first non-white char is a # (preprocessor command) */
	if ( *s!='#' ) {
       TAB();
   }

    inDQuote = inSQuote = FALSE;
    while ( *s != '\0' )
    {
        if ( *s == '\\' )
        {
            fputc( *s++, output ); /* Avoid '"' Case */
            if ( *s == '\0' ) goto done;
            if ( *s == '\'' ) fputc( *s++, output );
            if ( *s == '\"' ) fputc( *s++, output );
        }
        if ( *s == '\'' )
        {
            if ( !inDQuote ) inSQuote = !inSQuote;
        }
        if ( *s == '"' )
        {
            if ( !inSQuote ) inDQuote = !inDQuote;
        }
        if ( *s == '\n' )
        {
            fputc('\n', output);
			s++;
            PastWhiteSpace( s );
            if ( *s == '}' )
            {
                --tabs;
				   TAB();
                fputc( *s++, output );
                continue;
            }
            if ( *s == '\0' ) goto done;
			if ( *s != '#' )	/* #define, #endif etc.. start at col 1 */
            {
				TAB();
			}
        }
        if ( *s == '}' && !(inSQuote || inDQuote) )
        {
            --tabs;            /* Indent one fewer */
        }
        if ( *s == '{' && !(inSQuote || inDQuote) )
        {
            tabs++;            /* Indent one more */
        }
        fputc( *s, output );
        s++;
    }
    if ( final_newline ) fputc('\n', output);

done:
   tabs = save_tabs;
}

static void
dumpAfterActions( FILE *output )
{
	ListNode *p;
	require(output!=NULL, "dumpAfterActions: output file was NULL for some reason");
	if ( AfterActions != NULL )
	{
		for (p = AfterActions->next; p!=NULL; p=p->next)
		{
			UserAction *ua = (UserAction *)p->elem;
			dumpAction( ua->action, output, 0, ua->file, ua->line, 1);
		}
	}
	fclose( output );
}

/*
 * Find the next action in the stream of execution.  Do not pass
 * junctions with more than one path leaving them.
 * Only pass generic junctions.
 *
 *	Scan forward while (generic junction with p2==NULL)
 *	If we stop on an action, return ptr to the action
 *	else return NULL;
 */
static ActionNode *
findImmedAction( Node *q )
{
	Junction *j;
	require(q!=NULL, "findImmedAction: NULL node");
	require(q->ntype>=1 && q->ntype<=NumNodeTypes, "findImmedAction: invalid node");
	
	while ( q->ntype == nJunction )
	{
		j = (Junction *)q;
		if ( j->jtype != Generic || j->p2 != NULL ) return NULL;
		q = j->p1;
		if ( q == NULL ) return NULL;
	}
	if ( q->ntype == nAction ) return (ActionNode *)q;
	return NULL;
}

static void
dumpRetValAssign( char *retval, char *ret_def )
{
	char *q = ret_def;
	
	TAB();
	while ( *retval != '\0' )
	{
		while ( isspace((*retval)) ) retval++;
		while ( *retval!=',' && *retval!='\0' ) fputc(*retval++, output);
		fprintf(output, " = _trv.");
		
		DumpNextNameInDef(&q, output);
		fputc(';', output); fputc(' ', output);
		if ( *retval == ',' ) retval++;
	}
}

/* This function computes the set of tokens that can possibly be seen k
 * tokens in the future from point j
 */
static set
ComputeErrorSet( Junction *j, int k )
{
	Junction *alt1;
	set a, rk, f;
	require(j->ntype==nJunction, "ComputeErrorSet: non junction passed");

	f = rk = empty;
	for (alt1=j; alt1!=NULL; alt1 = (Junction *)alt1->p2)
	{
		REACH(alt1->p1, k, &rk, a);
		require(set_nil(rk), "ComputeErrorSet: rk != nil");
		set_free(rk);
		set_orin(&f, a);
		set_free(a);
	}
	return f;
}

static char *
tokenFollowSet(TokNode *p)
{
    static char buf[100];
    set rk, a;
    int n;
    rk = empty;

    REACH(p->next, 1, &rk, a);
    require(set_nil(rk), "rk != nil");
    set_free(rk);
    n = DefErrSet( &a, 0, NULL );
    set_free(a);
    sprintf(buf, "err%d", n);
    return buf;
}

static void
makeErrorClause( Junction *q, set f, int max_k )
{
	_gen("if (!_success) {\n");
	tabs++;

   // Generate code without exception handling
   // Use only the single token set, even if max_k > 1

   if (max_k != 1)
   {
	   set_free(f);
      f = ComputeErrorSet(q, 1);
   }
   gen1("fail_syntax(err%d);\n", DefErrSet(&f,1,NULL));
	set_free(f);

   tabs--;
   gen("}\n");
}



/* If predicates are allowed in parsing expressions:
 *
 * (	production 1
 * |	production 2
 * ...
 * |	production n
 * )
 *
 * where production 1 yields visible predicates: <<pred>>? <<pred2>>?
 *
 * generates (if -prc on):
 *
 * if ( (production 1 prediction) &&
 *		(((context_of_pred ? pred : 1) &&
 *		(context_of_pred2 ? pred2 : 1))||!(ctx_pred||ctx_pred2))) ) {
 * 		...
 * }
 * else if ( production 2 prediction ) {
 * 		...
 * }
 * ...
 *
 * A predicate tree of
 *
 *		p1
 *		|
 *		p2--p3
 *
 * results in
 *
 * if ( (production 1 prediction) &&
 *		(((context_of_p1 ? p1 : 1) &&
 *		((context_of_p2 ? p2 : 0)||
 *		(context_of_p3 ? p3 : 0))) || !(ctx_p1||ctx_p2||ctx_p3))
 *		) {
 * 		...
 * }
 *
 * If no context, then just test predicate expression.
 */
#ifdef DUM
void
genPredTree( Predicate *p, Junction *j )
{
	int context_was_present = 0;
	Predicate *start_of_OR_list = NULL;

	_gen("(");
	start_of_OR_list = p;
	if ( HoistPredicateContext ) _gen("(");

	for (; p!=NULL; p=p->right)
	{
		if ( HoistPredicateContext )
		{
			context_was_present = 0;
			if ( LL_k>1 && p->tcontext!=NULL )
			{
				context_was_present = 1;
				_gen("((");
				genExprTree(p->tcontext, 1);
				_gen(")");
			}
			else if ( LL_k==1 && set_deg(p->scontext[1])>0 )
			{
				context_was_present = 1;
				_gen("((");
				genExprSets(&(p->scontext[0]), CLL_k);
				_gen(")");
			}
			/* &&'s must use ?: still; only leaves with no parent can avoid ?: */
			if ( p->down!=NULL || p->up!=NULL ) {_gen(" ? ");}
			else {_gen(" && ");}
		}

		_gen("(");
		dumpAction(p->expr, output, 0, -1 /*indicates no line info*/, j->line, 0);
		_gen(")");

		if ( HoistPredicateContext && context_was_present )
		{
			if ( p->down!=NULL || p->up!=NULL ) {		/* &&'s must use ?: still */
				_gen(" : ");
				genPredTermEliminator(p);
			}
			_gen(")");
		}

		if ( p->down!=NULL )
		{
			_gen("&&");
			if ( HoistPredicateContext && context_was_present ) _gen("(");
			genPredTree(p->down, j);
			if ( HoistPredicateContext && context_was_present && )
			{
				_gen(") || !(");
				genCombinedPredTreeContext(start_of_OR_list);
				_gen(")");
			}
		}

		if ( p->right!=NULL ) _gen("||");
	}

	if ( HoistPredicateContext )
	{
		_gen(")) || !(");
		genCombinedPredTreeContext(start_of_OR_list);
		_gen(")");
	}
	_gen(")");
}
#endif

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions