| /* |
| * build.c -- functions associated with building syntax diagrams. |
| * |
| * $Id: build.c,v 1.3 95/06/15 18:07:00 parrt Exp $ |
| * $Revision: 1.3 $ |
| * |
| * 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> |
| #ifdef __cplusplus |
| #ifndef __STDC__ |
| #define __STDC__ |
| #endif |
| #endif |
| #include "set.h" |
| #include "syn.h" |
| #include "hash.h" |
| #include "generic.h" |
| #include "dlgdef.h" |
| |
| #define SetBlk(g, t, approx) { \ |
| ((Junction *)g.left)->jtype = t; \ |
| ((Junction *)g.left)->approx = approx; \ |
| ((Junction *)g.left)->end = (Junction *) g.right; \ |
| ((Junction *)g.right)->jtype = EndBlk;} |
| |
| /* Add the parameter string 'parm' to the parms field of a block-type junction |
| * g.left points to the sentinel node on a block. i.e. g.left->p1 points to |
| * the actual junction with its jtype == some block-type. |
| */ |
| void |
| #ifdef __STDC__ |
| addParm( Node *p, char *parm ) |
| #else |
| addParm( p, parm ) |
| Node *p; |
| char *parm; |
| #endif |
| { |
| char *q = (char *) malloc( strlen(parm) + 1 ); |
| require(p!=NULL, "addParm: NULL object\n"); |
| require(q!=NULL, "addParm: unable to alloc parameter\n"); |
| |
| strcpy(q, parm); |
| if ( p->ntype == nRuleRef ) |
| { |
| ((RuleRefNode *)p)->parms = q; |
| } |
| else if ( p->ntype == nJunction ) |
| { |
| ((Junction *)p)->parm = q; /* only one parameter allowed on subrules */ |
| } |
| else fatal_internal("addParm: invalid node for adding parm"); |
| } |
| |
| /* |
| * Build an action node for the syntax diagram |
| * |
| * buildAction(ACTION) ::= --o-->ACTION-->o-- |
| * |
| * Where o is a junction node. |
| */ |
| Graph |
| #ifdef __STDC__ |
| buildAction( char *action, int file, int line, int is_predicate ) |
| #else |
| buildAction( action, file, line, is_predicate ) |
| char *action; |
| int file; |
| int line; |
| int is_predicate; |
| #endif |
| { |
| Junction *j1, *j2; |
| Graph g; |
| ActionNode *a; |
| require(action!=NULL, "buildAction: invalid action"); |
| |
| j1 = newJunction(); |
| j2 = newJunction(); |
| a = newActionNode(); |
| a->action = (char *) malloc( strlen(action)+1 ); |
| require(a->action!=NULL, "buildAction: cannot alloc space for action\n"); |
| strcpy(a->action, action); |
| j1->p1 = (Node *) a; |
| a->next = (Node *) j2; |
| a->is_predicate = is_predicate; |
| g.left = (Node *) j1; g.right = (Node *) j2; |
| a->file = file; |
| a->line = line; |
| a->rname = "not filled in"; |
| return g; |
| } |
| |
| /* |
| * Build a token node for the syntax diagram |
| * |
| * buildToken(TOKEN) ::= --o-->TOKEN-->o-- |
| * |
| * Where o is a junction node. |
| */ |
| Graph |
| #ifdef __STDC__ |
| buildToken( char *text ) |
| #else |
| buildToken( text ) |
| char *text; |
| #endif |
| { |
| Junction *j1, *j2; |
| Graph g; |
| TokNode *t; |
| require(text!=NULL, "buildToken: invalid token name"); |
| |
| j1 = newJunction(); |
| j2 = newJunction(); |
| t = newTokNode(); |
| t->altstart = CurAltStart; |
| if ( *text == '"' ) {t->label=FALSE; t->token = addTexpr( text );} |
| else {t->label=TRUE; t->token = addTname( text );} |
| j1->p1 = (Node *) t; |
| t->next = (Node *) j2; |
| g.left = (Node *) j1; g.right = (Node *) j2; |
| return g; |
| } |
| |
| /* |
| * Build a wild-card node for the syntax diagram |
| * |
| * buildToken(TOKEN) ::= --o-->'.'-->o-- |
| * |
| * Where o is a junction node. |
| */ |
| Graph |
| #ifdef __STDC__ |
| buildWildCard( char *text ) |
| #else |
| buildWildCard( text ) |
| char *text; |
| #endif |
| { |
| Junction *j1, *j2; |
| Graph g; |
| TokNode *t; |
| TCnode *w; |
| TermEntry *p; |
| require(text!=NULL, "buildWildCard: invalid token name"); |
| |
| j1 = newJunction(); |
| j2 = newJunction(); |
| t = newTokNode(); |
| |
| /* If the ref a wild card, make a token class for it */ |
| if ( Tnum(WildCardString) == 0 ) |
| { |
| w = newTCnode; |
| w->tok = addTname( WildCardString ); |
| set_orel(w->tok, &imag_tokens); |
| set_orel(w->tok, &tokclasses); |
| WildCardToken = w->tok; |
| require((p=(TermEntry *)hash_get(Tname, WildCardString)) != NULL, |
| "hash table mechanism is broken"); |
| p->classname = 1; /* entry is class name, not token */ |
| p->tclass = w; /* save ptr to this tclass def */ |
| list_add(&tclasses, (char *)w); |
| } |
| else { |
| p=(TermEntry *)hash_get(Tname, WildCardString); |
| require( p!= NULL, "hash table mechanism is broken"); |
| w = p->tclass; |
| } |
| |
| t->token = w->tok; |
| t->wild_card = 1; |
| t->tclass = w; |
| |
| t->altstart = CurAltStart; |
| j1->p1 = (Node *) t; |
| t->next = (Node *) j2; |
| g.left = (Node *) j1; g.right = (Node *) j2; |
| return g; |
| } |
| |
| void |
| #ifdef __STDC__ |
| setUpperRange(TokNode *t, char *text) |
| #else |
| setUpperRange(t, text) |
| TokNode *t; |
| char *text; |
| #endif |
| { |
| require(t!=NULL, "setUpperRange: NULL token node"); |
| require(text!=NULL, "setUpperRange: NULL token string"); |
| |
| if ( *text == '"' ) {t->upper_range = addTexpr( text );} |
| else {t->upper_range = addTname( text );} |
| } |
| |
| /* |
| * Build a rule reference node of the syntax diagram |
| * |
| * buildRuleRef(RULE) ::= --o-->RULE-->o-- |
| * |
| * Where o is a junction node. |
| * |
| * If rule 'text' has been defined already, don't alloc new space to store string. |
| * Set r->text to point to old copy in string table. |
| */ |
| Graph |
| #ifdef __STDC__ |
| buildRuleRef( char *text ) |
| #else |
| buildRuleRef( text ) |
| char *text; |
| #endif |
| { |
| Junction *j1, *j2; |
| Graph g; |
| RuleRefNode *r; |
| RuleEntry *p; |
| require(text!=NULL, "buildRuleRef: invalid rule name"); |
| |
| j1 = newJunction(); |
| j2 = newJunction(); |
| r = newRNode(); |
| r->altstart = CurAltStart; |
| r->assign = NULL; |
| if ( (p=(RuleEntry *)hash_get(Rname, text)) != NULL ) r->text = p->str; |
| else r->text = mystrdup( text ); |
| j1->p1 = (Node *) r; |
| r->next = (Node *) j2; |
| g.left = (Node *) j1; g.right = (Node *) j2; |
| return g; |
| } |
| |
| /* |
| * Or two subgraphs into one graph via: |
| * |
| * Or(G1, G2) ::= --o-G1-o-- |
| * | ^ |
| * v | |
| * o-G2-o |
| * |
| * Set the altnum of junction starting G2 to 1 + altnum of junction starting G1. |
| * If, however, the G1 altnum is 0, make it 1 and then |
| * make G2 altnum = G1 altnum + 1. |
| */ |
| Graph |
| #ifdef __STDC__ |
| Or( Graph g1, Graph g2 ) |
| #else |
| Or( g1, g2 ) |
| Graph g1; |
| Graph g2; |
| #endif |
| { |
| Graph g; |
| require(g1.left != NULL, "Or: invalid graph"); |
| require(g2.left != NULL && g2.right != NULL, "Or: invalid graph"); |
| |
| ((Junction *)g1.left)->p2 = g2.left; |
| ((Junction *)g2.right)->p1 = g1.right; |
| /* set altnums */ |
| if ( ((Junction *)g1.left)->altnum == 0 ) ((Junction *)g1.left)->altnum = 1; |
| ((Junction *)g2.left)->altnum = ((Junction *)g1.left)->altnum + 1; |
| g.left = g2.left; |
| g.right = g1.right; |
| return g; |
| } |
| |
| /* |
| * Catenate two subgraphs |
| * |
| * Cat(G1, G2) ::= --o-G1-o-->o-G2-o-- |
| * Cat(NULL,G2)::= --o-G2-o-- |
| * Cat(G1,NULL)::= --o-G1-o-- |
| */ |
| Graph |
| #ifdef __STDC__ |
| Cat( Graph g1, Graph g2 ) |
| #else |
| Cat( g1, g2 ) |
| Graph g1; |
| Graph g2; |
| #endif |
| { |
| Graph g; |
| |
| if ( g1.left == NULL && g1.right == NULL ) return g2; |
| if ( g2.left == NULL && g2.right == NULL ) return g1; |
| ((Junction *)g1.right)->p1 = g2.left; |
| g.left = g1.left; |
| g.right = g2.right; |
| return g; |
| } |
| |
| /* |
| * Make a subgraph an optional block |
| * |
| * makeOpt(G) ::= --o-->o-G-o-->o-- |
| * | ^ |
| * v | |
| * o-------o |
| * |
| * Note that this constructs {A|B|...|Z} as if (A|B|...|Z|) was found. |
| * |
| * The node on the far right is added so that every block owns its own |
| * EndBlk node. |
| */ |
| Graph |
| #ifdef __STDC__ |
| makeOpt( Graph g1, int approx ) |
| #else |
| makeOpt( g1, approx ) |
| Graph g1; |
| int approx; |
| #endif |
| { |
| Junction *j1,*j2,*p; |
| Graph g; |
| require(g1.left != NULL && g1.right != NULL, "makeOpt: invalid graph"); |
| |
| j1 = newJunction(); |
| j2 = newJunction(); |
| ((Junction *)g1.right)->p1 = (Node *) j2; /* add node to G at end */ |
| g = emptyAlt(); |
| if ( ((Junction *)g1.left)->altnum == 0 ) ((Junction *)g1.left)->altnum = 1; |
| ((Junction *)g.left)->altnum = ((Junction *)g1.left)->altnum + 1; |
| for(p=(Junction *)g1.left; p->p2!=NULL; p=(Junction *)p->p2) |
| {;} /* find last alt */ |
| p->p2 = g.left; /* add optional alternative */ |
| ((Junction *)g.right)->p1 = (Node *)j2; /* opt alt points to EndBlk */ |
| g1.right = (Node *)j2; |
| SetBlk(g1, aOptBlk, approx); |
| j1->p1 = g1.left; /* add generic node in front */ |
| g.left = (Node *) j1; |
| g.right = g1.right; |
| |
| return g; |
| } |
| |
| /* |
| * Make a graph into subblock |
| * |
| * makeBlk(G) ::= --o-->o-G-o-->o-- |
| * |
| * The node on the far right is added so that every block owns its own |
| * EndBlk node. |
| */ |
| Graph |
| #ifdef __STDC__ |
| makeBlk( Graph g1, int approx ) |
| #else |
| makeBlk( g1, approx ) |
| Graph g1; |
| int approx; |
| #endif |
| { |
| Junction *j,*j2; |
| Graph g; |
| require(g1.left != NULL && g1.right != NULL, "makeBlk: invalid graph"); |
| |
| j = newJunction(); |
| j2 = newJunction(); |
| ((Junction *)g1.right)->p1 = (Node *) j2; /* add node to G at end */ |
| g1.right = (Node *)j2; |
| SetBlk(g1, aSubBlk, approx); |
| j->p1 = g1.left; /* add node in front */ |
| g.left = (Node *) j; |
| g.right = g1.right; |
| |
| return g; |
| } |
| |
| /* |
| * Make a subgraph into a loop (closure) block -- (...)* |
| * |
| * makeLoop(G) ::= |---| |
| * v | |
| * --o-->o-->o-G-o-->o-- |
| * | ^ |
| * v | |
| * o-----------o |
| * |
| * After making loop, always place generic node out front. It becomes |
| * the start of enclosing block. The aLoopBlk is the target of the loop. |
| * |
| * Loop blks have TWO EndBlk nodes--the far right and the node that loops back |
| * to the aLoopBlk node. Node with which we can branch past loop == aLoopBegin and |
| * one which is loop target == aLoopBlk. |
| * The branch-past (initial) aLoopBegin node has end |
| * pointing to the last EndBlk node. The loop-target node has end==NULL. |
| * |
| * Loop blocks have a set of locks (from 1..CLL_k) on the aLoopBlk node. |
| */ |
| Graph |
| #ifdef __STDC__ |
| makeLoop( Graph g1, int approx ) |
| #else |
| makeLoop( g1, approx ) |
| Graph g1; |
| int approx; |
| #endif |
| { |
| Junction *back, *front, *begin; |
| Graph g; |
| require(g1.left != NULL && g1.right != NULL, "makeLoop: invalid graph"); |
| |
| back = newJunction(); |
| front = newJunction(); |
| begin = newJunction(); |
| g = emptyAlt(); |
| ((Junction *)g1.right)->p2 = g1.left; /* add loop branch to G */ |
| ((Junction *)g1.right)->p1 = (Node *) back; /* add node to G at end */ |
| ((Junction *)g1.right)->jtype = EndBlk; /* mark 1st EndBlk node */ |
| ((Junction *)g1.left)->jtype = aLoopBlk; /* mark 2nd aLoopBlk node */ |
| ((Junction *)g1.left)->end = (Junction *) g1.right; |
| ((Junction *)g1.left)->lock = makelocks(); |
| ((Junction *)g1.left)->pred_lock = makelocks(); |
| g1.right = (Node *) back; |
| begin->p1 = (Node *) g1.left; |
| g1.left = (Node *) begin; |
| begin->p2 = (Node *) g.left; /* make bypass arc */ |
| ((Junction *)g.right)->p1 = (Node *) back; |
| SetBlk(g1, aLoopBegin, approx); |
| front->p1 = g1.left; /* add node to front */ |
| g1.left = (Node *) front; |
| |
| return g1; |
| } |
| |
| /* |
| * Make a subgraph into a plus block -- (...)+ -- 1 or more times |
| * |
| * makePlus(G) ::= |---| |
| * v | |
| * --o-->o-G-o-->o-- |
| * |
| * After making loop, always place generic node out front. It becomes |
| * the start of enclosing block. The aPlusBlk is the target of the loop. |
| * |
| * Plus blks have TWO EndBlk nodes--the far right and the node that loops back |
| * to the aPlusBlk node. |
| * |
| * Plus blocks have a set of locks (from 1..CLL_k) on the aPlusBlk node. |
| */ |
| Graph |
| #ifdef __STDC__ |
| makePlus( Graph g1, int approx ) |
| #else |
| makePlus( g1, approx ) |
| Graph g1; |
| int approx; |
| #endif |
| { |
| int has_empty_alt_already = 0; |
| Graph g; |
| Junction *j2, *j3, *first_alt; |
| Junction *last_alt, *p; |
| require(g1.left != NULL && g1.right != NULL, "makePlus: invalid graph"); |
| |
| first_alt = (Junction *)g1.left; |
| j2 = newJunction(); |
| j3 = newJunction(); |
| if ( ((Junction *)g1.left)->altnum == 0 ) ((Junction *)g1.left)->altnum = 1; |
| ((Junction *)g1.right)->p2 = g1.left; /* add loop branch to G */ |
| ((Junction *)g1.right)->p1 = (Node *) j2; /* add node to G at end */ |
| ((Junction *)g1.right)->jtype = EndBlk; /* mark 1st EndBlk node */ |
| g1.right = (Node *) j2; |
| SetBlk(g1, aPlusBlk, approx); |
| ((Junction *)g1.left)->lock = makelocks(); |
| ((Junction *)g1.left)->pred_lock = makelocks(); |
| j3->p1 = g1.left; /* add node to front */ |
| g1.left = (Node *) j3; |
| |
| /* add an optional branch which is the "exit" branch of loop */ |
| /* FIRST, check to ensure that there does not already exist |
| * an optional path. |
| */ |
| /* find last alt */ |
| for(p=first_alt; p!=NULL; p=(Junction *)p->p2) |
| { |
| if ( p->p1->ntype == nJunction && |
| p->p1!=NULL && |
| ((Junction *)p->p1)->jtype==Generic && |
| ((Junction *)p->p1)->p1!=NULL && |
| ((Junction *)((Junction *)p->p1)->p1)->jtype==EndBlk ) |
| { |
| has_empty_alt_already = 1; |
| } |
| last_alt = p; |
| } |
| if ( !has_empty_alt_already ) |
| { |
| require(last_alt!=NULL, "last_alt==NULL; bad (..)+"); |
| g = emptyAlt(); |
| last_alt->p2 = g.left; |
| ((Junction *)g.right)->p1 = (Node *) j2; |
| |
| /* make sure lookahead computation ignores this alt for |
| * FIRST("(..)+"); but it's still used for computing the FIRST |
| * of each alternative. |
| */ |
| ((Junction *)g.left)->ignore = 1; |
| } |
| |
| return g1; |
| } |
| |
| /* |
| * Return an optional path: --o-->o-- |
| */ |
| Graph |
| #ifdef __STDC__ |
| emptyAlt( void ) |
| #else |
| emptyAlt( ) |
| #endif |
| { |
| Junction *j1, *j2; |
| Graph g; |
| |
| j1 = newJunction(); |
| j2 = newJunction(); |
| j1->p1 = (Node *) j2; |
| g.left = (Node *) j1; |
| g.right = (Node *) j2; |
| |
| return g; |
| } |
| |
| /* N o d e A l l o c a t i o n */ |
| |
| TokNode * |
| #ifdef __STDC__ |
| newTokNode( void ) |
| #else |
| newTokNode( ) |
| #endif |
| { |
| static TokNode *FreeList = NULL; |
| TokNode *p, *newblk; |
| |
| if ( FreeList == NULL ) |
| { |
| newblk = (TokNode *)calloc(TokenBlockAllocSize, sizeof(TokNode)); |
| if ( newblk == NULL ) |
| fatal_internal(eMsg1("out of memory while building rule '%s'",CurRule)); |
| for (p=newblk; p<&(newblk[TokenBlockAllocSize]); p++) |
| { |
| p->next = (Node *)FreeList; /* add all new token nodes to FreeList */ |
| FreeList = p; |
| } |
| } |
| p = FreeList; |
| FreeList = (TokNode *)FreeList->next;/* remove a TokNode node */ |
| p->next = NULL; /* NULL the ptr we used */ |
| |
| p->ntype = nToken; |
| p->rname = CurRule; |
| p->file = CurFile; |
| p->line = zzline; |
| p->altstart = NULL; |
| |
| return p; |
| } |
| |
| RuleRefNode * |
| #ifdef __STDC__ |
| newRNode( void ) |
| #else |
| newRNode( ) |
| #endif |
| { |
| static RuleRefNode *FreeList = NULL; |
| RuleRefNode *p, *newblk; |
| |
| if ( FreeList == NULL ) |
| { |
| newblk = (RuleRefNode *)calloc(RRefBlockAllocSize, sizeof(RuleRefNode)); |
| if ( newblk == NULL ) |
| fatal_internal(eMsg1("out of memory while building rule '%s'",CurRule)); |
| for (p=newblk; p<&(newblk[RRefBlockAllocSize]); p++) |
| { |
| p->next = (Node *)FreeList; /* add all new rref nodes to FreeList */ |
| FreeList = p; |
| } |
| } |
| p = FreeList; |
| FreeList = (RuleRefNode *)FreeList->next;/* remove a Junction node */ |
| p->next = NULL; /* NULL the ptr we used */ |
| |
| p->ntype = nRuleRef; |
| p->rname = CurRule; |
| p->file = CurFile; |
| p->line = zzline; |
| p->astnode = ASTinclude; |
| p->altstart = NULL; |
| |
| return p; |
| } |
| |
| Junction * |
| #ifdef __STDC__ |
| newJunction( void ) |
| #else |
| newJunction( ) |
| #endif |
| { |
| static Junction *FreeList = NULL; |
| Junction *p, *newblk; |
| |
| if ( FreeList == NULL ) |
| { |
| newblk = (Junction *)calloc(JunctionBlockAllocSize, sizeof(Junction)); |
| if ( newblk == NULL ) |
| fatal_internal(eMsg1("out of memory while building rule '%s'",CurRule)); |
| for (p=newblk; p<&(newblk[JunctionBlockAllocSize]); p++) |
| { |
| p->p1 = (Node *)FreeList; /* add all new Junction nodes to FreeList */ |
| FreeList = p; |
| } |
| } |
| p = FreeList; |
| FreeList = (Junction *)FreeList->p1;/* remove a Junction node */ |
| p->p1 = NULL; /* NULL the ptr we used */ |
| |
| p->ntype = nJunction; p->visited = 0; |
| p->jtype = Generic; |
| p->rname = CurRule; |
| p->file = CurFile; |
| p->line = zzline; |
| p->exception_label = NULL; |
| p->fset = (set *) calloc(CLL_k+1, sizeof(set)); |
| require(p->fset!=NULL, "cannot allocate fset in newJunction"); |
| |
| return p; |
| } |
| |
| ActionNode * |
| #ifdef __STDC__ |
| newActionNode( void ) |
| #else |
| newActionNode( ) |
| #endif |
| { |
| static ActionNode *FreeList = NULL; |
| ActionNode *p, *newblk; |
| |
| if ( FreeList == NULL ) |
| { |
| newblk = (ActionNode *)calloc(ActionBlockAllocSize, sizeof(ActionNode)); |
| if ( newblk == NULL ) |
| fatal_internal(eMsg1("out of memory while building rule '%s'",CurRule)); |
| for (p=newblk; p<&(newblk[ActionBlockAllocSize]); p++) |
| { |
| p->next = (Node *)FreeList; /* add all new Action nodes to FreeList */ |
| FreeList = p; |
| } |
| } |
| p = FreeList; |
| FreeList = (ActionNode *)FreeList->next;/* remove an Action node */ |
| p->ntype = nAction; |
| p->next = NULL; /* NULL the ptr we used */ |
| p->done = 0; |
| p->pred_fail = NULL; |
| p->guardpred = NULL; |
| |
| return p; |
| } |
| |
| /* |
| * allocate the array of locks (1..CLL_k) used to inhibit infinite recursion. |
| * Infinite recursion can occur in (..)* blocks, FIRST calcs and FOLLOW calcs. |
| * Therefore, we need locks on aLoopBlk, RuleBlk, EndRule nodes. |
| * |
| * if ( lock[k]==TRUE ) then we have been here before looking for k tokens |
| * of lookahead. |
| */ |
| char * |
| #ifdef __STDC__ |
| makelocks( void ) |
| #else |
| makelocks( ) |
| #endif |
| { |
| char *p = (char *) calloc(CLL_k+1, sizeof(char)); |
| require(p!=NULL, "cannot allocate lock array"); |
| |
| return p; |
| } |