/*
* 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