/* first, recycle anything that's on the freelist */ if (nfa->freestates != NULL)
{
s = nfa->freestates;
nfa->freestates = s->next;
} /* otherwise, is there anything left in the last statebatch? */ elseif (nfa->lastsb != NULL && nfa->lastsbused < nfa->lastsb->nstates)
{
s = &nfa->lastsb->s[nfa->lastsbused++];
} /* otherwise, need to allocate a new statebatch */ else
{ struct statebatch *newSb;
size_t nstates;
/* *newfstate-allocateanNFAstatewithaspecifiedflagvalue
*/ staticstruct state * /* NULL on error */
newfstate(struct nfa *nfa, int flag)
{ struct state *s;
s = newstate(nfa); if (s != NULL)
s->flag = (char) flag; return s;
}
/* check for duplicate arc, using whichever chain is shorter */ if (from->nouts <= to->nins)
{ for (a = from->outs; a != NULL; a = a->outchain) if (a->to == to && a->co == co && a->type == t) return;
} else
{ for (a = to->ins; a != NULL; a = a->inchain) if (a->from == from && a->co == co && a->type == t) return;
}
/* no dup, so create the arc */
createarc(nfa, t, co, from, to);
}
/* *createarc-createanewarcwithinanNFA * *Thisfunctionmust*only*beusedafterverifyingthatthereisnoexisting *identicalarc(sametype/color/from/to).
*/ staticvoid
createarc(struct nfa *nfa, int t,
color co, struct state *from, struct state *to)
{ struct arc *a;
a = allocarc(nfa); if (NISERR()) return;
assert(a != NULL);
/* first, recycle anything that's on the freelist */ if (nfa->freearcs != NULL)
{
a = nfa->freearcs;
nfa->freearcs = a->freechain;
} /* otherwise, is there anything left in the last arcbatch? */ elseif (nfa->lastab != NULL && nfa->lastabused < nfa->lastab->narcs)
{
a = &nfa->lastab->a[nfa->lastabused++];
} /* otherwise, need to allocate a new arcbatch */ else
{ struct arcbatch *newAb;
size_t narcs;
/* *sortins-sorttheinarcsofastatebyfrom/color/type
*/ staticvoid
sortins(struct nfa *nfa, struct state *s)
{ struct arc **sortarray; struct arc *a; int n = s->nins; int i;
if (n <= 1) return; /* nothing to do */ /* make an array of arc pointers ... */
sortarray = (struct arc **) MALLOC(n * sizeof(struct arc *)); if (sortarray == NULL)
{
NERR(REG_ESPACE); return;
}
i = 0; for (a = s->ins; a != NULL; a = a->inchain)
sortarray[i++] = a;
assert(i == n); /* ... sort the array */
qsort(sortarray, n, sizeof(struct arc *), sortins_cmp); /* ... and rebuild arc list in order */ /* it seems worth special-casing first and last items to simplify loop */
a = sortarray[0];
s->ins = a;
a->inchain = sortarray[1];
a->inchainRev = NULL; for (i = 1; i < n - 1; i++)
{
a = sortarray[i];
a->inchain = sortarray[i + 1];
a->inchainRev = sortarray[i - 1];
}
a = sortarray[i];
a->inchain = NULL;
a->inchainRev = sortarray[i - 1];
FREE(sortarray);
}
/* we check the fields in the order they are most likely to be different */ if (aa->from->no < bb->from->no) return -1; if (aa->from->no > bb->from->no) return1; if (aa->co < bb->co) return -1; if (aa->co > bb->co) return1; if (aa->type < bb->type) return -1; if (aa->type > bb->type) return1; return0;
}
/* *sortouts-sorttheoutarcsofastatebyto/color/type
*/ staticvoid
sortouts(struct nfa *nfa, struct state *s)
{ struct arc **sortarray; struct arc *a; int n = s->nouts; int i;
if (n <= 1) return; /* nothing to do */ /* make an array of arc pointers ... */
sortarray = (struct arc **) MALLOC(n * sizeof(struct arc *)); if (sortarray == NULL)
{
NERR(REG_ESPACE); return;
}
i = 0; for (a = s->outs; a != NULL; a = a->outchain)
sortarray[i++] = a;
assert(i == n); /* ... sort the array */
qsort(sortarray, n, sizeof(struct arc *), sortouts_cmp); /* ... and rebuild arc list in order */ /* it seems worth special-casing first and last items to simplify loop */
a = sortarray[0];
s->outs = a;
a->outchain = sortarray[1];
a->outchainRev = NULL; for (i = 1; i < n - 1; i++)
{
a = sortarray[i];
a->outchain = sortarray[i + 1];
a->outchainRev = sortarray[i - 1];
}
a = sortarray[i];
a->outchain = NULL;
a->outchainRev = sortarray[i - 1];
FREE(sortarray);
}
/* we check the fields in the order they are most likely to be different */ if (aa->to->no < bb->to->no) return -1; if (aa->to->no > bb->to->no) return1; if (aa->co < bb->co) return -1; if (aa->co > bb->co) return1; if (aa->type < bb->type) return -1; if (aa->type > bb->type) return1; return0;
}
if (newState->nins == 0)
{ /* No need for de-duplication */ struct arc *a;
while ((a = oldState->ins) != NULL)
{
createarc(nfa, a->type, a->co, a->from, newState);
freearc(nfa, a);
}
} elseif (!BULK_ARC_OP_USE_SORT(oldState->nins, newState->nins))
{ /* With not too many arcs, just do them one at a time */ struct arc *a;
sortins(nfa, oldState);
sortins(nfa, newState); if (NISERR()) return; /* might have failed to sort */
oa = oldState->ins;
na = newState->ins; while (oa != NULL && na != NULL)
{ struct arc *a = oa;
switch (sortins_cmp(&oa, &na))
{ case -1: /* newState does not have anything matching oa */
oa = oa->inchain;
/* *Ratherthandoingcreatearc+freearc,wecanjustunlink *andrelinktheexistingarcstruct.
*/
changearctarget(a, newState); break; case0: /* match, advance in both lists */
oa = oa->inchain;
na = na->inchain; /* ... and drop duplicate arc from oldState */
freearc(nfa, a); break; case +1: /* advance only na; oa might have a match later */
na = na->inchain; break; default:
assert(NOTREACHED);
}
} while (oa != NULL)
{ /* newState does not have anything matching oa */ struct arc *a = oa;
oa = oa->inchain;
changearctarget(a, newState);
}
}
/* *copyins-copyinarcsofastatetoanotherstate * *Thecommentsformoveins()applyhereaswell.However,incurrent *usage,thisis*only*calledwithbrand-newtargetstates,sothat *onlythe"noneedforde-duplication"codepathiseverreached. *Wekeeptherest#ifdef'doutincaseit'sneededinthefuture.
*/ staticvoid
copyins(struct nfa *nfa, struct state *oldState, struct state *newState)
{
assert(oldState != newState);
assert(newState->nins == 0); /* see comment above */
if (newState->nins == 0)
{ /* No need for de-duplication */ struct arc *a;
for (a = oldState->ins; a != NULL; a = a->inchain)
createarc(nfa, a->type, a->co, a->from, newState);
} #ifdef NOT_USED /* see comment above */ elseif (!BULK_ARC_OP_USE_SORT(oldState->nins, newState->nins))
{ /* With not too many arcs, just do them one at a time */ struct arc *a;
for (a = oldState->ins; a != NULL; a = a->inchain)
cparc(nfa, a, a->from, newState);
} else
{ /* *Withmanyarcs,useasort-mergeapproach.Notethatcreatearc() *willputnewarcsontothefrontofnewState'schain,soitdoes *notbreakourwalkthroughthesortedpartofthechain.
*/ struct arc *oa; struct arc *na;
sortins(nfa, oldState);
sortins(nfa, newState); if (NISERR()) return; /* might have failed to sort */
oa = oldState->ins;
na = newState->ins; while (oa != NULL && na != NULL)
{ struct arc *a = oa;
switch (sortins_cmp(&oa, &na))
{ case -1: /* newState does not have anything matching oa */
oa = oa->inchain;
createarc(nfa, a->type, a->co, a->from, newState); break; case0: /* match, advance in both lists */
oa = oa->inchain;
na = na->inchain; break; case +1: /* advance only na; oa might have a match later */
na = na->inchain; break; default:
assert(NOTREACHED);
}
} while (oa != NULL)
{ /* newState does not have anything matching oa */ struct arc *a = oa;
/* *Nowmergeintos'inchain.Notethatcreatearc()willputnewarcs *ontothefrontofs'schain,soitdoesnotbreakourwalkthroughthe *sortedpartofthechain.
*/
i = 0;
na = s->ins; while (i < arccount && na != NULL)
{ struct arc *a = arcarray[i];
switch (sortins_cmp(&a, &na))
{ case -1: /* s does not have anything matching a */
createarc(nfa, a->type, a->co, a->from, s);
i++; break; case0: /* match, advance in both lists */
i++;
na = na->inchain; break; case +1: /* advance only na; array might have a match later */
na = na->inchain; break; default:
assert(NOTREACHED);
}
} while (i < arccount)
{ /* s does not have anything matching a */ struct arc *a = arcarray[i];
/* *moveouts-movealloutarcsofastatetoanotherstate * *Seecommentsformoveins()
*/ staticvoid
moveouts(struct nfa *nfa, struct state *oldState, struct state *newState)
{
assert(oldState != newState);
if (newState->nouts == 0)
{ /* No need for de-duplication */ struct arc *a;
while ((a = oldState->outs) != NULL)
{
createarc(nfa, a->type, a->co, newState, a->to);
freearc(nfa, a);
}
} elseif (!BULK_ARC_OP_USE_SORT(oldState->nouts, newState->nouts))
{ /* With not too many arcs, just do them one at a time */ struct arc *a;
sortouts(nfa, oldState);
sortouts(nfa, newState); if (NISERR()) return; /* might have failed to sort */
oa = oldState->outs;
na = newState->outs; while (oa != NULL && na != NULL)
{ struct arc *a = oa;
switch (sortouts_cmp(&oa, &na))
{ case -1: /* newState does not have anything matching oa */
oa = oa->outchain;
/* *Ratherthandoingcreatearc+freearc,wecanjustunlink *andrelinktheexistingarcstruct.
*/
changearcsource(a, newState); break; case0: /* match, advance in both lists */
oa = oa->outchain;
na = na->outchain; /* ... and drop duplicate arc from oldState */
freearc(nfa, a); break; case +1: /* advance only na; oa might have a match later */
na = na->outchain; break; default:
assert(NOTREACHED);
}
} while (oa != NULL)
{ /* newState does not have anything matching oa */ struct arc *a = oa;
oa = oa->outchain;
changearcsource(a, newState);
}
}
/* *copyouts-copyoutarcsofastatetoanotherstate * *Seecommentsforcopyins()
*/ staticvoid
copyouts(struct nfa *nfa, struct state *oldState, struct state *newState)
{
assert(oldState != newState);
assert(newState->nouts == 0); /* see comment above */
if (newState->nouts == 0)
{ /* No need for de-duplication */ struct arc *a;
for (a = oldState->outs; a != NULL; a = a->outchain)
createarc(nfa, a->type, a->co, newState, a->to);
} #ifdef NOT_USED /* see comment above */ elseif (!BULK_ARC_OP_USE_SORT(oldState->nouts, newState->nouts))
{ /* With not too many arcs, just do them one at a time */ struct arc *a;
for (a = oldState->outs; a != NULL; a = a->outchain)
cparc(nfa, a, newState, a->to);
} else
{ /* *Withmanyarcs,useasort-mergeapproach.Notethatcreatearc() *willputnewarcsontothefrontofnewState'schain,soitdoes *notbreakourwalkthroughthesortedpartofthechain.
*/ struct arc *oa; struct arc *na;
sortouts(nfa, oldState);
sortouts(nfa, newState); if (NISERR()) return; /* might have failed to sort */
oa = oldState->outs;
na = newState->outs; while (oa != NULL && na != NULL)
{ struct arc *a = oa;
switch (sortouts_cmp(&oa, &na))
{ case -1: /* newState does not have anything matching oa */
oa = oa->outchain;
createarc(nfa, a->type, a->co, newState, a->to); break; case0: /* match, advance in both lists */
oa = oa->outchain;
na = na->outchain; break; case +1: /* advance only na; oa might have a match later */
na = na->outchain; break; default:
assert(NOTREACHED);
}
} while (oa != NULL)
{ /* newState does not have anything matching oa */ struct arc *a = oa;
/* *cloneouts-copyoutarcsofastatetoanotherstatepair,modifyingtype * *ThisisonlyusedtoconvertPLAINarcstoAHEAD/BEHINDarcs,whichshare *thesameinterpretationof"co".Itwouldn'tbesensiblewithLACONs.
*/ staticvoid
cloneouts(struct nfa *nfa, struct state *old, struct state *from, struct state *to, int type)
{ struct arc *a;
assert(old != from);
assert(type == AHEAD || type == BEHIND);
for (a = old->outs; a != NULL; a = a->outchain)
{
assert(a->type == PLAIN);
newarc(nfa, type, a->co, from, to);
}
}
/* *delsub-deleteasub-NFA,updatingsubrepointersifnecessary * *Thisusesarecursivetraversalofthesub-NFA,markingalready-seen *statesusingtheirtmppointer.
*/ staticvoid
delsub(struct nfa *nfa, struct state *lp, /* the sub-NFA goes from here... */ struct state *rp) /* ...to here, *not* inclusive */
{
assert(lp != rp);
rp->tmp = rp; /* mark end */
deltraverse(nfa, lp, lp); if (NISERR()) return; /* asserts might not hold after failure */
assert(lp->nouts == 0 && rp->nins == 0); /* did the job */
assert(lp->no != FREESTATE && rp->no != FREESTATE); /* no more */
rp->tmp = NULL; /* unmark end */
lp->tmp = NULL; /* and begin, marked by deltraverse */
}
/* *deltraverse-therecursiveheartofdelsub *Thisroutine'sbasicjobistodestroyallout-arcsofthestate.
*/ staticvoid
deltraverse(struct nfa *nfa, struct state *leftend, struct state *s)
{ struct arc *a; struct state *to;
/* Since this is recursive, it could be driven to stack overflow */ if (STACK_TOO_DEEP(nfa->v->re))
{
NERR(REG_ETOOBIG); return;
}
if (s->nouts == 0) return; /* nothing to do */ if (s->tmp != NULL) return; /* already in progress */
s->tmp = s; /* mark as in progress */
while ((a = s->outs) != NULL)
{
to = a->to;
deltraverse(nfa, leftend, to); if (NISERR()) return; /* asserts might not hold after failure */
assert(to->nouts == 0 || to->tmp != NULL);
freearc(nfa, a); if (to->nins == 0 && to->tmp == NULL)
{
assert(to->nouts == 0);
freestate(nfa, to);
}
}
assert(s->no != FREESTATE); /* we're still here */
assert(s == leftend || s->nins != 0); /* and still reachable */
assert(s->nouts == 0); /* but have no outarcs */
s->tmp = NULL; /* we're done here */
}
/* *dupnfa-duplicatesub-NFA * *Anotherrecursivetraversal,thistimeusingtmptopointtoduplicates *aswellasmarkalready-seenstates.(Youknewtherewasareasonwhy *it'sastatepointer,didn'tyou?:-))
*/ staticvoid
dupnfa(struct nfa *nfa, struct state *start, /* duplicate of subNFA starting here */ struct state *stop, /* and stopping here */ struct state *from, /* stringing duplicate from here */ struct state *to) /* to here */
{ if (start == stop)
{
newarc(nfa, EMPTY, 0, from, to); return;
}
stop->tmp = to;
duptraverse(nfa, start, from); /* done, except for clearing out the tmp pointers */
stop->tmp = NULL;
cleartraverse(nfa, start);
}
/* *duptraverse-recursiveheartofdupnfa
*/ staticvoid
duptraverse(struct nfa *nfa, struct state *s, struct state *stmp) /* s's duplicate, or NULL */
{ struct arc *a;
/* Since this is recursive, it could be driven to stack overflow */ if (STACK_TOO_DEEP(nfa->v->re))
{
NERR(REG_ETOOBIG); return;
}
for (a = s->outs; a != NULL && !NISERR(); a = a->outchain)
{
duptraverse(nfa, a->to, (struct state *) NULL); if (NISERR()) break;
assert(a->to->tmp != NULL);
cparc(nfa, a, s->tmp, a->to->tmp);
}
}
/* *removeconstraints-removeanyconstraintsinanNFA * *Constraintarcsarereplacedbyemptyarcs,essentiallytreatingall *constraintsasautomaticallysatisfied.
*/ staticvoid
removeconstraints(struct nfa *nfa, struct state *start, /* process subNFA starting here */ struct state *stop) /* and stopping here */
{ if (start == stop) return;
stop->tmp = stop;
removetraverse(nfa, start); /* done, except for clearing out the tmp pointers */
/* Since this is recursive, it could be driven to stack overflow */ if (STACK_TOO_DEEP(nfa->v->re))
{
NERR(REG_ETOOBIG); return;
}
if (s->tmp != NULL) return; /* already done */
s->tmp = s; for (a = s->outs; a != NULL && !NISERR(); a = oa)
{
removetraverse(nfa, a->to); if (NISERR()) break;
oa = a->outchain; switch (a->type)
{ case PLAIN: case EMPTY: case CANTMATCH: /* nothing to do */ break; case AHEAD: case BEHIND: case'^': case'$': case LACON: /* replace it */
newarc(nfa, EMPTY, 0, s, a->to);
freearc(nfa, a); break; default:
NERR(REG_ASSERT); break;
}
}
}
/* Since this is recursive, it could be driven to stack overflow */ if (STACK_TOO_DEEP(nfa->v->re))
{
NERR(REG_ETOOBIG); return;
}
if (s->tmp == NULL) return;
s->tmp = NULL;
for (a = s->outs; a != NULL; a = a->outchain)
cleartraverse(nfa, a->to);
}
/* *single_color_transition-doesgettingfroms1tos2crossonePLAINarc? * *Iftraversingfroms1tos2requiresasinglePLAINmatch(possiblyofany *ofasetofcolors),returnastatewhoseoutarclistcontainsonlyPLAIN *arcsofthosecolor(s).OtherwisereturnNULL. * *ThisisusedbeforeoptimizingtheNFA,sotheremaybeEMPTYarcs,which *weshouldignore;thepossibilityofanEMPTYiswhytheresultstatecould *bedifferentfroms1. * *It'sworthtroublingtohandlemultipleparallelPLAINarcsherebecausea *bracketconstructsuchas[abc]mightyieldeitheroneorseveralparallel *PLAINarcsdependingonearlieratomsintheexpression.We'dratherthat *thatimplementationdetailnotcreateuser-visibleperformancedifferences.
*/ staticstruct state *
single_color_transition(struct state *s1, struct state *s2)
{ struct arc *a;
/* Ignore leading EMPTY arc, if any */ if (s1->nouts == 1 && s1->outs->type == EMPTY)
s1 = s1->outs->to; /* Likewise for any trailing EMPTY arc */ if (s2->nins == 1 && s2->ins->type == EMPTY)
s2 = s2->ins->from; /* Perhaps we could have a single-state loop in between, if so reject */ if (s1 == s2) return NULL; /* s1 must have at least one outarc... */ if (s1->outs == NULL) return NULL; /* ... and they must all be PLAIN arcs to s2 */ for (a = s1->outs; a != NULL; a = a->outchain)
{ if (a->type != PLAIN || a->to != s2) return NULL;
} /* OK, return s1 as the possessor of the relevant outarcs */ return s1;
}
if (verbose)
fprintf(f, "\ninitial cleanup:\n"); #endif /* If we have any CANTMATCH arcs, drop them; but this is uncommon */ if (nfa->flags & HASCANTMATCH)
{
removecantmatch(nfa);
nfa->flags &= ~HASCANTMATCH;
}
cleanup(nfa); /* may simplify situation */ #ifdef REG_DEBUG if (verbose)
dumpnfa(nfa, f); if (verbose)
fprintf(f, "\nempties:\n"); #endif
fixempties(nfa, f); /* get rid of EMPTY arcs */ #ifdef REG_DEBUG if (verbose)
fprintf(f, "\nconstraints:\n"); #endif
fixconstraintloops(nfa, f); /* get rid of constraint loops */
pullback(nfa, f); /* pull back constraints backward */
pushfwd(nfa, f); /* push fwd constraints forward */ #ifdef REG_DEBUG if (verbose)
fprintf(f, "\nfinal cleanup:\n"); #endif
cleanup(nfa); /* final tidying */ #ifdef REG_DEBUG if (verbose)
dumpnfa(nfa, f); #endif return analyze(nfa); /* and analysis */
}
/* *pullback-pullbackconstraintsbackwardtoeliminatethem
*/ staticvoid
pullback(struct nfa *nfa,
FILE *f) /* for debug output; NULL none */
{ struct state *s; struct state *nexts; struct arc *a; struct arc *nexta; struct state *intermediates; int progress;
/* find and pull until there are no more */ do
{
progress = 0; for (s = nfa->states; s != NULL && !NISERR(); s = nexts)
{
nexts = s->next;
intermediates = NULL; for (a = s->outs; a != NULL && !NISERR(); a = nexta)
{
nexta = a->outchain; if (a->type == '^' || a->type == BEHIND) if (pull(nfa, a, &intermediates))
progress = 1;
} /* clear tmp fields of intermediate states created here */ while (intermediates != NULL)
{ struct state *ns = intermediates->tmp;
intermediates->tmp = NULL;
intermediates = ns;
} /* if s is now useless, get rid of it */ if ((s->nins == 0 || s->nouts == 0) && !s->flag)
dropstate(nfa, s);
} if (progress && f != NULL)
dumpnfa(nfa, f);
} while (progress && !NISERR()); if (NISERR()) return;
/* *Any^constraintswewereabletopulltothestartstatecannowbe *replacedbyPLAINarcsreferencingtheBOSorBOLcolors.Thereshould *benoother^orBEHINDarcsleftintheNFA,thoughwedonotcheck *thathere(compact()willfailifso).
*/ for (a = nfa->pre->outs; a != NULL; a = nexta)
{
nexta = a->outchain; if (a->type == '^')
{
assert(a->co == 0 || a->co == 1);
newarc(nfa, PLAIN, nfa->bos[a->co], a->from, a->to);
freearc(nfa, a);
}
}
}
assert(from != to); /* should have gotten rid of this earlier */ if (from->flag) /* can't pull back beyond start */ return0; if (from->nins == 0)
{ /* unreachable */
freearc(nfa, con); return1;
}
/* *First,clonefromstateifnecessarytoavoidotheroutarcs.Thismay *seemwasteful,butitsimplifiesthelogic,andwe'llgetridofthe *clonestateagainatthebottom.
*/ if (from->nouts > 1)
{
s = newstate(nfa); if (NISERR()) return0;
copyins(nfa, from, s); /* duplicate inarcs */
cparc(nfa, con, s, to); /* move constraint arc */
freearc(nfa, con); if (NISERR()) return0;
from = s;
con = from->outs;
}
assert(from->nouts == 1);
/* propagate the constraint into the from state's inarcs */ for (a = from->ins; a != NULL && !NISERR(); a = nexta)
{
nexta = a->inchain; switch (combine(nfa, con, a))
{ case INCOMPATIBLE: /* destroy the arc */
freearc(nfa, a); break; case SATISFIED: /* no action needed */ break; case COMPATIBLE: /* swap the two arcs, more or less */ /* need an intermediate state, but might have one already */ for (s = *intermediates; s != NULL; s = s->tmp)
{
assert(s->nins > 0 && s->nouts > 0); if (s->ins->from == a->from && s->outs->to == to) break;
} if (s == NULL)
{
s = newstate(nfa); if (NISERR()) return0;
s->tmp = *intermediates;
*intermediates = s;
}
cparc(nfa, con, a->from, s);
cparc(nfa, a, s, to);
freearc(nfa, a); break; case REPLACEARC: /* replace arc's color */
newarc(nfa, a->type, con->co, a->from, to);
freearc(nfa, a); break; default:
assert(NOTREACHED); break;
}
}
/* remaining inarcs, if any, incorporate the constraint */
moveins(nfa, from, to);
freearc(nfa, con); /* from state is now useless, but we leave it to pullback() to clean up */ return1;
}
/* *pushfwd-pushforwardconstraintsforwardtoeliminatethem
*/ staticvoid
pushfwd(struct nfa *nfa,
FILE *f) /* for debug output; NULL none */
{ struct state *s; struct state *nexts; struct arc *a; struct arc *nexta; struct state *intermediates; int progress;
/* find and push until there are no more */ do
{
progress = 0; for (s = nfa->states; s != NULL && !NISERR(); s = nexts)
{
nexts = s->next;
intermediates = NULL; for (a = s->ins; a != NULL && !NISERR(); a = nexta)
{
nexta = a->inchain; if (a->type == '$' || a->type == AHEAD) if (push(nfa, a, &intermediates))
progress = 1;
} /* clear tmp fields of intermediate states created here */ while (intermediates != NULL)
{ struct state *ns = intermediates->tmp;
intermediates->tmp = NULL;
intermediates = ns;
} /* if s is now useless, get rid of it */ if ((s->nins == 0 || s->nouts == 0) && !s->flag)
dropstate(nfa, s);
} if (progress && f != NULL)
dumpnfa(nfa, f);
} while (progress && !NISERR()); if (NISERR()) return;
/* *Any$constraintswewereabletopushtothepoststatecannowbe *replacedbyPLAINarcsreferencingtheEOSorEOLcolors.Thereshould *benoother$orAHEADarcsleftintheNFA,thoughwedonotcheck *thathere(compact()willfailifso).
*/ for (a = nfa->post->ins; a != NULL; a = nexta)
{
nexta = a->inchain; if (a->type == '$')
{
assert(a->co == 0 || a->co == 1);
newarc(nfa, PLAIN, nfa->eos[a->co], a->from, a->to);
freearc(nfa, a);
}
}
}
assert(to != from); /* should have gotten rid of this earlier */ if (to->flag) /* can't push forward beyond end */ return0; if (to->nouts == 0)
{ /* dead end */
freearc(nfa, con); return1;
}
/* *First,clonetostateifnecessarytoavoidotherinarcs.Thismay *seemwasteful,butitsimplifiesthelogic,andwe'llgetridofthe *clonestateagainatthebottom.
*/ if (to->nins > 1)
{
s = newstate(nfa); if (NISERR()) return0;
copyouts(nfa, to, s); /* duplicate outarcs */
cparc(nfa, con, from, s); /* move constraint arc */
freearc(nfa, con); if (NISERR()) return0;
to = s;
con = to->ins;
}
assert(to->nins == 1);
/* propagate the constraint into the to state's outarcs */ for (a = to->outs; a != NULL && !NISERR(); a = nexta)
{
nexta = a->outchain; switch (combine(nfa, con, a))
{ case INCOMPATIBLE: /* destroy the arc */
freearc(nfa, a); break; case SATISFIED: /* no action needed */ break; case COMPATIBLE: /* swap the two arcs, more or less */ /* need an intermediate state, but might have one already */ for (s = *intermediates; s != NULL; s = s->tmp)
{
assert(s->nins > 0 && s->nouts > 0); if (s->ins->from == from && s->outs->to == a->to) break;
} if (s == NULL)
{
s = newstate(nfa); if (NISERR()) return0;
s->tmp = *intermediates;
*intermediates = s;
}
cparc(nfa, con, s, a->to);
cparc(nfa, a, from, s);
freearc(nfa, a); break; case REPLACEARC: /* replace arc's color */
newarc(nfa, a->type, con->co, from, a->to);
freearc(nfa, a); break; default:
assert(NOTREACHED); break;
}
}
/* remaining outarcs, if any, incorporate the constraint */
moveouts(nfa, to, from);
freearc(nfa, con); /* to state is now useless, but we leave it to pushfwd() to clean up */ return1;
}
/* *combine-constraintlandsonanarc,whathappens? * *#defINCOMPATIBLE1// destroys arc *#defSATISFIED2// constraint satisfied *#defCOMPATIBLE3// compatible but not satisfied yet *#defREPLACEARC4// replace arc's color with constraint color
*/ staticint
combine(struct nfa *nfa, struct arc *con, struct arc *a)
{ #define CA(ct,at) (((ct)<<CHAR_BIT) | (at))
switch (CA(con->type, a->type))
{ case CA('^', PLAIN): /* newlines are handled separately */ case CA('$', PLAIN): return INCOMPATIBLE; break; case CA(AHEAD, PLAIN): /* color constraints meet colors */ case CA(BEHIND, PLAIN): if (con->co == a->co) return SATISFIED; if (con->co == RAINBOW)
{ /* con is satisfied unless arc's color is a pseudocolor */ if (!(nfa->cm->cd[a->co].flags & PSEUDO)) return SATISFIED;
} elseif (a->co == RAINBOW)
{ /* con is incompatible if it's for a pseudocolor */ /* (this is hypothetical; we make no such constraints today) */ if (nfa->cm->cd[con->co].flags & PSEUDO) return INCOMPATIBLE; /* otherwise, constraint constrains arc to be only its color */ return REPLACEARC;
} return INCOMPATIBLE; break; case CA('^', '^'): /* collision, similar constraints */ case CA('$', '$'): if (con->co == a->co) /* true duplication */ return SATISFIED; return INCOMPATIBLE; break; case CA(AHEAD, AHEAD): /* collision, similar constraints */ case CA(BEHIND, BEHIND): if (con->co == a->co) /* true duplication */ return SATISFIED; if (con->co == RAINBOW)
{ /* con is satisfied unless arc's color is a pseudocolor */ if (!(nfa->cm->cd[a->co].flags & PSEUDO)) return SATISFIED;
} elseif (a->co == RAINBOW)
{ /* con is incompatible if it's for a pseudocolor */ /* (this is hypothetical; we make no such constraints today) */ if (nfa->cm->cd[con->co].flags & PSEUDO) return INCOMPATIBLE; /* otherwise, constraint constrains arc to be only its color */ return REPLACEARC;
} return INCOMPATIBLE; break; case CA('^', BEHIND): /* collision, dissimilar constraints */ case CA(BEHIND, '^'): case CA('$', AHEAD): case CA(AHEAD, '$'): return INCOMPATIBLE; break; case CA('^', '$'): /* constraints passing each other */ case CA('^', AHEAD): case CA(BEHIND, '$'): case CA(BEHIND, AHEAD): case CA('$', '^'): case CA('$', BEHIND): case CA(AHEAD, '^'): case CA(AHEAD, BEHIND): case CA('^', LACON): case CA(BEHIND, LACON): case CA('$', LACON): case CA(AHEAD, LACON): return COMPATIBLE; break;
}
assert(NOTREACHED); return INCOMPATIBLE; /* for benefit of blind compilers */
}
/* *fixempties-getridofEMPTYarcs
*/ staticvoid
fixempties(struct nfa *nfa,
FILE *f) /* for debug output; NULL none */
{ struct state *s; struct state *s2; struct state *nexts; struct arc *a; struct arc *nexta; int totalinarcs; struct arc **inarcsorig; struct arc **arcarray; int arccount; int prevnins; int nskip;
/* *First,getridofanystateswhosesoleout-arcisanEMPTY,since *they'rebasicallyjustaliasesfortheirsuccessor.Theparsing *algorithmcreatesenoughofthesethatit'sworthspecial-casingthis.
*/ for (s = nfa->states; s != NULL && !NISERR(); s = nexts)
{
nexts = s->next; if (s->flag || s->nouts != 1) continue;
a = s->outs;
assert(a != NULL && a->outchain == NULL); if (a->type != EMPTY) continue; if (s != a->to)
moveins(nfa, s, a->to);
dropstate(nfa, s);
}
/* *Similarly,getridofanystatewithasingleEMPTYin-arc,byfolding *itintoitspredecessor.
*/ for (s = nfa->states; s != NULL && !NISERR(); s = nexts)
{
nexts = s->next; /* while we're at it, ensure tmp fields are clear for next step */
assert(s->tmp == NULL); if (s->flag || s->nins != 1) continue;
a = s->ins;
assert(a != NULL && a->inchain == NULL); if (a->type != EMPTY) continue; if (s != a->from)
moveouts(nfa, s, a->from);
dropstate(nfa, s);
}
/* Remember the states' first original inarcs */ /* ... and while at it, count how many old inarcs there are altogether */
inarcsorig = (struct arc **) MALLOC(nfa->nstates * sizeof(struct arc *)); if (inarcsorig == NULL)
{
NERR(REG_ESPACE); return;
}
totalinarcs = 0; for (s = nfa->states; s != NULL; s = s->next)
{
inarcsorig[s->no] = s->ins;
totalinarcs += s->nins;
}
/* And iterate over the target states */ for (s = nfa->states; s != NULL && !NISERR(); s = s->next)
{ /* Ignore target states without non-EMPTY outarcs, per note above */ if (!s->flag && !hasnonemptyout(s)) continue;
/* Find predecessor states and accumulate their original inarcs */
arccount = 0; for (s2 = emptyreachable(nfa, s, s, inarcsorig); s2 != s; s2 = nexts)
{ /* Add s2's original inarcs to arcarray[], but ignore empties */ for (a = inarcsorig[s2->no]; a != NULL; a = a->inchain)
{ if (a->type != EMPTY)
arcarray[arccount++] = a;
}
/* Reset the tmp fields as we walk back */
nexts = s2->tmp;
s2->tmp = NULL;
}
s->tmp = NULL;
assert(arccount <= totalinarcs);
/* Remember how many original inarcs this state has */
prevnins = s->nins;
/* Add non-duplicate inarcs to target state */
mergeins(nfa, s, arcarray, arccount);
/* Now we must update the state's inarcsorig pointer */
nskip = s->nins - prevnins;
a = s->ins; while (nskip-- > 0)
a = a->inchain;
inarcsorig[s->no] = a;
}
FREE(arcarray);
FREE(inarcsorig);
if (NISERR()) return;
/* *NowremovealltheEMPTYarcs,sincewedon'tneedthemanymore.
*/ for (s = nfa->states; s != NULL; s = s->next)
{ for (a = s->outs; a != NULL; a = nexta)
{
nexta = a->outchain; if (a->type == EMPTY)
freearc(nfa, a);
}
}
/* *Andremoveanystatesthathavebecomeuseless.(Thiscleanupisnot *verythorough,andwouldbeevenlesssoifwetriedtocombineitwith *thepreviousstep;butcleanup()willtakecareofanythingwemiss.)
*/ for (s = nfa->states; s != NULL; s = nexts)
{
nexts = s->next; if ((s->nins == 0 || s->nouts == 0) && !s->flag)
dropstate(nfa, s);
}
for (a = s->outs; a != NULL; a = a->outchain)
{ if (isconstraintarc(a)) return1;
} return0;
}
/* *fixconstraintloops-getridofloopscontainingonlyconstraintarcs * *Aloopofstatesthatcontainsonlyconstraintarcsisuseless,since *passingaroundthelooprepresentsnoforwardprogress.Moreover,it *wouldcauseinfiniteloopinginpullback/pushfwd,soweneedtogetrid *ofsuchloopsbeforedoingthat.
*/ staticvoid
fixconstraintloops(struct nfa *nfa,
FILE *f) /* for debug output; NULL none */
{ struct state *s; struct state *nexts; struct arc *a; struct arc *nexta; int hasconstraints;
/* *Inthetrivialcaseofastatethatloopstoitself,wecanjustdrop *theconstraintarcaltogether.Thisisworthspecial-casingbecause *suchloopsarefarmorecommonthanloopscontainingmultiplestates. *Whilewe'reatit,notewhetheranyconstraintarcssurvive.
*/
hasconstraints = 0; for (s = nfa->states; s != NULL && !NISERR(); s = nexts)
{
nexts = s->next; /* while we're at it, ensure tmp fields are clear for next step */
assert(s->tmp == NULL); for (a = s->outs; a != NULL && !NISERR(); a = nexta)
{
nexta = a->outchain; if (isconstraintarc(a))
{ if (a->to == s)
freearc(nfa, a); else
hasconstraints = 1;
}
} /* If we removed all the outarcs, the state is useless. */ if (s->nouts == 0 && !s->flag)
dropstate(nfa, s);
}
/* Nothing to do if no remaining constraint arcs */ if (NISERR() || !hasconstraints) return;
/* *StartingfromeachremainingNFAstate,searchoutwardsfora *constraintloop.Ifwefindaloop,breaktheloop,thenstartthe *searchover.(Wecouldpossiblyretainsomestatefromthefirstscan, *butitwouldcomplicatethingsgreatly,andmulti-stateconstraint *loopsarerareenoughthatit'snotworthoptimizingthecase.)
*/
restart: for (s = nfa->states; s != NULL && !NISERR(); s = s->next)
{ if (findconstraintloop(nfa, s)) goto restart;
}
if (NISERR()) return;
/* *Nowremoveanystatesthathavebecomeuseless.(Thiscleanupisnot *verythorough,andwouldbeevenlesssoifwetriedtocombineitwith *thepreviousstep;butcleanup()willtakecareofanythingwemiss.) * *Becausefindconstraintloopintentionallydoesn'tresetalltmpfields, *wehavetoclearthemafterit'sdone.Thisisaconvenientplaceto *dothat,too.
*/ for (s = nfa->states; s != NULL; s = nexts)
{
nexts = s->next;
s->tmp = NULL; if ((s->nins == 0 || s->nouts == 0) && !s->flag)
dropstate(nfa, s);
}
/* Since this is recursive, it could be driven to stack overflow */ if (STACK_TOO_DEEP(nfa->v->re))
{
NERR(REG_ETOOBIG); return1; /* to exit as quickly as possible */
}
if (s->tmp != NULL)
{ /* Already proven uninteresting? */ if (s->tmp == s) return0; /* Found a loop involving s */
breakconstraintloop(nfa, s); /* The tmp fields have been cleaned up by breakconstraintloop */ return1;
} for (a = s->outs; a != NULL; a = a->outchain)
{ if (isconstraintarc(a))
{ struct state *sto = a->to;
/* *Startbyidentifyingwhichloopstepwewanttobreakat. *Preferentiallythisisonewithonlyoneconstraintarc.(XXXare *thereanyothersecondaryheuristicswewanttousehere?)Setrefarc *topointtotheselectedloneconstraintarc,ifthereisone.
*/
refarc = NULL;
s = sinitial; do
{
nexts = s->tmp;
assert(nexts != s); /* should not see any one-element loops */ if (refarc == NULL)
{ int narcs = 0;
for (a = s->outs; a != NULL; a = a->outchain)
{ if (a->to == nexts && isconstraintarc(a))
{
refarc = a;
narcs++;
}
}
assert(narcs > 0); if (narcs > 1)
refarc = NULL; /* multiple constraint arcs here, no good */
}
s = nexts;
} while (s != sinitial);
if (refarc)
{ /* break at the refarc */
shead = refarc->from;
stail = refarc->to;
assert(stail == shead->tmp);
} else
{ /* for lack of a better idea, break after sinitial */
shead = sinitial;
stail = sinitial->tmp;
}
/* *Resetthetmpfieldssothatwecanusethemforlocalstoragein *clonesuccessorstates.(findconstraintloopwon'tmind,sinceit'sjust *goingtoabandonitssearchanyway.)
*/ for (s = nfa->states; s != NULL; s = s->next)
s->tmp = NULL;
/* *Moveshead'sconstraint-looparcstopointtosclone,orjustdropthem *ifwediscoveredwedon'tneedsclone.
*/ for (a = shead->outs; a != NULL; a = nexta)
{
nexta = a->outchain; if (a->to == stail && isconstraintarc(a))
{ if (sclone)
cparc(nfa, a, shead, sclone);
freearc(nfa, a); if (NISERR()) break;
}
}
}
/* Since this is recursive, it could be driven to stack overflow */ if (STACK_TOO_DEEP(nfa->v->re))
{
NERR(REG_ETOOBIG); return;
}
/* If this state hasn't already got a donemap, create one */
donemap = curdonemap; if (donemap == NULL)
{
donemap = (char *) MALLOC(nstates * sizeof(char)); if (donemap == NULL)
{
NERR(REG_ESPACE); return;
}
canmerge = 0; for (s = sclone; s->ins; s = s->ins->from)
{ if (s->nins == 1 &&
a->type == s->ins->type && a->co == s->ins->co)
{
canmerge = 1; break;
}
}
}
if (canmerge)
{ /* *Wecanmergeintosclone.Ifwepreviouslymadeachild *clonestate,dropit;there'snoneedtovisitit.(This *canhappenifssourcehasmultiplepathwaystosto,andwe *onlyjustnowfoundonethatisprovablyano-op.)
*/ if (prevclone)
dropstate(nfa, prevclone); /* kills our outarc, too */
/* Recurse to merge sto's outarcs into sclone */
clonesuccessorstates(nfa,
sto,
sclone,
spredecessor,
refarc,
donemap,
outerdonemap,
nstates); /* sto should now be marked as previously visited */
assert(NISERR() || donemap[sto->no] == 1);
} elseif (prevclone)
{ /* *Wealreadyhaveaclonestateforthissuccessor,sojust *makeanotherarctoit.
*/
cparc(nfa, a, sclone, prevclone);
} else
{ /* *Weneedtocreateanewsuccessorclonestate.
*/ struct state *stoclone;
stoclone = newstate(nfa); if (stoclone == NULL)
{
assert(NISERR()); break;
} /* Mark it as to what it's a clone of */
stoclone->tmp = sto; /* ... and add the outarc leading to it */
cparc(nfa, a, sclone, stoclone);
}
} else
{ /* *Non-constraintoutarcsjustgetcopiedtosclone,asdooutarcs *leadingtostateswithnoconstraintoutarc.
*/
cparc(nfa, a, sclone, sto);
}
}
/* *Ifweareatouterlevelforthisclonestate,recursetoallitschild *clonestates,clearingtheirtmpfieldsaswego.(Ifwe'renot *outermostforsclone,leavethistobedonebytheoutercalllevel.) *Notethatifwehavemultipleoutarcsleadingtothesameclonestate, *itwillonlyberecursed-toonce.
*/ if (curdonemap == NULL)
{ for (a = sclone->outs; a != NULL && !NISERR(); a = a->outchain)
{ struct state *stoclone = a->to; struct state *sto = stoclone->tmp;
for (s = nfa->states; s != NULL; s = s->next)
{ struct arc *a; struct arc *nexta;
for (a = s->outs; a != NULL; a = nexta)
{
nexta = a->outchain; if (a->type == CANTMATCH)
{
freearc(nfa, a); if (NISERR()) return;
}
}
}
}
/* *cleanup-cleanupNFAafteroptimizations
*/ staticvoid
cleanup(struct nfa *nfa)
{ struct state *s; struct state *nexts; int n;
if (NISERR()) return;
/* clear out unreachable or dead-end states */ /* use pre to mark reachable, then post to mark can-reach-post */
markreachable(nfa, nfa->pre, (struct state *) NULL, nfa->pre);
markcanreach(nfa, nfa->post, nfa->pre, nfa->post); for (s = nfa->states; s != NULL && !NISERR(); s = nexts)
{
nexts = s->next; if (s->tmp != nfa->post && !s->flag)
dropstate(nfa, s);
}
assert(NISERR() || nfa->post->nins == 0 || nfa->post->tmp == nfa->post);
cleartraverse(nfa, nfa->pre);
assert(NISERR() || nfa->post->nins == 0 || nfa->post->tmp == NULL); /* the nins==0 (final unreachable) case will be caught later */
/* renumber surviving states */
n = 0; for (s = nfa->states; s != NULL; s = s->next)
s->no = n++;
nfa->nstates = n;
}
/* *markreachable-recursivemarkingofreachablestates
*/ staticvoid
markreachable(struct nfa *nfa, struct state *s, struct state *okay, /* consider only states with this mark */ struct state *mark) /* the value to mark with */
{ struct arc *a;
/* Since this is recursive, it could be driven to stack overflow */ if (STACK_TOO_DEEP(nfa->v->re))
{
NERR(REG_ETOOBIG); return;
}
if (s->tmp != okay) return;
s->tmp = mark;
for (a = s->outs; a != NULL; a = a->outchain)
markreachable(nfa, a->to, okay, mark);
}
/* *markcanreach-recursivemarkingofstateswhichcanreachhere
*/ staticvoid
markcanreach(struct nfa *nfa, struct state *s, struct state *okay, /* consider only states with this mark */ struct state *mark) /* the value to mark with */
{ struct arc *a;
/* Since this is recursive, it could be driven to stack overflow */ if (STACK_TOO_DEEP(nfa->v->re))
{
NERR(REG_ETOOBIG); return;
}
if (s->tmp != okay) return;
s->tmp = mark;
for (a = s->ins; a != NULL; a = a->inchain)
markcanreach(nfa, a->from, okay, mark);
}
/* *analyze-ascertainpotentially-usefulfactsaboutanoptimizedNFA
*/ staticlong/* re_info bits to be ORed in */
analyze(struct nfa *nfa)
{ struct arc *a; struct arc *aa;
if (NISERR()) return0;
/* Detect whether NFA can't match anything */ if (nfa->pre->outs == NULL) return REG_UIMPOSSIBLE;
/* Detect whether NFA matches all strings (possibly with length bounds) */
checkmatchall(nfa);
/* Detect whether NFA can possibly match a zero-length string */ for (a = nfa->pre->outs; a != NULL; a = a->outchain) for (aa = a->to->outs; aa != NULL; aa = aa->outchain) if (aa->to == nfa->post) return REG_UEMPTYMATCH; return0;
}
/* *First,scanallthestatestoverifythatonlyRAINBOWarcsappear, *pluspseudocolorarcsadjacenttothepreandpoststates.Thislets *usquicklyeliminatemostcasesthataren'tmatchallNFAs.
*/ for (s = nfa->states; s != NULL; s = s->next)
{ struct arc *a;
for (a = s->outs; a != NULL; a = a->outchain)
{ if (a->type != PLAIN) return; /* any LACONs make it non-matchall */ if (a->co != RAINBOW)
{ if (nfa->cm->cd[a->co].flags & PSEUDO)
{ /* *Pseudocolorarc:verifyit'sinavalidplace(this *seemsquiteunlikelytofail,butlet'sbesure).
*/ if (s == nfa->pre &&
(a->co == nfa->bos[0] || a->co == nfa->bos[1])) /* okay BOS/BOL arc */ ; elseif (a->to == nfa->post &&
(a->co == nfa->eos[0] || a->co == nfa->eos[1])) /* okay EOS/EOL arc */ ; else return; /* unexpected pseudocolor arc */ /* We'll check these arcs some more below. */
} else return; /* any other color makes it non-matchall */
}
} /* Also, assert that the tmp fields are available for use. */
assert(s->tmp == NULL);
}
/* *Recursivelysearchthegraphforall-RAINBOWpathstothe"post"state, *startingatthe"pre"state,andcomputingthelengthsofthepaths. *(Giventheprecedingchecks,thereshouldbeatleastonesuchpath. *Howeverwecouldgetbackafalseresultanyway,incasethereare *multi-stateloops,pathsexceedingDUPINF+1length,ornon-algorithmic *failuressuchasENOMEM.)
*/ if (checkmatchall_recurse(nfa, nfa->pre, haspaths))
{ /* The useful result is the path length array for the pre state */ bool *haspath = haspaths[nfa->pre->no]; int minmatch,
maxmatch,
morematch;
assert(haspath != NULL);
/* *haspath[]nowrepresentsthesetofpossiblepathlengths;butwe *wanttoreducethattoaminandmaxvalue,becauseitdoesn'tseem *worthcomplicatingregexec.ctodealwithnonconsecutivepossible *matchlengths.Findminandmaxoffirstrunoflengths,then *verifytherearenononconsecutivelengths.
*/ for (minmatch = 0; minmatch <= DUPINF + 1; minmatch++)
{ if (haspath[minmatch]) break;
}
assert(minmatch <= DUPINF + 1); /* else checkmatchall_recurse lied */ for (maxmatch = minmatch; maxmatch < DUPINF + 1; maxmatch++)
{ if (!haspath[maxmatch + 1]) break;
} for (morematch = maxmatch + 1; morematch <= DUPINF + 1; morematch++)
{ if (haspath[morematch])
{
haspath = NULL; /* fail, there are nonconsecutive lengths */ break;
}
}
if (haspath != NULL)
{ /* *Success,sorecordtheinfo.Herewehaveafinepoint:the *pathlengthfromtheprestateincludesthepre-to-initial *transition,soit'sonemorethantheactuallymatchedstring *length.(Weavoidedcountingthefinal-to-posttransition *withincheckmatchall_recurse,butnotthisone.)Thisiswhy *checkmatchall_recurseallowsonemorelevelofpathlengththan *mightseemnecessary.Thisdecrementalsotakescareof *convertingcheckmatchall_recurse'sdefinitionof"infinity"as *"DUPINF+1"toournormalrepresentationas"DUPINF".
*/
assert(minmatch > 0); /* else pre and post states were adjacent */
nfa->minmatchall = minmatch - 1;
nfa->maxmatchall = maxmatch - 1;
nfa->flags |= MATCHALL;
}
}
/* Clean up */ for (i = 0; i < nfa->nstates; i++)
{ if (haspaths[i] != NULL)
FREE(haspaths[i]);
}
FREE(haspaths);
}
/* *Sincethisisrecursive,itcouldbedriventostackoverflow.Butwe *neednottreatthatasahardfailure;justdeemtheNFAnon-matchall.
*/ if (STACK_TOO_DEEP(nfa->v->re)) returnfalse;
/* In case the search takes a long time, check for cancel */
INTERRUPT(nfa->v->re);
/* Create a haspath array for this state */
haspath = (bool *) MALLOC((DUPINF + 2) * sizeof(bool)); if (haspath == NULL) returnfalse; /* again, treat as non-matchall */
memset(haspath, 0, (DUPINF + 2) * sizeof(bool));
/* Mark this state as being visited */
assert(s->tmp == NULL);
s->tmp = s;
for (a = s->outs; a != NULL; a = a->outchain)
{ if (a->co != RAINBOW) continue; /* ignore pseudocolor arcs */ if (a->to == nfa->post)
{ /* We found an all-RAINBOW path to the post state */
result = true;
/* *Markthisstateasbeingzerostepsawayfromthestringend *(thetransitiontothepoststateisn'tcounted).
*/
haspath[0] = true;
} elseif (a->to == s)
{ /* We found a cycle of length 1, which we'll deal with below. */
foundloop = true;
} elseif (a->to->tmp != NULL)
{ /* It's busy, so we found a cycle of length > 1, so fail. */
result = false; break;
} else
{ /* Consider paths forward through this to-state. */ bool *nexthaspath; int i;
/* If to-state was not already visited, recurse */ if (haspaths[a->to->no] == NULL)
{
result = checkmatchall_recurse(nfa, a->to, haspaths); /* Fail if any recursive path fails */ if (!result) break;
} else
{ /* The previous visit must have found path(s) to the end */
result = true;
}
assert(a->to->tmp == NULL);
nexthaspath = haspaths[a->to->no];
assert(nexthaspath != NULL);
/* *Now,foreverypathoflengthifroma->totothestringend, *thereisapathoflengthi+1fromstothestringend.
*/ if (nexthaspath[DUPINF] != nexthaspath[DUPINF + 1])
{ /* *a->tohasapathoflengthexactlyDUPINF,butnotlonger; *orithaspathsofalllengths>DUPINFbutnotoneof *exactlythatlength.Ineithercase,wecannotrepresent *thepossiblepathlengthsfromscorrectly,sofail.
*/
result = false; break;
} /* Merge knowledge of these path lengths into what we have */ for (i = 0; i < DUPINF; i++)
haspath[i + 1] |= nexthaspath[i]; /* Infinity + 1 is still infinity */
haspath[DUPINF + 1] |= nexthaspath[DUPINF + 1];
}
}
if (result && foundloop)
{ /* *Ifthereisalength-1loopatthisstate,thenfindtheshortest *knownpathlengthtotheend.Theloopmeansthateverylarger *pathlengthispossible,too.(Itdoesn'tmatterwhetheranyof *thelongerlengthswerealreadyknownpossible.)
*/ int i;
for (i = 0; i <= DUPINF; i++)
{ if (haspath[i]) break;
} for (i++; i <= DUPINF + 1; i++)
haspath[i] = true;
}
/* Report out the completed path length map */
assert(s->no < nfa->nstates);
assert(haspaths[s->no] == NULL);
haspaths[s->no] = haspath;
/* Mark state no longer busy */
s->tmp = NULL;
return result;
}
/* *check_out_colors_match-subroutineforcheckmatchall * *Checkwhetherthesetofstatesreachablefromsbyarcsofcolorco1 *isequivalenttothesetreachablebyarcsofcolorco2. *checkmatchallalreadyverifiedthatalloftheNFA'sarcsarePLAIN, *soweneednotexaminearctypeshere.
*/ staticbool
check_out_colors_match(struct state *s, color co1, color co2)
{ bool result = true; struct arc *a;
/* *Todothisinlineartime,weassumethattheNFAcontainsnoduplicate *arcs.Runthroughtheout-arcs,markingstatesreachablebyarcsof *colorco1.Runthroughagain,un-markingstatesreachablebyarcsof *colorco2;ifweseeanot-markedstate,weknowthisco2arcis *unmatched.Thenrunthroughagain,checkingforstill-markedstates, *andinanycaseleavingallthetmpfieldsresettoNULL.
*/ for (a = s->outs; a != NULL; a = a->outchain)
{ if (a->co == co1)
{
assert(a->to->tmp == NULL);
a->to->tmp = a->to;
}
} for (a = s->outs; a != NULL; a = a->outchain)
{ if (a->co == co2)
{ if (a->to->tmp != NULL)
a->to->tmp = NULL; else
result = false; /* unmatched co2 arc */
}
} for (a = s->outs; a != NULL; a = a->outchain)
{ if (a->co == co1)
{ if (a->to->tmp != NULL)
{
result = false; /* unmatched co1 arc */
a->to->tmp = NULL;
}
}
} return result;
}
/* *check_in_colors_match-subroutineforcheckmatchall * *Checkwhetherthesetofstatesthatcanreachsbyarcsofcolorco1 *isequivalenttothesetthatcanreachsbyarcsofcolorco2. *checkmatchallalreadyverifiedthatalloftheNFA'sarcsarePLAIN, *soweneednotexaminearctypeshere.
*/ staticbool
check_in_colors_match(struct state *s, color co1, color co2)
{ bool result = true; struct arc *a;
/* *Identicalalgorithmtocheck_out_colors_match,exceptexaminethe *from-statesofs'inarcs.
*/ for (a = s->ins; a != NULL; a = a->inchain)
{ if (a->co == co1)
{
assert(a->from->tmp == NULL);
a->from->tmp = a->from;
}
} for (a = s->ins; a != NULL; a = a->inchain)
{ if (a->co == co2)
{ if (a->from->tmp != NULL)
a->from->tmp = NULL; else
result = false; /* unmatched co2 arc */
}
} for (a = s->ins; a != NULL; a = a->inchain)
{ if (a->co == co1)
{ if (a->from->tmp != NULL)
{
result = false; /* unmatched co1 arc */
a->from->tmp = NULL;
}
}
} return result;
}
/* *TheREG_MAX_COMPILE_SPACErestrictionensuresthatintegeroverflow *can'toccurinthisloopnorintheallocationrequestsbelow.
*/
nstates = 0;
narcs = 0; for (s = nfa->states; s != NULL; s = s->next)
{
nstates++;
narcs += s->nouts + 1; /* need one extra for endmarker */
}
ca = cnfa->arcs; for (s = nfa->states; s != NULL; s = s->next)
{
assert((size_t) s->no < nstates);
cnfa->stflags[s->no] = 0;
cnfa->states[s->no] = ca;
first = ca; for (a = s->outs; a != NULL; a = a->outchain) switch (a->type)
{ case PLAIN:
ca->co = a->co;
ca->to = a->to->no;
ca++; break; case LACON:
assert(s->no != cnfa->pre);
assert(a->co >= 0); /* make sure the modified color number will fit */ if (a->co > MAX_COLOR - cnfa->ncolors)
{
NERR(REG_ECOLORS); return;
}
ca->co = (color) (cnfa->ncolors + a->co);
ca->to = a->to->no;
ca++;
cnfa->flags |= HASLACONS; break; default:
NERR(REG_ASSERT); return;
}
carcsort(first, ca - first);
ca->co = COLORLESS;
ca->to = 0;
ca++;
}
assert(ca == &cnfa->arcs[narcs]);
assert(cnfa->nstates != 0);
/* mark no-progress states */ for (a = nfa->pre->outs; a != NULL; a = a->outchain)
cnfa->stflags[a->to->no] = CNFA_NOPROGRESS;
cnfa->stflags[nfa->pre->no] = CNFA_NOPROGRESS;
}
/* *carcsort-sortcompacted-NFAarcsbycolor
*/ staticvoid
carcsort(struct carc *first, size_t n)
{ if (n > 1)
qsort(first, n, sizeof(struct carc), carc_cmp);
}
if (aa->co < bb->co) return -1; if (aa->co > bb->co) return +1; if (aa->to < bb->to) return -1; if (aa->to > bb->to) return +1; /* This is unreached, since there should be no duplicate arcs now: */ return0;
}
fprintf(f, "%d%s%c", s->no, (s->tmp != NULL) ? "T" : "",
(s->flag) ? s->flag : '.'); if (s->prev != NULL && s->prev->next != s)
fprintf(f, "\tstate chain bad\n"); if (s->nouts == 0)
fprintf(f, "\tno out arcs\n"); else
dumparcs(s, f); for (a = s->ins; a != NULL; a = a->inchain)
{ if (a->to != s)
fprintf(f, "\tlink from %d to %d on %d's in-chain\n",
a->from->no, a->to->no, s->no);
}
fflush(f);
}
/* *dumparcs-dumpout-arcsinhuman-readableform
*/ staticvoid
dumparcs(struct state *s,
FILE *f)
{ int pos; struct arc *a;
/* printing oldest arcs first is usually clearer */
a = s->outs;
assert(a != NULL); while (a->outchain != NULL)
a = a->outchain;
pos = 1; do
{
dumparc(a, s, f); if (pos == 5)
{
fprintf(f, "\n");
pos = 1;
} else
pos++;
a = a->outchainRev;
} while (a != NULL); if (pos != 1)
fprintf(f, "\n");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.