// startCond() returns the leading empty-width conditions that must be true // in any match. It returns -1 (all bits set) if no matches are possible. public int startCond() { int flag = 0; // bitmask of EMPTY_* flags int pc = start; bool done = false; for (;;) { Inst i = inst[pc]; switch (i.op) { case Inst.InstOp.EMPTY_WIDTH: flag |= i.arg; break; case Inst.InstOp.FAIL: return(-1); case Inst.InstOp.CAPTURE: case Inst.InstOp.NOP: break; // skip default: done = true; break; } if (done) { break; } pc = i.@out; } return(flag); }
// add() adds an entry to |q| for |pc|, unless the |q| already has such an // entry. It also recursively adds an entry for all instructions reachable // from |pc| by following empty-width conditions satisfied by |cond|. |pos| // gives the current position in the input. |cond| is a bitmask of EMPTY_* // flags. private Thread add(Queue q, int pc, int pos, int[] cap, int cond, Thread t) { if (pc == 0) { return(t); } if (q.contains(pc)) { return(t); } int d = q.add(pc); Inst inst = prog.inst[pc]; switch (inst.op) { default: throw new IllegalStateException("unhandled"); case Inst.InstOp.FAIL: break; // nothing case Inst.InstOp.ALT: case Inst.InstOp.ALT_MATCH: t = add(q, inst.@out, pos, cap, cond, t); t = add(q, inst.arg, pos, cap, cond, t); break; case Inst.InstOp.EMPTY_WIDTH: if ((inst.arg & ~cond) == 0) { t = add(q, inst.@out, pos, cap, cond, t); } break; case Inst.InstOp.NOP: t = add(q, inst.@out, pos, cap, cond, t); break; case Inst.InstOp.CAPTURE: if (inst.arg < ncap) { int opos = cap[inst.arg]; cap[inst.arg] = pos; add(q, inst.@out, pos, cap, cond, null); cap[inst.arg] = opos; } else { t = add(q, inst.@out, pos, cap, cond, t); } break; case Inst.InstOp.MATCH: case Inst.InstOp.RUNE: case Inst.InstOp.RUNE1: case Inst.InstOp.RUNE_ANY: case Inst.InstOp.RUNE_ANY_NOT_NL: if (t == null) { t = alloc(inst); } else { t.inst = inst; } if (ncap > 0 && t.cap != cap) { System.Array.Copy(cap, 0, t.cap, 0, ncap); } q.denseThreads[d] = t; t = null; break; } return(t); }
// step() executes one step of the machine, running each of the threads // on |runq| and appending new threads to |nextq|. // The step processes the rune |c| (which may be -1 for EOF), // which starts at position |pos| and ends at |nextPos|. // |nextCond| gives the setting for the EMPTY_* flags after |c|. // |anchor| is the anchoring flag and |atEnd| signals if we are at the end of // the input string. private void step( Queue runq, Queue nextq, int pos, int nextPos, int c, int nextCond, int anchor, bool atEnd) { bool longest = re2.longest; for (int j = 0; j < runq.size; ++j) { Thread t = runq.denseThreads[j]; if (t == null) { continue; } if (longest && matched && ncap > 0 && matchcap[0] < t.cap[0]) { free(t); continue; } Inst i = t.inst; bool do_add = false; switch (i.op) { case Inst.InstOp.MATCH: if (anchor == RE2.ANCHOR_BOTH && !atEnd) { // Don't match if we anchor at both start and end and those // expectations aren't met. break; } if (ncap > 0 && (!longest || !matched || matchcap[1] < pos)) { t.cap[1] = pos; System.Array.Copy(t.cap, 0, matchcap, 0, ncap); } if (!longest) { free(runq, j + 1); } matched = true; break; case Inst.InstOp.RUNE: do_add = i.matchRune(c); break; case Inst.InstOp.RUNE1: do_add = c == i.runes[0]; break; case Inst.InstOp.RUNE_ANY: do_add = true; break; case Inst.InstOp.RUNE_ANY_NOT_NL: do_add = c != '\n'; break; default: throw new IllegalStateException("bad inst"); } if (do_add) { t = add(nextq, i.@out, nextPos, t.cap, nextCond, t); } if (t != null) { free(t); runq.denseThreads[j] = null; } } runq.clear(); }