mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-28 12:25:19 +01:00
b1264ef1e3
Signed-off-by: Sergey Isakov <isakov-sl@bk.ru>
1865 lines
48 KiB
C
Executable File
1865 lines
48 KiB
C
Executable File
/*
|
|
* misc.c
|
|
*
|
|
* Manage tokens, regular expressions.
|
|
* Print methods for debugging
|
|
* Compute follow lists onto tail ends of rules.
|
|
*
|
|
* The following functions are visible:
|
|
*
|
|
* int addTname(char *); Add token name
|
|
* int addTexpr(char *); Add token expression
|
|
* int Tnum(char *); Get number of expr/token
|
|
* void Tklink(char *, char *); Link a name with an expression
|
|
* int hasAction(expr); Does expr already have action assigned?
|
|
* void setHasAction(expr); Indicate that expr now has an action
|
|
* Entry *newEntry(char *,int); Create new table entry with certain size
|
|
* void list_add(ListNode **list, char *e)
|
|
* void list_free(ListNode **list, int freeData); *** MR10 ***
|
|
* void list_apply(ListNode *list, void (*f)())
|
|
* void lexclass(char *m); switch to new/old lexical class
|
|
* void lexmode(int i); switch to old lexical class i
|
|
*
|
|
* 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-2001
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include "pcctscfg.h"
|
|
#include "set.h"
|
|
#include "syn.h"
|
|
#include "hash.h"
|
|
#include "generic.h"
|
|
#include "dlgdef.h"
|
|
#include <ctype.h>
|
|
|
|
static int tsize=TSChunk; /* size of token str arrays */
|
|
|
|
static void
|
|
#ifdef __USE_PROTOS
|
|
RemapForcedTokensInSyntaxDiagram(Node *);
|
|
#else
|
|
RemapForcedTokensInSyntaxDiagram();
|
|
#endif
|
|
|
|
/* T o k e n M a n i p u l a t i o n */
|
|
|
|
/*
|
|
* add token 't' to the TokenStr/Expr array. Make more room if necessary.
|
|
* 't' is either an expression or a token name.
|
|
*
|
|
* There is only one TokenStr array, but multiple ExprStr's. Therefore,
|
|
* for each lex class (element of lclass) we must extend the ExprStr array.
|
|
* ExprStr's and TokenStr are always all the same size.
|
|
*
|
|
* Also, there is a Texpr hash table for each automaton.
|
|
*/
|
|
static void
|
|
#ifdef __USE_PROTOS
|
|
Ttrack( char *t )
|
|
#else
|
|
Ttrack( t )
|
|
char *t;
|
|
#endif
|
|
{
|
|
if ( TokenNum >= tsize ) /* terminal table overflow? */
|
|
{
|
|
char **p;
|
|
int i, more, j;
|
|
|
|
more = TSChunk * (1 + ((TokenNum-tsize) / TSChunk));
|
|
tsize += more;
|
|
TokenStr = (char **) realloc((char *)TokenStr, tsize*sizeof(char *));
|
|
require(TokenStr != NULL, "Ttrack: can't extend TokenStr");
|
|
for (i=0; i<NumLexClasses; i++)
|
|
{
|
|
lclass[i].exprs = (char **)
|
|
realloc((char *)lclass[i].exprs, tsize*sizeof(char *));
|
|
require(lclass[i].exprs != NULL, "Ttrack: can't extend ExprStr");
|
|
for (p= &lclass[i].exprs[tsize-more],j=1; j<=more; j++) *p++ = NULL;
|
|
}
|
|
for (p= &TokenStr[tsize-more],i=1; i<=more; i++) *p++ = NULL;
|
|
lexmode( CurrentLexClass ); /* reset ExprStr in case table moved */
|
|
}
|
|
/* note: we use the actual ExprStr/TokenStr array
|
|
* here as TokenInd doesn't exist yet
|
|
*/
|
|
if ( *t == '"' ) ExprStr[TokenNum] = t;
|
|
else TokenStr[TokenNum] = t;
|
|
}
|
|
|
|
static Expr *
|
|
#ifdef __USE_PROTOS
|
|
newExpr( char *e )
|
|
#else
|
|
newExpr( e )
|
|
char *e;
|
|
#endif
|
|
{
|
|
Expr *p = (Expr *) calloc(1, sizeof(Expr));
|
|
require(p!=NULL, "newExpr: cannot alloc Expr node");
|
|
|
|
p->expr = e;
|
|
p->lclass = CurrentLexClass;
|
|
return p;
|
|
}
|
|
|
|
/* switch to lexical class/mode m. This amounts to creating a new
|
|
* lex mode if one does not already exist and making ExprStr point
|
|
* to the correct char string array. We must also switch Texpr tables.
|
|
*
|
|
* BTW, we need multiple ExprStr arrays because more than one automaton
|
|
* may have the same label for a token, but with different expressions.
|
|
* We need to track an expr for each automaton. If we disallowed this
|
|
* feature, only one ExprStr would be required.
|
|
*/
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
lexclass( char *m )
|
|
#else
|
|
lexclass( m )
|
|
char *m;
|
|
#endif
|
|
{
|
|
int i;
|
|
TermEntry *p;
|
|
static char EOFSTR[] = "\"@\"";
|
|
|
|
if ( hash_get(Tname, m) != NULL )
|
|
{
|
|
warn(eMsg1("lexclass name conflicts with token/errclass label '%s'",m));
|
|
}
|
|
/* does m already exist? */
|
|
i = LexClassIndex(m);
|
|
if ( i != -1 ) {lexmode(i); return;}
|
|
/* must make new one */
|
|
NumLexClasses++;
|
|
CurrentLexClass = NumLexClasses-1;
|
|
require(NumLexClasses<=MaxLexClasses, "number of allowable lexclasses exceeded\nIncrease MaxLexClasses in generic.h and recompile all C files");
|
|
lclass[CurrentLexClass].classnum = m;
|
|
lclass[CurrentLexClass].exprs = (char **) calloc(tsize, sizeof(char *));
|
|
require(lclass[CurrentLexClass].exprs!=NULL,
|
|
"lexclass: cannot allocate ExprStr");
|
|
lclass[CurrentLexClass].htable = newHashTable();
|
|
ExprStr = lclass[CurrentLexClass].exprs;
|
|
Texpr = lclass[CurrentLexClass].htable;
|
|
/* define EOF for each automaton */
|
|
p = newTermEntry( EOFSTR );
|
|
p->token = EofToken; /* couldn't have remapped tokens yet, use EofToken */
|
|
hash_add(Texpr, EOFSTR, (Entry *)p);
|
|
list_add(&ExprOrder, (void *)newExpr(EOFSTR));
|
|
/* note: we use the actual ExprStr array
|
|
* here as TokenInd doesn't exist yet
|
|
*/
|
|
ExprStr[EofToken] = EOFSTR;
|
|
}
|
|
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
lexmode( int i )
|
|
#else
|
|
lexmode( i )
|
|
int i;
|
|
#endif
|
|
{
|
|
require(i<NumLexClasses, "lexmode: invalid mode");
|
|
ExprStr = lclass[i].exprs;
|
|
Texpr = lclass[i].htable;
|
|
CurrentLexClass = i;
|
|
}
|
|
|
|
/* return index into lclass array of lexical class. return -1 if nonexistent */
|
|
int
|
|
#ifdef __USE_PROTOS
|
|
LexClassIndex( char *cl )
|
|
#else
|
|
LexClassIndex( cl )
|
|
char *cl;
|
|
#endif
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<NumLexClasses; i++)
|
|
{
|
|
if ( strcmp(lclass[i].classnum, cl) == 0 ) return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
#ifdef __USE_PROTOS
|
|
hasAction( char *expr )
|
|
#else
|
|
hasAction( expr )
|
|
char *expr;
|
|
#endif
|
|
{
|
|
TermEntry *p;
|
|
require(expr!=NULL, "hasAction: invalid expr");
|
|
|
|
p = (TermEntry *) hash_get(Texpr, expr);
|
|
require(p!=NULL, eMsg1("hasAction: expr '%s' doesn't exist",expr));
|
|
return (p->action!=NULL);
|
|
}
|
|
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
setHasAction( char *expr, char *action )
|
|
#else
|
|
setHasAction( expr, action )
|
|
char *expr;
|
|
char *action;
|
|
#endif
|
|
{
|
|
TermEntry *p;
|
|
require(expr!=NULL, "setHasAction: invalid expr");
|
|
|
|
p = (TermEntry *) hash_get(Texpr, expr);
|
|
require(p!=NULL, eMsg1("setHasAction: expr '%s' doesn't exist",expr));
|
|
p->action = action;
|
|
}
|
|
|
|
ForcedToken *
|
|
#ifdef __USE_PROTOS
|
|
newForcedToken(char *token, int tnum)
|
|
#else
|
|
newForcedToken(token, tnum)
|
|
char *token;
|
|
int tnum;
|
|
#endif
|
|
{
|
|
ForcedToken *ft = (ForcedToken *) calloc(1, sizeof(ForcedToken));
|
|
require(ft!=NULL, "out of memory");
|
|
ft->token = token;
|
|
ft->tnum = tnum;
|
|
return ft;
|
|
}
|
|
|
|
/*
|
|
* Make a token indirection array that remaps token numbers and then walk
|
|
* the appropriate symbol tables and SynDiag to change token numbers
|
|
*/
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
RemapForcedTokens(void)
|
|
#else
|
|
RemapForcedTokens()
|
|
#endif
|
|
{
|
|
ListNode *p;
|
|
ForcedToken *q;
|
|
int max_token_number=0; /* MR9 23-Sep-97 Removed "unsigned" */
|
|
int i;
|
|
|
|
if ( ForcedTokens == NULL ) return;
|
|
|
|
/* find max token num */
|
|
for (p = ForcedTokens->next; p!=NULL; p=p->next)
|
|
{
|
|
q = (ForcedToken *) p->elem;
|
|
if ( q->tnum > max_token_number ) max_token_number = q->tnum;
|
|
}
|
|
fprintf(stderr, "max token number is %d\n", max_token_number);
|
|
|
|
/* make token indirection array */
|
|
TokenInd = (int *) calloc(max_token_number+1, sizeof(int));
|
|
LastTokenCounted = TokenNum;
|
|
TokenNum = max_token_number+1;
|
|
require(TokenInd!=NULL, "RemapForcedTokens: cannot allocate TokenInd");
|
|
|
|
/* fill token indirection array and change token id htable ; swap token indices */
|
|
for (i=1; i<TokenNum; i++) TokenInd[i] = i;
|
|
for (p = ForcedTokens->next; p!=NULL; p=p->next)
|
|
{
|
|
TermEntry *te;
|
|
int old_pos, t;
|
|
|
|
q = (ForcedToken *) p->elem;
|
|
fprintf(stderr, "%s forced to %d\n", q->token, q->tnum);
|
|
te = (TermEntry *) hash_get(Tname, q->token);
|
|
require(te!=NULL, "RemapForcedTokens: token not in hash table");
|
|
old_pos = te->token;
|
|
fprintf(stderr, "Before: TokenInd[old_pos==%d] is %d\n", old_pos, TokenInd[old_pos]);
|
|
fprintf(stderr, "Before: TokenInd[target==%d] is %d\n", q->tnum, TokenInd[q->tnum]);
|
|
q = (ForcedToken *) p->elem;
|
|
t = TokenInd[old_pos];
|
|
TokenInd[old_pos] = q->tnum;
|
|
TokenInd[q->tnum] = t;
|
|
te->token = q->tnum; /* update token type id symbol table */
|
|
fprintf(stderr, "After: TokenInd[old_pos==%d] is %d\n", old_pos, TokenInd[old_pos]);
|
|
fprintf(stderr, "After: TokenInd[target==%d] is %d\n", q->tnum, TokenInd[q->tnum]);
|
|
|
|
/* Change the token number in the sym tab entry for the exprs
|
|
* at the old position of the token id and the target position
|
|
*/
|
|
/* update expr at target (if any) of forced token id */
|
|
if ( q->tnum < TokenNum ) /* is it a valid position? */
|
|
{
|
|
for (i=0; i<NumLexClasses; i++)
|
|
{
|
|
if ( lclass[i].exprs[q->tnum]!=NULL )
|
|
{
|
|
/* update the symbol table for this expr */
|
|
TermEntry *e = (TermEntry *) hash_get(lclass[i].htable, lclass[i].exprs[q->tnum]);
|
|
require(e!=NULL, "RemapForcedTokens: expr not in hash table");
|
|
e->token = old_pos;
|
|
fprintf(stderr, "found expr '%s' at target %d in lclass[%d]; changed to %d\n",
|
|
lclass[i].exprs[q->tnum], q->tnum, i, old_pos);
|
|
}
|
|
}
|
|
}
|
|
/* update expr at old position (if any) of forced token id */
|
|
for (i=0; i<NumLexClasses; i++)
|
|
{
|
|
if ( lclass[i].exprs[old_pos]!=NULL )
|
|
{
|
|
/* update the symbol table for this expr */
|
|
TermEntry *e = (TermEntry *) hash_get(lclass[i].htable, lclass[i].exprs[old_pos]);
|
|
require(e!=NULL, "RemapForcedTokens: expr not in hash table");
|
|
e->token = q->tnum;
|
|
fprintf(stderr, "found expr '%s' for id %s in lclass[%d]; changed to %d\n",
|
|
lclass[i].exprs[old_pos], q->token, i, q->tnum);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Update SynDiag */
|
|
RemapForcedTokensInSyntaxDiagram((Node *)SynDiag);
|
|
}
|
|
|
|
static void
|
|
#ifdef __USE_PROTOS
|
|
RemapForcedTokensInSyntaxDiagram(Node *p)
|
|
#else
|
|
RemapForcedTokensInSyntaxDiagram(p)
|
|
Node *p;
|
|
#endif
|
|
{
|
|
Junction *j = (Junction *) p;
|
|
RuleRefNode *r = (RuleRefNode *) p;
|
|
TokNode *t = (TokNode *)p;
|
|
|
|
if ( p==NULL ) return;
|
|
require(p->ntype>=1 && p->ntype<=NumNodeTypes, "Remap...: invalid diagram node");
|
|
switch ( p->ntype )
|
|
{
|
|
case nJunction :
|
|
if ( j->visited ) return;
|
|
if ( j->jtype == EndRule ) return;
|
|
j->visited = TRUE;
|
|
RemapForcedTokensInSyntaxDiagram( j->p1 );
|
|
RemapForcedTokensInSyntaxDiagram( j->p2 );
|
|
j->visited = FALSE;
|
|
return;
|
|
case nRuleRef :
|
|
RemapForcedTokensInSyntaxDiagram( r->next );
|
|
return;
|
|
case nToken :
|
|
if ( t->remapped ) return; /* we've been here before */
|
|
t->remapped = 1;
|
|
fprintf(stderr, "remapping %d to %d\n", t->token, TokenInd[t->token]);
|
|
t->token = TokenInd[t->token];
|
|
RemapForcedTokensInSyntaxDiagram( t->next );
|
|
return;
|
|
case nAction :
|
|
RemapForcedTokensInSyntaxDiagram( ((ActionNode *)p)->next );
|
|
return;
|
|
default :
|
|
fatal_internal("invalid node type");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add a token name. Return the token number associated with it. If it already
|
|
* exists, then return the token number assigned to it.
|
|
*
|
|
* Track the order in which tokens are found so that the DLG output maintains
|
|
* that order. It also lets us map token numbers to strings.
|
|
*/
|
|
int
|
|
#ifdef __USE_PROTOS
|
|
addTname( char *token )
|
|
#else
|
|
addTname( token )
|
|
char *token;
|
|
#endif
|
|
{
|
|
TermEntry *p;
|
|
require(token!=NULL, "addTname: invalid token name");
|
|
|
|
if ( (p=(TermEntry *)hash_get(Tname, token)) != NULL ) return p->token;
|
|
p = newTermEntry( token );
|
|
Ttrack( p->str );
|
|
p->token = TokenNum++;
|
|
hash_add(Tname, token, (Entry *)p);
|
|
return p->token;
|
|
}
|
|
|
|
/* This is the same as addTname except we force the TokenNum to be tnum.
|
|
* We don't have to use the Forced token stuff as no tokens will have
|
|
* been defined with #tokens when this is called. This is only called
|
|
* when a #tokdefs meta-op is used.
|
|
*/
|
|
int
|
|
#ifdef __USE_PROTOS
|
|
addForcedTname( char *token, int tnum )
|
|
#else
|
|
addForcedTname( token, tnum )
|
|
char *token;
|
|
int tnum;
|
|
#endif
|
|
{
|
|
TermEntry *p;
|
|
require(token!=NULL, "addTname: invalid token name");
|
|
|
|
if ( (p=(TermEntry *)hash_get(Tname, token)) != NULL ) return p->token;
|
|
p = newTermEntry( token );
|
|
Ttrack( p->str );
|
|
p->token = tnum;
|
|
hash_add(Tname, token, (Entry *)p);
|
|
return p->token;
|
|
}
|
|
|
|
/*
|
|
* Add a token expr. Return the token number associated with it. If it already
|
|
* exists, then return the token number assigned to it.
|
|
*/
|
|
int
|
|
#ifdef __USE_PROTOS
|
|
addTexpr( char *expr )
|
|
#else
|
|
addTexpr( expr )
|
|
char *expr;
|
|
#endif
|
|
{
|
|
TermEntry *p;
|
|
require(expr!=NULL, "addTexpr: invalid regular expression");
|
|
|
|
if ( (p=(TermEntry *)hash_get(Texpr, expr)) != NULL ) return p->token;
|
|
p = newTermEntry( expr );
|
|
Ttrack( p->str );
|
|
/* track the order in which they occur */
|
|
list_add(&ExprOrder, (void *)newExpr(p->str));
|
|
p->token = TokenNum++;
|
|
hash_add(Texpr, expr, (Entry *)p);
|
|
return p->token;
|
|
}
|
|
|
|
/* return the token number of 'term'. Return 0 if no 'term' exists */
|
|
int
|
|
#ifdef __USE_PROTOS
|
|
Tnum( char *term )
|
|
#else
|
|
Tnum( term )
|
|
char *term;
|
|
#endif
|
|
{
|
|
TermEntry *p;
|
|
require(term!=NULL, "Tnum: invalid terminal");
|
|
|
|
if ( *term=='"' ) p = (TermEntry *) hash_get(Texpr, term);
|
|
else p = (TermEntry *) hash_get(Tname, term);
|
|
if ( p == NULL ) return 0;
|
|
else return p->token;
|
|
}
|
|
|
|
/* associate a Name with an expr. If both have been already assigned
|
|
* token numbers, then an error is reported. Add the token or expr
|
|
* that has not been added if no error. This 'represents' the #token
|
|
* ANTLR pseudo-op. If both have not been defined, define them both
|
|
* linked to same token number.
|
|
*/
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
Tklink( char *token, char *expr )
|
|
#else
|
|
Tklink( token, expr )
|
|
char *token;
|
|
char *expr;
|
|
#endif
|
|
{
|
|
TermEntry *p, *q;
|
|
require(token!=NULL && expr!=NULL, "Tklink: invalid token name and/or expr");
|
|
|
|
p = (TermEntry *) hash_get(Tname, token);
|
|
q = (TermEntry *) hash_get(Texpr, expr);
|
|
if ( p != NULL && q != NULL ) /* both defined */
|
|
{
|
|
warn( eMsg2("token name %s and rexpr %s already defined; ignored",
|
|
token, expr) );
|
|
return;
|
|
}
|
|
if ( p==NULL && q==NULL ) /* both not defined */
|
|
{
|
|
int t = addTname( token );
|
|
q = newTermEntry( expr );
|
|
hash_add(Texpr, expr, (Entry *)q);
|
|
q->token = t;
|
|
/* note: we use the actual ExprStr array
|
|
* here as TokenInd doesn't exist yet
|
|
*/
|
|
ExprStr[t] = q->str;
|
|
/* track the order in which they occur */
|
|
list_add(&ExprOrder, (void *)newExpr(q->str));
|
|
return;
|
|
}
|
|
if ( p != NULL ) /* one is defined, one is not */
|
|
{
|
|
q = newTermEntry( expr );
|
|
hash_add(Texpr, expr, (Entry *)q);
|
|
q->token = p->token;
|
|
ExprStr[p->token] = q->str; /* both expr and token str defined now */
|
|
list_add(&ExprOrder, (void *)newExpr(q->str));
|
|
}
|
|
else /* trying to associate name with expr here*/
|
|
{
|
|
p = newTermEntry( token );
|
|
hash_add(Tname, token, (Entry *)p);
|
|
p->token = q->token;
|
|
TokenStr[p->token] = p->str;/* both expr and token str defined now */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Given a string, this function allocates and returns a pointer to a
|
|
* hash table record of size 'sz' whose "str" pointer is reset to a position
|
|
* in the string table.
|
|
*/
|
|
Entry *
|
|
#ifdef __USE_PROTOS
|
|
newEntry( char *text, int sz )
|
|
#else
|
|
newEntry( text, sz )
|
|
char *text;
|
|
int sz;
|
|
#endif
|
|
{
|
|
Entry *p;
|
|
require(text!=NULL, "new: NULL terminal");
|
|
|
|
if ( (p = (Entry *) calloc(1,sz)) == 0 )
|
|
{
|
|
fatal_internal("newEntry: out of memory for terminals\n");
|
|
exit(PCCTS_EXIT_FAILURE);
|
|
}
|
|
p->str = mystrdup(text);
|
|
|
|
return(p);
|
|
}
|
|
|
|
/*
|
|
* add an element to a list.
|
|
*
|
|
* Any non-empty list has a sentinel node whose 'elem' pointer is really
|
|
* a pointer to the last element. (i.e. length(list) = #elemIn(list)+1).
|
|
* Elements are appended to the list.
|
|
*/
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
list_add( ListNode **list, void *e )
|
|
#else
|
|
list_add( list, e )
|
|
ListNode **list;
|
|
void *e;
|
|
#endif
|
|
{
|
|
ListNode *p, *tail;
|
|
require(e!=NULL, "list_add: attempting to add NULL list element");
|
|
|
|
p = newListNode;
|
|
require(p!=NULL, "list_add: cannot alloc new list node");
|
|
p->elem = e;
|
|
if ( *list == NULL )
|
|
{
|
|
ListNode *sentinel = newListNode;
|
|
require(sentinel!=NULL, "list_add: cannot alloc sentinel node");
|
|
*list=sentinel;
|
|
sentinel->next = p;
|
|
sentinel->elem = (char *)p; /* set tail pointer */
|
|
}
|
|
else /* find end of list */
|
|
{
|
|
tail = (ListNode *) (*list)->elem; /* get tail pointer */
|
|
tail->next = p;
|
|
(*list)->elem = (char *) p; /* reset tail */
|
|
}
|
|
}
|
|
|
|
/* MR10 list_free() frees the ListNode elements in the list */
|
|
/* MR10 if freeData then free the data elements of the list too */
|
|
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
list_free(ListNode **list,int freeData)
|
|
#else
|
|
list_free(list,freeData)
|
|
ListNode **list;
|
|
int freeData;
|
|
#endif
|
|
{
|
|
ListNode *p;
|
|
ListNode *next;
|
|
|
|
if (list == NULL) return;
|
|
if (*list == NULL) return;
|
|
for (p=*list; p != NULL; p=next) {
|
|
next=p->next;
|
|
if (freeData && p->elem != NULL) {
|
|
free( (char *) p->elem);
|
|
};
|
|
free( (char *) p);
|
|
};
|
|
*list=NULL;
|
|
}
|
|
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
list_apply( ListNode *list, void (*f)(void *) )
|
|
#else
|
|
list_apply( list, f )
|
|
ListNode *list;
|
|
void (*f)();
|
|
#endif
|
|
{
|
|
ListNode *p;
|
|
require(f!=NULL, "list_apply: NULL function to apply");
|
|
|
|
if ( list == NULL ) return;
|
|
for (p = list->next; p!=NULL; p=p->next) (*f)( p->elem );
|
|
}
|
|
|
|
/* MR27 */
|
|
|
|
#ifdef __USE_PROTOS
|
|
int list_search_cstring(ListNode *list, char * cstring)
|
|
#else
|
|
int list_search_cstring(list, cstring)
|
|
ListNode * list;
|
|
char * cstring;
|
|
#endif
|
|
{
|
|
ListNode *p;
|
|
if (list == NULL ) return 0;
|
|
for (p = list->next; p!=NULL; p=p->next) {
|
|
if (p->elem == NULL) continue;
|
|
if (0 == strcmp((char *) p->elem , cstring)) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* F O L L O W C y c l e S t u f f */
|
|
|
|
/* make a key based upon (rulename, computation, k value).
|
|
* Computation values are 'i'==FIRST, 'o'==FOLLOW.
|
|
*/
|
|
|
|
/* MR10 Make the key all characters so it can be read easily */
|
|
/* MR10 by a simple dump program. Also, separates */
|
|
/* MR10 'o' and 'i' from rule name */
|
|
|
|
char *
|
|
#ifdef __USE_PROTOS
|
|
Fkey( char *rule, int computation, int k )
|
|
#else
|
|
Fkey( rule, computation, k )
|
|
char *rule;
|
|
int computation;
|
|
int k;
|
|
#endif
|
|
{
|
|
static char key[MaxRuleName+2+2+1]; /* MR10 */
|
|
int i;
|
|
|
|
if ( k > 99 ) /* MR10 */
|
|
fatal("k>99 is too big for this implementation of ANTLR!\n"); /* MR10 */
|
|
if ( (i=strlen(rule)) > MaxRuleName ) /* MR10 */
|
|
fatal( eMsgd("rule name > max of %d\n", MaxRuleName) ); /* MR10 */
|
|
strcpy(key,rule);
|
|
|
|
/* MR10 */ key[i]='*';
|
|
/* MR10 */ key[i+1] = (char) computation; /* MR20 G. Hobbelt */
|
|
/* MR10 */ if (k < 10) {
|
|
/* MR10 */ key[i+2] = (char) ( '0' + k);
|
|
/* MR10 */ key[i+3] = '\0';
|
|
/* MR10 */ } else {
|
|
/* MR10 */ key[i+2] = (char) ( '0' + k/10);
|
|
/* MR10 */ key[i+3] = (char) ( '0' + k % 10);
|
|
/* MR10 */ key[i+4] = '\0';
|
|
/* MR10 */ };
|
|
|
|
return key;
|
|
}
|
|
|
|
/* Push a rule onto the kth FOLLOW stack */
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
FoPush( char *rule, int k )
|
|
#else
|
|
FoPush( rule, k )
|
|
char *rule;
|
|
int k;
|
|
#endif
|
|
{
|
|
RuleEntry *r;
|
|
require(rule!=NULL, "FoPush: tried to push NULL rule");
|
|
require(k<=CLL_k, "FoPush: tried to access non-existent stack");
|
|
|
|
/*fprintf(stderr, "FoPush(%s)\n", rule);*/
|
|
r = (RuleEntry *) hash_get(Rname, rule);
|
|
if ( r == NULL ) {fatal_internal( eMsg1("rule %s must be defined but isn't", rule) );}
|
|
if ( FoStack[k] == NULL ) /* Does the kth stack exist yet? */
|
|
{
|
|
/*fprintf(stderr, "allocating FoStack\n");*/
|
|
FoStack[k] = (int *) calloc(FoStackSize, sizeof(int));
|
|
require(FoStack[k]!=NULL, "FoPush: cannot allocate FOLLOW stack\n");
|
|
}
|
|
if ( FoTOS[k] == NULL )
|
|
{
|
|
FoTOS[k]=FoStack[k];
|
|
*(FoTOS[k]) = r->rulenum;
|
|
}
|
|
else
|
|
{
|
|
#ifdef MEMCHK
|
|
require(valid(FoStack[k]), "FoPush: invalid FoStack");
|
|
#endif
|
|
if ( FoTOS[k] >= &(FoStack[k][FoStackSize-1]) )
|
|
fatal( eMsgd("exceeded max depth of FOLLOW recursion (%d)\n",
|
|
FoStackSize) );
|
|
require(FoTOS[k]>=FoStack[k],
|
|
eMsg1("FoPush: FoStack stack-ptr is playing out of its sandbox",
|
|
rule));
|
|
++(FoTOS[k]);
|
|
*(FoTOS[k]) = r->rulenum;
|
|
}
|
|
{
|
|
/*
|
|
**** int *p;
|
|
**** fprintf(stderr, "FoStack[k=%d]:\n", k);
|
|
**** for (p=FoStack[k]; p<=FoTOS[k]; p++)
|
|
**** {
|
|
**** fprintf(stderr, "\t%s\n", RulePtr[*p]->rname);
|
|
**** }
|
|
*/
|
|
}
|
|
}
|
|
|
|
/* Pop one rule off of the FOLLOW stack. TOS ptr is NULL if empty. */
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
FoPop( int k )
|
|
#else
|
|
FoPop( k )
|
|
int k;
|
|
#endif
|
|
{
|
|
require(k<=CLL_k, "FoPop: tried to access non-existent stack");
|
|
/*fprintf(stderr, "FoPop\n");*/
|
|
require(FoTOS[k]>=FoStack[k]&&FoTOS[k]<=&(FoStack[k][FoStackSize-1]),
|
|
"FoPop: FoStack stack-ptr is playing out of its sandbox");
|
|
if ( FoTOS[k] == FoStack[k] ) FoTOS[k] = NULL;
|
|
else (FoTOS[k])--;
|
|
}
|
|
|
|
/* Compute FOLLOW cycle.
|
|
* Mark all FOLLOW sets for rules in cycle as incomplete.
|
|
* Then, save cycle on the cycle list (Cycles) for later resolution.
|
|
* The Cycle is stored in the form:
|
|
* (head of cycle==croot, rest of rules in cycle==cyclicDep)
|
|
*
|
|
* e.g. (Fo means "FOLLOW of", "-->" means requires or depends on)
|
|
*
|
|
* Fo(x)-->Fo(a)-->Fo(b)-->Fo(c)-->Fo(x)
|
|
* ^----Infinite recursion (cycle)
|
|
*
|
|
* the cycle would be: x -> {a,b,c} or stored as (x,{a,b,c}). Fo(x) depends
|
|
* on the FOLLOW of a,b, and c. The root of a cycle is always complete after
|
|
* Fo(x) finishes. Fo(a,b,c) however are not. It turns out that all rules
|
|
* in a FOLLOW cycle have the same FOLLOW set.
|
|
*/
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
RegisterCycle( char *rule, int k )
|
|
#else
|
|
RegisterCycle( rule, k )
|
|
char *rule;
|
|
int k;
|
|
#endif
|
|
{
|
|
CacheEntry *f;
|
|
Cycle *c;
|
|
int *p;
|
|
RuleEntry *r;
|
|
require(rule!=NULL, "RegisterCycle: tried to register NULL rule");
|
|
require(k<=CLL_k, "RegisterCycle: tried to access non-existent stack");
|
|
|
|
/*fprintf(stderr, "RegisterCycle(%s)\n", rule);*/
|
|
/* Find cycle start */
|
|
r = (RuleEntry *) hash_get(Rname, rule);
|
|
require(r!=NULL,eMsg1("rule %s must be defined but isn't", rule));
|
|
require(FoTOS[k]>=FoStack[k]&&FoTOS[k]<=&(FoStack[k][FoStackSize-1]),
|
|
eMsg1("RegisterCycle(%s): FoStack stack-ptr is playing out of its sandbox",
|
|
rule));
|
|
/*** if ( FoTOS[k]<FoStack[k]||FoTOS[k]>&(FoStack[k][FoStackSize-1]) )
|
|
**** {
|
|
**** fprintf(stderr, "RegisterCycle(%s): FoStack stack-ptr is playing out of its sandbox\n",
|
|
**** rule);
|
|
**** fprintf(stderr, "RegisterCycle: sp==0x%x out of bounds 0x%x...0x%x\n",
|
|
**** FoTOS[k], FoStack[k], &(FoStack[k][FoStackSize-1]));
|
|
**** exit(PCCTS_EXIT_FAILURE);
|
|
**** }
|
|
****/
|
|
|
|
#ifdef MEMCHK
|
|
require(valid(FoStack[k]), "RegisterCycle: invalid FoStack");
|
|
#endif
|
|
for (p=FoTOS[k]; *p != r->rulenum && p >= FoStack[k]; --p) {;}
|
|
require(p>=FoStack[k], "RegisterCycle: FoStack is screwed up beyond belief");
|
|
if ( p == FoTOS[k] ) return; /* don't worry about cycles to oneself */
|
|
|
|
/* compute cyclic dependents (rules in cycle except head) */
|
|
c = newCycle;
|
|
require(c!=NULL, "RegisterCycle: couldn't alloc new cycle");
|
|
c->cyclicDep = empty;
|
|
c->croot = *p++; /* record root of cycle */
|
|
for (; p<=FoTOS[k]; p++)
|
|
{
|
|
/* Mark all dependent rules as incomplete */
|
|
f = (CacheEntry *) hash_get(Fcache, Fkey(RulePtr[*p]->rname,'o',k));
|
|
if ( f==NULL )
|
|
{
|
|
f = newCacheEntry( Fkey(RulePtr[*p]->rname,'o',k) );
|
|
hash_add(Fcache, Fkey(RulePtr[*p]->rname,'o',k), (Entry *)f);
|
|
}
|
|
f->incomplete = TRUE;
|
|
|
|
set_orel(*p, &(c->cyclicDep)); /* mark rule as dependent of croot */
|
|
}
|
|
list_add(&(Cycles[k]), (void *)c);
|
|
}
|
|
|
|
/* make all rules in cycle complete
|
|
*
|
|
* while ( some set has changed ) do
|
|
* for each cycle do
|
|
* if degree of FOLLOW set for croot > old degree then
|
|
* update all FOLLOW sets for rules in cyclic dependency
|
|
* change = TRUE
|
|
* endif
|
|
* endfor
|
|
* endwhile
|
|
*/
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
ResolveFoCycles( int k )
|
|
#else
|
|
ResolveFoCycles( k )
|
|
int k;
|
|
#endif
|
|
{
|
|
ListNode *p, *q;
|
|
Cycle *c;
|
|
int changed = 1;
|
|
CacheEntry *f,*g;
|
|
int r;
|
|
/* int i; */ /* MR10 not useful */
|
|
unsigned d;
|
|
|
|
unsigned *cursor; /* MR10 */
|
|
unsigned *origin; /* MR10 */
|
|
|
|
/*fprintf(stderr, "Resolving following cycles for %d\n", k);*/
|
|
while ( changed )
|
|
{
|
|
changed = 0;
|
|
/* MR10 i = 0; */
|
|
for (p = Cycles[k]->next; p!=NULL; p=p->next)
|
|
{
|
|
c = (Cycle *) p->elem;
|
|
/*fprintf(stderr, "cycle %d: %s -->", i++, RulePtr[c->croot]->rname);*/
|
|
/*s_fprT(stderr, c->cyclicDep);*/
|
|
/*fprintf(stderr, "\n");*/
|
|
f = (CacheEntry *)
|
|
hash_get(Fcache, Fkey(RulePtr[c->croot]->rname,'o',k));
|
|
require(f!=NULL, eMsg1("FOLLOW(%s) must be in cache but isn't", RulePtr[c->croot]->rname) );
|
|
if ( (d=set_deg(f->fset)) > c->deg )
|
|
{
|
|
/*fprintf(stderr, "Fo(%s) has changed\n", RulePtr[c->croot]->rname);*/
|
|
changed = 1;
|
|
c->deg = d; /* update cycle FOLLOW set degree */
|
|
|
|
/* MR10 */ origin=set_pdq(c->cyclicDep);
|
|
/* MR10 */ for (cursor=origin; *cursor != nil; cursor++) {
|
|
/* MR10 */ r=*cursor;
|
|
|
|
/******** while ( !set_nil(c->cyclicDep) ) { *****/
|
|
/******** r = set_int(c->cyclicDep); *****/
|
|
/******** set_rm(r, c->cyclicDep); *****/
|
|
|
|
/*fprintf(stderr, "updating Fo(%s)\n", RulePtr[r]->rname);*/
|
|
g = (CacheEntry *)
|
|
hash_get(Fcache, Fkey(RulePtr[r]->rname,'o',k));
|
|
require(g!=NULL, eMsg1("FOLLOW(%s) must be in cache but isn't", RulePtr[r]->rname) );
|
|
set_orin(&(g->fset), f->fset);
|
|
g->incomplete = FALSE;
|
|
}
|
|
/* MR10 */ free( (char *) origin);
|
|
/* MR10 */ origin=NULL;
|
|
}
|
|
}
|
|
/* MR10 - this if statement appears to be meaningless since i is always 0 */
|
|
/* MR10 if ( i == 1 ) changed = 0; */ /* if only 1 cycle, no need to repeat */
|
|
}
|
|
/* kill Cycle list */
|
|
for (q = Cycles[k]->next; q != NULL; q=p)
|
|
{
|
|
p = q->next;
|
|
set_free( ((Cycle *)q->elem)->cyclicDep );
|
|
free((char *)q);
|
|
}
|
|
free( (char *)Cycles[k] );
|
|
Cycles[k] = NULL;
|
|
}
|
|
|
|
|
|
/* P r i n t i n g S y n t a x D i a g r a m s */
|
|
|
|
static void
|
|
#ifdef __USE_PROTOS
|
|
pBlk( Junction *q, int btype )
|
|
#else
|
|
pBlk( q, btype )
|
|
Junction *q;
|
|
int btype;
|
|
#endif
|
|
{
|
|
int k,a;
|
|
Junction *alt, *p;
|
|
|
|
q->end->pvisited = TRUE;
|
|
if ( btype == aLoopBegin )
|
|
{
|
|
require(q->p2!=NULL, "pBlk: invalid ()* block");
|
|
PRINT(q->p1);
|
|
alt = (Junction *)q->p2;
|
|
PRINT(alt->p1);
|
|
if ( PrintAnnotate )
|
|
{
|
|
printf(" /* Opt ");
|
|
k = 1;
|
|
while ( !set_nil(alt->fset[k]) )
|
|
{
|
|
s_fprT(stdout, alt->fset[k]);
|
|
if ( k++ == CLL_k ) break;
|
|
if ( !set_nil(alt->fset[k]) ) printf(", ");
|
|
}
|
|
printf(" */\n");
|
|
}
|
|
return;
|
|
}
|
|
for (a=1,alt=q; alt != NULL; alt= (Junction *) alt->p2, a++)
|
|
{
|
|
if ( alt->p1 != NULL ) PRINT(alt->p1);
|
|
if ( PrintAnnotate )
|
|
{
|
|
printf( " /* [%d] ", alt->altnum);
|
|
k = 1;
|
|
while ( !set_nil(alt->fset[k]) )
|
|
{
|
|
s_fprT(stdout, alt->fset[k]);
|
|
if ( k++ == CLL_k ) break;
|
|
if ( !set_nil(alt->fset[k]) ) printf(", ");
|
|
}
|
|
if ( alt->p2 == NULL && btype == aOptBlk )
|
|
printf( " (optional branch) */\n");
|
|
else printf( " */\n");
|
|
}
|
|
|
|
/* ignore implied empty alt of Plus blocks */
|
|
if ( alt->p2 != NULL && ((Junction *)alt->p2)->ignore ) break;
|
|
|
|
if ( alt->p2 != NULL && !(((Junction *)alt->p2)->p2==NULL && btype == aOptBlk) )
|
|
{
|
|
if ( pLevel == 1 )
|
|
{
|
|
printf("\n");
|
|
if ( a+1==pAlt1 || a+1==pAlt2 ) printf("=>");
|
|
printf("\t");
|
|
}
|
|
else printf(" ");
|
|
printf("|");
|
|
if ( pLevel == 1 )
|
|
{
|
|
p = (Junction *) ((Junction *)alt->p2)->p1;
|
|
while ( p!=NULL )
|
|
{
|
|
if ( p->ntype==nAction )
|
|
{
|
|
p=(Junction *)((ActionNode *)p)->next;
|
|
continue;
|
|
}
|
|
if ( p->ntype!=nJunction )
|
|
{
|
|
break;
|
|
}
|
|
if ( p->jtype==EndBlk || p->jtype==EndRule )
|
|
{
|
|
p = NULL;
|
|
break;
|
|
}
|
|
p = (Junction *)p->p1;
|
|
}
|
|
if ( p==NULL ) printf("\n\t"); /* Empty alt? */
|
|
}
|
|
}
|
|
}
|
|
q->end->pvisited = FALSE;
|
|
}
|
|
|
|
/* How to print out a junction */
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
pJunc( Junction *q )
|
|
#else
|
|
pJunc( q )
|
|
Junction *q;
|
|
#endif
|
|
{
|
|
int dum_k;
|
|
int doing_rule;
|
|
require(q!=NULL, "pJunc: NULL node");
|
|
require(q->ntype==nJunction, "pJunc: not junction");
|
|
|
|
if ( q->pvisited == TRUE ) return;
|
|
q->pvisited = TRUE;
|
|
switch ( q->jtype )
|
|
{
|
|
case aSubBlk :
|
|
if ( PrintAnnotate ) First(q, 1, q->jtype, &dum_k);
|
|
if ( q->end->p1 != NULL && ((Junction *)q->end->p1)->ntype==nJunction &&
|
|
((Junction *)q->end->p1)->jtype == EndRule ) doing_rule = 1;
|
|
else doing_rule = 0;
|
|
pLevel++;
|
|
if ( pLevel==1 )
|
|
{
|
|
if ( pAlt1==1 ) printf("=>");
|
|
printf("\t");
|
|
}
|
|
else printf(" ");
|
|
if ( doing_rule )
|
|
{
|
|
if ( pLevel==1 ) printf(" ");
|
|
pBlk(q,q->jtype);
|
|
}
|
|
else {
|
|
printf("(");
|
|
if ( pLevel==1 ) printf(" ");
|
|
pBlk(q,q->jtype);
|
|
if ( pLevel>1 ) printf(" ");
|
|
printf(")");
|
|
}
|
|
if ( q->guess ) printf("?");
|
|
pLevel--;
|
|
if ( PrintAnnotate ) freeBlkFsets(q);
|
|
if ( q->end->p1 != NULL ) PRINT(q->end->p1);
|
|
break;
|
|
case aOptBlk :
|
|
if ( PrintAnnotate ) First(q, 1, q->jtype, &dum_k);
|
|
pLevel++;
|
|
if ( pLevel==1 )
|
|
{
|
|
if ( pAlt1==1 ) printf("=>");
|
|
printf("\t");
|
|
}
|
|
else printf(" ");
|
|
printf("{");
|
|
if ( pLevel==1 ) printf(" ");
|
|
pBlk(q,q->jtype);
|
|
if ( pLevel>1 ) printf(" ");
|
|
else printf("\n\t");
|
|
printf("}");
|
|
pLevel--;
|
|
if ( PrintAnnotate ) freeBlkFsets(q);
|
|
if ( q->end->p1 != NULL ) PRINT(q->end->p1);
|
|
break;
|
|
case aLoopBegin :
|
|
if ( PrintAnnotate ) First(q, 1, q->jtype, &dum_k);
|
|
pLevel++;
|
|
if ( pLevel==1 )
|
|
{
|
|
if ( pAlt1==1 ) printf("=>");
|
|
printf("\t");
|
|
}
|
|
else printf(" ");
|
|
printf("(");
|
|
if ( pLevel==1 ) printf(" ");
|
|
pBlk(q,q->jtype);
|
|
if ( pLevel>1 ) printf(" ");
|
|
else printf("\n\t");
|
|
printf(")*");
|
|
pLevel--;
|
|
if ( PrintAnnotate ) freeBlkFsets(q);
|
|
if ( q->end->p1 != NULL ) PRINT(q->end->p1);
|
|
break;
|
|
case aLoopBlk :
|
|
if ( PrintAnnotate ) First(q, 1, q->jtype, &dum_k);
|
|
pBlk(q,q->jtype);
|
|
if ( PrintAnnotate ) freeBlkFsets(q);
|
|
break;
|
|
case aPlusBlk :
|
|
if ( PrintAnnotate ) First(q, 1, q->jtype, &dum_k);
|
|
pLevel++;
|
|
if ( pLevel==1 )
|
|
{
|
|
if ( pAlt1==1 ) printf("=>");
|
|
printf("\t");
|
|
}
|
|
else printf(" ");
|
|
printf("(");
|
|
if ( pLevel==1 ) printf(" ");
|
|
pBlk(q,q->jtype);
|
|
if ( pLevel>1 ) printf(" ");
|
|
printf(")+");
|
|
pLevel--;
|
|
if ( PrintAnnotate ) freeBlkFsets(q);
|
|
if ( q->end->p1 != NULL ) PRINT(q->end->p1);
|
|
break;
|
|
case EndBlk :
|
|
break;
|
|
case RuleBlk :
|
|
printf( "\n%s :\n", q->rname);
|
|
PRINT(q->p1);
|
|
if ( q->p2 != NULL ) PRINT(q->p2);
|
|
break;
|
|
case Generic :
|
|
if ( q->p1 != NULL ) PRINT(q->p1);
|
|
q->pvisited = FALSE;
|
|
if ( q->p2 != NULL ) PRINT(q->p2);
|
|
break;
|
|
case EndRule :
|
|
printf( "\n\t;\n");
|
|
break;
|
|
}
|
|
q->pvisited = FALSE;
|
|
}
|
|
|
|
/* How to print out a rule reference node */
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
pRuleRef( RuleRefNode *p )
|
|
#else
|
|
pRuleRef( p )
|
|
RuleRefNode *p;
|
|
#endif
|
|
{
|
|
require(p!=NULL, "pRuleRef: NULL node");
|
|
require(p->ntype==nRuleRef, "pRuleRef: not rule ref node");
|
|
|
|
printf( " %s", p->text);
|
|
PRINT(p->next);
|
|
}
|
|
|
|
/* How to print out a terminal node */
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
pToken( TokNode *p )
|
|
#else
|
|
pToken( p )
|
|
TokNode *p;
|
|
#endif
|
|
{
|
|
require(p!=NULL, "pToken: NULL node");
|
|
require(p->ntype==nToken, "pToken: not token node");
|
|
|
|
if ( p->wild_card ) printf(" .");
|
|
printf( " %s", TerminalString(p->token));
|
|
PRINT(p->next);
|
|
}
|
|
|
|
/* How to print out a terminal node */
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
pAction( ActionNode *p )
|
|
#else
|
|
pAction( p )
|
|
ActionNode *p;
|
|
#endif
|
|
{
|
|
require(p!=NULL, "pAction: NULL node");
|
|
require(p->ntype==nAction, "pAction: not action node");
|
|
|
|
PRINT(p->next);
|
|
}
|
|
|
|
/* F i l l F o l l o w L i s t s */
|
|
|
|
/*
|
|
* Search all rules for all rule reference nodes, q to rule, r.
|
|
* Add q->next to follow list dangling off of rule r.
|
|
* i.e.
|
|
*
|
|
* r: -o-R-o-->o--> Ptr to node following rule r in another rule
|
|
* |
|
|
* o--> Ptr to node following another reference to r.
|
|
*
|
|
* This is the data structure employed to avoid FOLLOW set computation. We
|
|
* simply compute the FIRST (reach) of the EndRule Node which follows the
|
|
* list found at the end of all rules which are referenced elsewhere. Rules
|
|
* not invoked by other rules have no follow list (r->end->p1==NULL).
|
|
* Generally, only start symbols are not invoked by another rule.
|
|
*
|
|
* Note that this mechanism also gives a free cross-reference mechanism.
|
|
*
|
|
* The entire syntax diagram is layed out like this:
|
|
*
|
|
* SynDiag
|
|
* |
|
|
* v
|
|
* o-->R1--o
|
|
* |
|
|
* o-->R2--o
|
|
* |
|
|
* ...
|
|
* |
|
|
* o-->Rn--o
|
|
*
|
|
*/
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
FoLink( Node *p )
|
|
#else
|
|
FoLink( p )
|
|
Node *p;
|
|
#endif
|
|
{
|
|
RuleEntry *q;
|
|
Junction *j = (Junction *) p;
|
|
RuleRefNode *r = (RuleRefNode *) p;
|
|
|
|
if ( p==NULL ) return;
|
|
require(p->ntype>=1 && p->ntype<=NumNodeTypes,
|
|
eMsgd("FoLink: invalid diagram node: ntype==%d",p->ntype));
|
|
switch ( p->ntype )
|
|
{
|
|
case nJunction :
|
|
if ( j->fvisited ) return;
|
|
if ( j->jtype == EndRule ) return;
|
|
j->fvisited = TRUE;
|
|
FoLink( j->p1 );
|
|
FoLink( j->p2 );
|
|
/* MR14 */
|
|
/* MR14 */ /* Need to determine whether the guess block is an */
|
|
/* MR14 */ /* of the form (alpha)? beta before follow sets are */
|
|
/* MR14 */ /* computed. This is necessary to solve problem */
|
|
/* MR14 */ /* of doing follow on the alpha of an (alpha)? beta block. */
|
|
/* MR14 */
|
|
/* MR14 */ /* This is performed by analysis_point as a side-effect. */
|
|
/* MR14 */
|
|
/* MR14 */
|
|
/* MR14 */ if (j->jtype == aSubBlk && j->guess) {
|
|
/* MR14 */ Junction *ignore;
|
|
/* MR14 */ ignore=analysis_point(j);
|
|
/* MR14 */ }
|
|
/* MR14 */
|
|
return;
|
|
case nRuleRef :
|
|
if ( r->linked ) return;
|
|
q = (RuleEntry *) hash_get(Rname, r->text);
|
|
if ( q == NULL )
|
|
{
|
|
warnFL( eMsg1("rule %s not defined",r->text), FileStr[r->file], r->line );
|
|
}
|
|
else
|
|
{
|
|
if ( r->parms!=NULL && RulePtr[q->rulenum]->pdecl==NULL )
|
|
{
|
|
warnFL( eMsg1("rule %s accepts no parameter(s)", r->text),
|
|
FileStr[r->file], r->line );
|
|
}
|
|
if ( r->parms==NULL && RulePtr[q->rulenum]->pdecl!=NULL )
|
|
{
|
|
warnFL( eMsg1("rule %s requires parameter(s)", r->text),
|
|
FileStr[r->file], r->line );
|
|
}
|
|
if ( r->assign!=NULL && RulePtr[q->rulenum]->ret==NULL )
|
|
{
|
|
warnFL( eMsg1("rule %s yields no return value(s)", r->text),
|
|
FileStr[r->file], r->line );
|
|
}
|
|
if ( r->assign==NULL && RulePtr[q->rulenum]->ret!=NULL )
|
|
{
|
|
warnFL( eMsg1("rule %s returns a value(s)", r->text),
|
|
FileStr[r->file], r->line );
|
|
}
|
|
if ( !r->linked )
|
|
{
|
|
addFoLink( r->next, r->rname, RulePtr[q->rulenum] );
|
|
r->linked = TRUE;
|
|
}
|
|
}
|
|
FoLink( r->next );
|
|
return;
|
|
case nToken :
|
|
FoLink( ((TokNode *)p)->next );
|
|
return;
|
|
case nAction :
|
|
FoLink( ((ActionNode *)p)->next );
|
|
return;
|
|
default :
|
|
fatal_internal("invalid node type");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add a reference to the end of a rule.
|
|
*
|
|
* 'r' points to the RuleBlk node in a rule. r->end points to the last node
|
|
* (EndRule jtype) in a rule.
|
|
*
|
|
* Initial:
|
|
* r->end --> o
|
|
*
|
|
* After:
|
|
* r->end --> o-->o--> Ptr to node following rule r in another rule
|
|
* |
|
|
* o--> Ptr to node following another reference to r.
|
|
*
|
|
* Note that the links are added to the head of the list so that r->end->p1
|
|
* always points to the most recently added follow-link. At the end, it should
|
|
* point to the last reference found in the grammar (starting from the 1st rule).
|
|
*/
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
addFoLink( Node *p, char *rname, Junction *r )
|
|
#else
|
|
addFoLink( p, rname, r )
|
|
Node *p;
|
|
char *rname;
|
|
Junction *r;
|
|
#endif
|
|
{
|
|
Junction *j;
|
|
require(r!=NULL, "addFoLink: incorrect rule graph");
|
|
require(r->end!=NULL, "addFoLink: incorrect rule graph");
|
|
require(r->end->jtype==EndRule, "addFoLink: incorrect rule graph");
|
|
require(p!=NULL, "addFoLink: NULL FOLLOW link");
|
|
|
|
j = newJunction();
|
|
j->rname = rname; /* rname on follow links point to target rule */
|
|
j->p1 = p; /* link to other rule */
|
|
j->p2 = (Node *) r->end->p1;/* point to head of list */
|
|
r->end->p1 = (Node *) j; /* reset head to point to new node */
|
|
}
|
|
|
|
void
|
|
#ifdef __USE_PROTOS
|
|
GenCrossRef( Junction *p )
|
|
#else
|
|
GenCrossRef( p )
|
|
Junction *p;
|
|
#endif
|
|
{
|
|
set a;
|
|
Junction *j;
|
|
RuleEntry *q;
|
|
unsigned e;
|
|
require(p!=NULL, "GenCrossRef: why are you passing me a null grammar?");
|
|
|
|
printf("Cross Reference:\n\n");
|
|
a = empty;
|
|
for (; p!=NULL; p = (Junction *)p->p2)
|
|
{
|
|
printf("Rule %20s referenced by {", p->rname);
|
|
/* make a set of rules for uniqueness */
|
|
for (j = (Junction *)(p->end)->p1; j!=NULL; j = (Junction *)j->p2)
|
|
{
|
|
q = (RuleEntry *) hash_get(Rname, j->rname);
|
|
require(q!=NULL, "GenCrossRef: FoLinks are screwed up");
|
|
set_orel(q->rulenum, &a);
|
|
}
|
|
for (; !set_nil(a); set_rm(e, a))
|
|
{
|
|
e = set_int(a);
|
|
printf(" %s", RulePtr[e]->rname);
|
|
}
|
|
printf(" }\n");
|
|
}
|
|
set_free( a );
|
|
}
|
|
|
|
/*
|
|
The single argument is a pointer to the start of an element of a
|
|
C++ style function prototypet list. Given a pointer to the start of
|
|
an formal we must locate the comma (or the end of the string)
|
|
and locate the datatype, formal name, and initial value expression.
|
|
|
|
The function returns a pointer to the character following the comma
|
|
which terminates the formal declaration, or a pointer to the end of
|
|
the string if none was found.
|
|
|
|
I thought we were parsing specialists, how come I'm doing this by
|
|
hand written code ?
|
|
|
|
Examples of input:
|
|
|
|
Foo f,
|
|
Foo f = Foo(1),
|
|
Foo f = Foo(1,2),
|
|
Foo f = &farray[1,2],
|
|
Foo f = ",",
|
|
Foo f = ',',
|
|
TFoo<int,char> f = TFoo<int,char>(1,2),
|
|
|
|
A non-zero value for nesting indicates a problem matching '(' and ')',
|
|
'[' and ']', '<' and '>', '{' and '}', or improperly terminated string
|
|
or character literal.
|
|
|
|
*/
|
|
|
|
|
|
/*
|
|
* Don't care if it is a valid string literal or not, just find the end
|
|
* Start with pointer to leading "\""
|
|
*/
|
|
|
|
#ifdef __USE_PROTOS
|
|
char * skipStringLiteral(char *pCurrent)
|
|
#else
|
|
char * skipStringLiteral(pCurrent)
|
|
char *pCurrent;
|
|
#endif
|
|
{
|
|
char *p = pCurrent;
|
|
if (*p == 0) return p;
|
|
require (*p == '\"', "skipStringLiteral")
|
|
p++;
|
|
for (p = p; *p != 0; p++) {
|
|
if (*p == '\\') {
|
|
p++;
|
|
if (*p == 0) break;
|
|
p++;
|
|
}
|
|
if (*p == '\"') {
|
|
p++;
|
|
break;
|
|
}
|
|
}
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
* Don't care if it is a valid character literal or not, just find the end
|
|
* Start with pointer to leading "'"
|
|
*/
|
|
|
|
#ifdef __USE_PROTOS
|
|
char * skipCharLiteral(char *pStart)
|
|
#else
|
|
char * skipCharLiteral(pStart)
|
|
char *pStart;
|
|
#endif
|
|
{
|
|
char *p = pStart;
|
|
if (*p == 0) return p;
|
|
require (*p == '\'', "skipCharLiteral")
|
|
p++;
|
|
for (p = p; *p != 0; p++) {
|
|
if (*p == '\\') {
|
|
p++;
|
|
if (*p == 0) break;
|
|
p++;
|
|
}
|
|
if (*p == '\'') {
|
|
p++;
|
|
break;
|
|
}
|
|
}
|
|
return p;
|
|
}
|
|
|
|
#ifdef __USE_PROTOS
|
|
char * skipSpaces(char *pStart)
|
|
#else
|
|
char * skipSpaces(pStart)
|
|
char * pStart;
|
|
#endif
|
|
{
|
|
char *p = pStart;
|
|
while (*p != 0 && isspace(*p)) p++;
|
|
return p;
|
|
}
|
|
|
|
#ifdef __USE_PROTOS
|
|
char * skipToSeparatorOrEqualSign(char *pStart, int *pNest)
|
|
#else
|
|
char * skipToSeparatorOrEqualSign(pStart, pNest)
|
|
char *pStart;
|
|
int *pNest;
|
|
#endif
|
|
{
|
|
char *p = pStart;
|
|
|
|
int nest = 0;
|
|
|
|
*pNest = (-1);
|
|
|
|
while (*p != 0) {
|
|
switch (*p) {
|
|
|
|
case '(' :
|
|
case '[' :
|
|
case '<' :
|
|
case '{' :
|
|
nest++;
|
|
p++;
|
|
break;
|
|
|
|
case ')' :
|
|
case ']' :
|
|
case '>' :
|
|
case '}' :
|
|
nest--;
|
|
p++;
|
|
break;
|
|
|
|
case '"' :
|
|
p = skipStringLiteral(p);
|
|
break;
|
|
|
|
case '\'' :
|
|
p = skipCharLiteral(p);
|
|
break;
|
|
|
|
case '\\':
|
|
p++;
|
|
if (*p == 0) goto EXIT;
|
|
p++;
|
|
break;
|
|
|
|
case ',':
|
|
case '=':
|
|
if (nest == 0) goto EXIT;
|
|
p++;
|
|
break;
|
|
|
|
default:
|
|
p++;
|
|
}
|
|
}
|
|
EXIT:
|
|
*pNest = nest;
|
|
return p;
|
|
}
|
|
|
|
#ifdef __USE_PROTOS
|
|
char * skipToSeparator(char *pStart, int *pNest)
|
|
#else
|
|
char * skipToSeparator(pStart, pNest)
|
|
char *pStart;
|
|
int *pNest;
|
|
#endif
|
|
{
|
|
char * p = pStart;
|
|
for ( ; ; ) {
|
|
p = skipToSeparatorOrEqualSign(p, pNest);
|
|
if (*pNest != 0) return p;
|
|
if (*p == ',') return p;
|
|
if (*p == 0) return p;
|
|
p++;
|
|
}
|
|
}
|
|
|
|
/* skip to just past the "=" separating the declaration from the initialization value */
|
|
|
|
#ifdef __USE_PROTOS
|
|
char * getInitializer(char *pStart)
|
|
#else
|
|
char * getInitializer(pStart)
|
|
char * pStart;
|
|
#endif
|
|
{
|
|
char *p;
|
|
char *pDataType;
|
|
char *pSymbol;
|
|
char *pEqualSign;
|
|
char *pValue;
|
|
char *pSeparator;
|
|
int nest = 0;
|
|
|
|
require(pStart!=NULL, "getInitializer: invalid string");
|
|
|
|
p = endFormal(pStart,
|
|
&pDataType,
|
|
&pSymbol,
|
|
&pEqualSign,
|
|
&pValue,
|
|
&pSeparator,
|
|
&nest);
|
|
if (nest != 0) return NULL;
|
|
if (pEqualSign == NULL) return NULL;
|
|
if (pValue == NULL) return NULL;
|
|
return strBetween(pValue, NULL, pSeparator);
|
|
}
|
|
|
|
/*
|
|
Examines the string from pStart to pEnd-1.
|
|
If the string has 0 length or is entirely white space
|
|
returns 1. Otherwise 0.
|
|
*/
|
|
|
|
#ifdef __USE_PROTOS
|
|
int isWhiteString(const char *pStart, const char *pEnd)
|
|
#else
|
|
int isWhiteString(pStart, pEnd)
|
|
const char *pStart;
|
|
const char *pEnd;
|
|
#endif
|
|
{
|
|
const char *p;
|
|
for (p = pStart; p < pEnd; p++) {
|
|
if (! isspace(*p)) return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
This replaces HasComma() which couldn't distinguish
|
|
|
|
foo ["a,b"]
|
|
|
|
from:
|
|
|
|
foo[a,b]
|
|
|
|
*/
|
|
|
|
#ifdef __USE_PROTOS
|
|
int hasMultipleOperands(char *pStart)
|
|
#else
|
|
int hasMultipleOperands(pStart)
|
|
char *pStart;
|
|
#endif
|
|
{
|
|
char *p = pStart;
|
|
int nest = 0;
|
|
|
|
p = skipSpaces(p);
|
|
if (*p == 0) return 0;
|
|
p = skipToSeparator(p, &nest);
|
|
if (nest == 0 && *p == ',') return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
#define MAX_STR_BETWEEN_WORK_AREA 1000
|
|
|
|
static char strBetweenWorkArea[MAX_STR_BETWEEN_WORK_AREA];
|
|
|
|
|
|
/*
|
|
strBetween(pStart, pNext, pStop)
|
|
|
|
Creates a null terminated string by copying the text between two pointers
|
|
to a work area. The start of the string is pStart. The end of the string
|
|
is the character before pNext, or if pNext is null then the character before
|
|
pStop. Trailing spaces are not included in the copy operation.
|
|
|
|
This is used when a string contains several parts. The pNext part may be
|
|
optional. The pStop will stop the scan when the optional part is not present
|
|
(is a null pointer).
|
|
*/
|
|
|
|
#ifdef __USE_PROTOS
|
|
char *strBetween(char *pStart, char *pNext, char *pStop)
|
|
#else
|
|
char *strBetween(pStart, pNext, pStop)
|
|
char *pStart;
|
|
char *pNext;
|
|
char *pStop;
|
|
#endif
|
|
{
|
|
char *p;
|
|
char *q = strBetweenWorkArea;
|
|
const char *pEnd;
|
|
|
|
pEnd = (pNext != NULL) ? pNext : pStop;
|
|
|
|
require (pEnd != NULL, "pEnd == NULL");
|
|
require (pEnd >= pStart, "pEnd < pStart");
|
|
for (pEnd--; pEnd >= pStart; pEnd--) { /* MR31 */
|
|
if (! isspace(*pEnd)) break;
|
|
}
|
|
for (p = pStart;
|
|
p <= pEnd && q < &strBetweenWorkArea[MAX_STR_BETWEEN_WORK_AREA-2];
|
|
p++, q++) {
|
|
*q = *p;
|
|
}
|
|
*q = 0;
|
|
return strBetweenWorkArea;
|
|
}
|
|
|
|
/*
|
|
function Returns pointer to character following separator at
|
|
value which to continue search for next formal. If at the
|
|
end of the string a pointer to the null byte at the
|
|
end of the string is returned.
|
|
|
|
pStart Pointer to the starting position of the formal list
|
|
|
|
This may be the middle of a longer string, for example
|
|
when looking for the end of formal #3 starting from
|
|
the middle of the complete formal list.
|
|
|
|
ppDataType Returns a pointer to the start of the data type in the
|
|
formal. Example: pointer to "Foo".
|
|
|
|
ppSymbol Returns a pointer to the start of the formal symbol.
|
|
Example: pointer to "f".
|
|
|
|
ppEqualSign Returns a pointer to the equal sign separating the
|
|
formal symbol from the initial value. If there is
|
|
no "=" then this will be NULL.
|
|
|
|
ppValue Returns a pointer to the initial value part of the
|
|
formal declaration. Example: pointer to "&farray[1,2]"
|
|
|
|
ppSeparator Returns a pointer to the character which terminated the
|
|
scan. This should be a pointer to a comma or a null
|
|
byte which terminates the string.
|
|
|
|
pNest Returns the nesting level when a separator was found.
|
|
This is non-zero for any kind of error. This is zero
|
|
for a successful parse of this portion of the formal
|
|
list.
|
|
|
|
*/
|
|
|
|
#ifdef __USE_PROTOS
|
|
char * endFormal(char *pStart,
|
|
char **ppDataType,
|
|
char **ppSymbol,
|
|
char **ppEqualSign,
|
|
char **ppValue,
|
|
char **ppSeparator,
|
|
int *pNest)
|
|
#else
|
|
char * endFormal(pStart,
|
|
ppDataType,
|
|
ppSymbol,
|
|
ppEqualSign,
|
|
ppValue,
|
|
ppSeparator,
|
|
pNest)
|
|
char *pStart;
|
|
char **ppDataType;
|
|
char **ppSymbol;
|
|
char **ppEqualSign;
|
|
char **ppValue;
|
|
char **ppSeparator;
|
|
int *pNest;
|
|
|
|
#endif
|
|
{
|
|
char *p = pStart;
|
|
char *q;
|
|
|
|
*ppDataType = NULL;
|
|
*ppSymbol = NULL;
|
|
*ppEqualSign = NULL;
|
|
*ppValue = NULL;
|
|
*ppSeparator = NULL;
|
|
|
|
*pNest = 0;
|
|
|
|
/* The first non-blank is the start of the datatype */
|
|
|
|
p = skipSpaces(p);
|
|
if (*p == 0) goto EXIT;
|
|
*ppDataType = p;
|
|
|
|
/* We are not looking for the symbol, we are looking
|
|
for the separator that follows the symbol. Then
|
|
we'll back up.
|
|
|
|
Search for the ',' or '=" or null terminator.
|
|
*/
|
|
|
|
p = skipToSeparatorOrEqualSign(p, pNest);
|
|
|
|
if (*pNest != 0) goto EXIT;
|
|
|
|
/*
|
|
Work backwards to find start of symbol
|
|
Skip spaces between the end of symbol and separator
|
|
Assume that there are no spaces in the formal, but
|
|
there is a space preceding the formal
|
|
*/
|
|
|
|
for (q = &p[-1]; q >= *ppDataType; q--) {
|
|
if (! isspace(*q)) break;
|
|
}
|
|
if (q < *ppDataType) goto EXIT;
|
|
|
|
/*
|
|
MR26 Handle things like: IIR_Bool (IIR_Decl::*constraint)()
|
|
Backup until we hit the end of a symbol string, then find the
|
|
start of the symbol string. This wont' work for functions
|
|
with prototypes, but works for the most common cases. For
|
|
others, use typedef names.
|
|
*/
|
|
|
|
/* MR26 */ for (q = q; q >= *ppDataType; q--) {
|
|
/* MR26 */ if (isalpha(*q) || isdigit(*q) || *q == '_' || *q == '$') break;
|
|
/* MR26 */ }
|
|
/* MR26 */ if (q < *ppDataType) goto EXIT;
|
|
|
|
for (q = q; q >= *ppDataType; q--) {
|
|
if ( ! (isalpha(*q) || isdigit(*q) || *q == '_' || *q == '$')) break;
|
|
}
|
|
|
|
*ppSymbol = &q[1];
|
|
|
|
if (*p == ',' || *p == 0) {
|
|
*ppSeparator = p;
|
|
goto EXIT;
|
|
}
|
|
|
|
*ppEqualSign = p;
|
|
p = skipSpaces(++p);
|
|
*ppValue = p;
|
|
if (*p == 0) goto EXIT;
|
|
|
|
|
|
while (*p != 0 && *pNest == 0 && *p != ',') {
|
|
p = skipToSeparator(p, pNest);
|
|
}
|
|
if (*pNest == 0) *ppSeparator = p;
|
|
|
|
EXIT:
|
|
if (*p == ',') p++;
|
|
return p;
|
|
}
|