📄 regc_nfa.c
字号:
struct arc *a; assert(old != new); for (a = old->outs; a != NULL; a = a->outchain) cparc(nfa, a, new, a->to);}/* * cloneouts - copy out arcs of a state to another state pair, modifying type */static voidcloneouts(struct nfa * nfa, struct state * old, struct state * from, struct state * to, int type){ struct arc *a; assert(old != from); for (a = old->outs; a != NULL; a = a->outchain) newarc(nfa, type, a->co, from, to);}/* * delsub - delete a sub-NFA, updating subre pointers if necessary * * This uses a recursive traversal of the sub-NFA, marking already-seen * states using their tmp pointer. */static voiddelsub(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); 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 - the recursive heart of delsub * This routine's basic job is to destroy all out-arcs of the state. */static voiddeltraverse(struct nfa * nfa, struct state * leftend, struct state * s){ struct arc *a; struct state *to; 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); 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 - duplicate sub-NFA * * Another recursive traversal, this time using tmp to point to duplicates * as well as mark already-seen states. (You knew there was a reason why * it's a state pointer, didn't you? :-)) */static voiddupnfa(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 - recursive heart of dupnfa */static voidduptraverse(struct nfa * nfa, struct state * s, struct state * stmp) /* s's duplicate, or NULL */{ struct arc *a; if (s->tmp != NULL) return; /* already done */ s->tmp = (stmp == NULL) ? newstate(nfa) : stmp; if (s->tmp == NULL) { assert(NISERR()); 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); }}/* * cleartraverse - recursive cleanup for algorithms that leave tmp ptrs set */static voidcleartraverse(struct nfa * nfa, struct state * s){ struct arc *a; if (s->tmp == NULL) return; s->tmp = NULL; for (a = s->outs; a != NULL; a = a->outchain) cleartraverse(nfa, a->to);}/* * specialcolors - fill in special colors for an NFA */static voidspecialcolors(struct nfa * nfa){ /* false colors for BOS, BOL, EOS, EOL */ if (nfa->parent == NULL) { nfa->bos[0] = pseudocolor(nfa->cm); nfa->bos[1] = pseudocolor(nfa->cm); nfa->eos[0] = pseudocolor(nfa->cm); nfa->eos[1] = pseudocolor(nfa->cm); } else { assert(nfa->parent->bos[0] != COLORLESS); nfa->bos[0] = nfa->parent->bos[0]; assert(nfa->parent->bos[1] != COLORLESS); nfa->bos[1] = nfa->parent->bos[1]; assert(nfa->parent->eos[0] != COLORLESS); nfa->eos[0] = nfa->parent->eos[0]; assert(nfa->parent->eos[1] != COLORLESS); nfa->eos[1] = nfa->parent->eos[1]; }}/* * optimize - optimize an NFA */static long /* re_info bits */optimize(struct nfa * nfa, FILE *f) /* for debug output; NULL none */{#ifdef REG_DEBUG int verbose = (f != NULL) ? 1 : 0; if (verbose) fprintf(f, "\ninitial cleanup:\n");#endif 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 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 */ return analyze(nfa); /* and analysis */}/* * pullback - pull back constraints backward to (with luck) eliminate them */static voidpullback(struct nfa * nfa, FILE *f) /* for debug output; NULL none */{ struct state *s; struct state *nexts; struct arc *a; struct arc *nexta; 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; for (a = s->outs; a != NULL && !NISERR(); a = nexta) { nexta = a->outchain; if (a->type == '^' || a->type == BEHIND) if (pull(nfa, a)) progress = 1; assert(nexta == NULL || s->no != FREESTATE); } } if (progress && f != NULL) dumpnfa(nfa, f); } while (progress && !NISERR()); if (NISERR()) return; 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); } }}/* * pull - pull a back constraint backward past its source state * A significant property of this function is that it deletes at most * one state -- the constraint's from state -- and only if the constraint * was that state's last outarc. */static int /* 0 couldn't, 1 could */pull(struct nfa * nfa, struct arc * con){ struct state *from = con->from; struct state *to = con->to; struct arc *a; struct arc *nexta; struct state *s; if (from == to) { /* circular constraint is pointless */ freearc(nfa, con); return 1; } if (from->flag) /* can't pull back beyond start */ return 0; if (from->nins == 0) { /* unreachable */ freearc(nfa, con); return 1; } /* * DGP 2007-11-15: Cloning a state with a circular constraint on its list * of outs can lead to trouble [Tcl Bug 1810038], so get rid of them first. */ for (a = from->outs; a != NULL; a = nexta) { nexta = a->outchain; switch (a->type) { case '^': case '$': case BEHIND: case AHEAD: if (from == a->to) freearc(nfa, a); break; } } /* first, clone from state if necessary to avoid other outarcs */ if (from->nouts > 1) { s = newstate(nfa); if (NISERR()) return 0; assert(to != from); /* con is not an inarc */ copyins(nfa, from, s); /* duplicate inarcs */ cparc(nfa, con, s, to); /* move constraint arc */ freearc(nfa, con); from = s; con = from->outs; } assert(from->nouts == 1); /* propagate the constraint into the from state's inarcs */ for (a = from->ins; a != NULL; a = nexta) { nexta = a->inchain; switch (combine(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 */ s = newstate(nfa); if (NISERR()) return 0; cparc(nfa, a, s, to); /* anticipate move */ cparc(nfa, con, a->from, s); if (NISERR()) return 0; freearc(nfa, a); break; default: assert(NOTREACHED); break; } } /* remaining inarcs, if any, incorporate the constraint */ moveins(nfa, from, to); dropstate(nfa, from); /* will free the constraint */ return 1;}/* * pushfwd - push forward constraints forward to (with luck) eliminate them */static voidpushfwd(struct nfa * nfa, FILE *f) /* for debug output; NULL none */{ struct state *s; struct state *nexts; struct arc *a; struct arc *nexta; 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; for (a = s->ins; a != NULL && !NISERR(); a = nexta) { nexta = a->inchain; if (a->type == '$' || a->type == AHEAD) if (push(nfa, a)) progress = 1; assert(nexta == NULL || s->no != FREESTATE); } } if (progress && f != NULL) dumpnfa(nfa, f); } while (progress && !NISERR()); if (NISERR()) return; 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); } }}/* * push - push a forward constraint forward past its destination state * A significant property of this function is that it deletes at most * one state -- the constraint's to state -- and only if the constraint * was that state's last inarc. */static int /* 0 couldn't, 1 could */push(struct nfa * nfa, struct arc * con){ struct state *from = con->from; struct state *to = con->to; struct arc *a; struct arc *nexta; struct state *s; if (to == from) { /* circular constraint is pointless */ freearc(nfa, con); return 1; } if (to->flag) /* can't push forward beyond end */ return 0; if (to->nouts == 0) { /* dead end */ freearc(nfa, con); return 1; } /* * DGP 2007-11-15: Here we duplicate the same protections as appear * in pull() above to avoid troubles with cloning a state with a * circular constraint on its list of ins. It is not clear whether * this is necessary, or is protecting against a "can't happen". * Any test case that actually leads to a freearc() call here would * be a welcome addition to the test suite. */ for (a = to->ins; a != NULL; a = nexta) { nexta = a->inchain; switch (a->type) { case '^': case '$': case BEHIND: case AHEAD: if (a->from == to) freearc(nfa, a); break; } } /* first, clone to state if necessary to avoid other inarcs */ if (to->nins > 1) { s = newstate(nfa); if (NISERR()) return 0; copyouts(nfa, to, s); /* duplicate outarcs */ cparc(nfa, con, from, s); /* move constraint */ freearc(nfa, con); to = s; con = to->ins; } assert(to->nins == 1); /* propagate the constraint into the to state's outarcs */ for (a = to->outs; a != NULL; a = nexta) { nexta = a->outchain; switch (combine(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 */ s = newstate(nfa); if (NISERR()) return 0; cparc(nfa, con, s, a->to); /* anticipate move */ cparc(nfa, a, from, s); if (NISERR()) return 0; freearc(nfa, a); break; default: assert(NOTREACHED); break; } } /* remaining outarcs, if any, incorporate the constraint */ moveouts(nfa, to, from); dropstate(nfa, to); /* will free the constraint */ return 1;}/* * combine - constraint lands on an arc, what happens? * * #def INCOMPATIBLE 1 // destroys arc * #def SATISFIED 2 // constraint satisfied * #def COMPATIBLE 3 // compatible but not satisfied yet */static intcombine(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; return INCOMPATIBLE; break; case CA('^', '^'): /* collision, similar constraints */ case CA('$', '$'): case CA(AHEAD, AHEAD): case CA(BEHIND, BEHIND): if (con->co == a->co) /* true duplication */ return SATISFIED; 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 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -