src/main/java/com/basistech/tclre/Compiler.java
private void parseqatom(int stopper, int type, State lp, State rp, Subre top) throws RegexException {
        State s;    /* temporaries for new states */
        State s2;
        int m;
        int n;
        Subre atom; /* atom's subtree */
        Subre t;

        boolean cap;    /* capturing parens? */
        int pos;        /* positive lookahead? */
        int subno;      /* capturing-parens or backref number */
        int atomtype;
        int qprefer;        /* quantifier short/long preference */
        int f;
        AtomSetter atomp;

    /* initial bookkeeping */
        atom = null;
        assert lp.nouts == 0;   /* must string new code */
        assert rp.nins == 0;    /*  between lp and rp */
        subno = 0;      /* just to shut lint up */

    /* an atom or constraint... */
        atomtype = nexttype;
        switch (atomtype) {
    /* first, constraints, which end by returning */
        case '^':
            nfa.newarc('^', (short)1, lp, rp);
            if (0 != (cflags & Flags.REG_NLANCH)) {
                nfa.newarc(BEHIND, nlcolor, lp, rp);
            }
            lex.next();
            return;

        case '$':
            nfa.newarc('$', (short)1, lp, rp);
            if (0 != (cflags & Flags.REG_NLANCH)) {
                nfa.newarc(AHEAD, nlcolor, lp, rp);
            }
            lex.next();
            return;

        case SBEGIN:
            nfa.newarc('^', (short)1, lp, rp);  /* BOL */
            nfa.newarc('^', (short)0, lp, rp);  /* or BOS */
            lex.next();
            return;

        case SEND:
            nfa.newarc('$', (short)1, lp, rp);  /* EOL */
            nfa.newarc('$', (short)0, lp, rp);  /* or EOS */
            lex.next();
            return;

        case '<':
            wordchrs(); /* does next() */
            s = nfa.newstate();
            nonword(BEHIND, lp, s);
            word(AHEAD, s, rp);
            return;

        case '>':
            wordchrs(); /* does next() */
            s = nfa.newstate();
            word(BEHIND, lp, s);
            nonword(AHEAD, s, rp);
            return;

        case WBDRY:
            wordchrs(); /* does next() */
            s = nfa.newstate();
            nonword(BEHIND, lp, s);
            word(AHEAD, s, rp);
            s = nfa.newstate();
            word(BEHIND, lp, s);
            nonword(AHEAD, s, rp);
            return;

        case NWBDRY:
            wordchrs(); /* does next() */
            s = nfa.newstate();
            word(BEHIND, lp, s);
            word(AHEAD, s, rp);
            s = nfa.newstate();
            nonword(BEHIND, lp, s);
            nonword(AHEAD, s, rp);
            return;

        case LACON: /* lookahead constraint */
            pos = nextvalue;
            lex.next();
            s = nfa.newstate();
            s2 = nfa.newstate();
            parse(')', LACON, s, s2); // parse for side-effect.
            assert see(')');
            lex.next();
            n = newlacon(s, s2, pos);
            nfa.newarc(LACON, (short)n, lp, rp);
            return;

    /* then errors, to get them out of the way */
        case '*':
        case '+':
        case '?':
        case '{':
            throw new RegexException("Pattern syntax error (*+?} misplaced).");

    /* then plain characters, and minor variants on that theme */
        case ')':       /* unbalanced paren */
            if ((cflags & Flags.REG_ADVANCED) != Flags.REG_EXTENDED) {
                throw new RegexException("Unbalanced parenthesis.");
            }
        /* legal in EREs due to specification botch */
            note(Flags.REG_UPBOTCH);
        /* fallthrough into case PLAIN */
        case PLAIN:
            // look out for surrogates as ordinary chars.
            if (nextvalue < Character.MAX_VALUE && Character.isHighSurrogate((char)nextvalue)) {
                char high = (char)nextvalue;
                lex.next();
                char low = (char)nextvalue;
                int codepoint = Character.toCodePoint(high, low);
                onechr(codepoint, lp, rp);
            } else {
                onechr(nextvalue, lp, rp);
            }
            cm.okcolors(nfa);
            lex.next();
            break;
        case '[':
            if (nextvalue == 1) {
                bracket(lp, rp);
            } else {
                cbracket(lp, rp);
            }
            assert see(']');
            lex.next();
            break;
        case '.':
            cm.rainbow(nfa, PLAIN,
                    (0 != (cflags & Flags.REG_NLSTOP)) ? nlcolor : Constants.COLORLESS,
                    lp, rp);
            lex.next();
            break;
    /* and finally the ugly stuff */
        case '(':   /* value flags as capturing or non */
            if (type == LACON) {
                cap = false;
            } else {
                cap = nextvalue != 0;
            }
            if (cap) {
                subno = subs.size() + 1; // first subno is 1.
                /*
                 * This recurses via a call to parse just below.
                 * So, the size() just above has to reflect this new sub,
                 * even though we won't create the object until a little further
                 * down.
                 */
                subs.add(null); 
            } else {
                atomtype = PLAIN;   /* something that's not '(' */
            }
            lex.next();
        /* need new endpoints because tree will contain pointers */
            s = nfa.newstate();
            s2 = nfa.newstate();

            nfa.emptyarc(lp, s);
            nfa.emptyarc(s2, rp);

            atom = parse(')', PLAIN, s, s2);
            assert see(')');
            lex.next();

            if (cap) {
                // we can't assert anything about the size of 'subs', recursion may have added to it.
                // but we can check that nothing has used our slot.
                assert subs.get(subno - 1) == null;
                subs.set(subno - 1, atom);
                t = new Subre('(', atom.flags | Subre.CAP, lp, rp);
                t.subno = subno;
                t.left = atom;
                atom = t;
            }
        /* postpone everything else pending possible {0} */
            break;
        case BACKREF:   /* the Feature From The Black Lagoon */
            if (type == LACON) {
                throw new RegexException("REG_ESUBREG");
            }
            if (nextvalue > subs.size()) {
                throw new RegexException(String.format("Backreference to %d out of range of defined subexpressions (%d)", nextvalue, subs
                        .size()));
            }
            if (subs.get(nextvalue - 1) == null) { // \1 is first backref, living in slot 0.
                throw new RegexException(String.format("Backreference to %d refers to non-capturing group.", nextvalue));
            }

            assert nextvalue > 0;
            atom = new Subre('b', Subre.BACKR, lp, rp);
            subno = nextvalue ;
            atom.subno = subno;
            nfa.emptyarc(lp, rp);   /* temporarily, so there's something */
            lex.next();
            break;

        default:
            throw new RuntimeException("Impossible type in lex");
        }

    /* ...and an atom may be followed by a quantifier */
        switch (nexttype) {
        case '*':
            m = 0;
            n = INFINITY;
            qprefer = (nextvalue != 0) ? Subre.LONGER : Subre.SHORTER;
            lex.next();
            break;
        case '+':
            m = 1;
            n = INFINITY;
            qprefer = (nextvalue != 0) ? Subre.LONGER : Subre.SHORTER;
            lex.next();
            break;
        case '?':
            m = 0;
            n = 1;
            qprefer = (nextvalue != 0) ? Subre.LONGER : Subre.SHORTER;
            lex.next();
            break;
        case '{':
            lex.next();
            m = scannum();
            if (eat(',')) {
                if (see(DIGIT)) {
                    n = scannum();
                } else {
                    n = INFINITY;
                }
                if (m > n) {
                    throw new RegexException("First quantity is larger than second quantity in {m,n} quantifier.");
                }
            /* {m,n} exercises preference, even if it's {m,m} */
                qprefer = (nextvalue != 0) ? Subre.LONGER : Subre.SHORTER;
            } else {
                n = m;
            /* {m} passes operand's preference through */
                qprefer = 0;
            }
            if (!see('}')) {    /* catches errors too */
                throw new RegexException("Invalid syntax for {m,n} quantifier.");
            }
            lex.next();
            break;

        default:        /* no quantifier */
            m = 1;
            n = 1;
            qprefer = 0;
            break;
        }

    /* annoying special case:  {0} or {0,0} cancels everything */
        if (m == 0 && n == 0) {
            if (atomtype == '(') {
                assert subno == subs.size() - 1;
                subs.remove(subs.size() - 1);
            }
            delsub(nfa, lp, rp);
            nfa.emptyarc(lp, rp);
            return;
        }

    /* if not a messy case, avoid hard part */
        assert !messy(top.flags);
        f = top.flags | qprefer | ((atom != null) ? atom.flags : 0);
        if (atomtype != '(' && atomtype != BACKREF && !messy(up(f))) {
            if (!(m == 1 && n == 1)) {
                repeat(lp, rp, m, n);
            }
            top.flags = f;
            return;
        }

    /*
     * hard part:  something messy
     * That is, capturing parens, back reference, short/long clash, or
     * an atom with substructure containing one of those.
     */

    /* now we'll need a subre for the contents even if they're boring */
        if (atom == null) {
            atom = new Subre('=', 0, lp, rp);
        }

    /*
     * prepare a general-purpose state skeleton
     *
     *    --. [s] ---prefix--. [begin] ---atom--. [end] ----rest--. [rp]
     *   /                                            /
     * [lp] ---. [s2] ----bypass---------------------
     *
     * where bypass is an empty, and prefix is some repetitions of atom
     */
        s = nfa.newstate();     /* first, new endpoints for the atom */
        s2 = nfa.newstate();
        nfa.moveouts(lp, s);
        nfa.moveins(rp, s2);
        atom.begin = s;
        atom.end = s2;
        s = nfa.newstate();     /* and spots for prefix and bypass */
        s2 = nfa.newstate();
        nfa.emptyarc(lp, s);
        nfa.emptyarc(lp, s2);

    /* break remaining subRE into x{...} and what follows */
        t = new Subre('.', Subre.combine(qprefer, atom.flags), lp, rp);
        t.left = atom;

        final Subre target = t;
        atomp = new AtomSetter() {

            @Override
            public void set(Subre s) {
                target.left = s;
            }
        };

    /* here we should recurse... but we must postpone that to the end */

    /* split top into prefix and remaining */
        assert top.op == '=' && top.left == null && top.right == null;
        top.left = new Subre('=', top.flags, top.begin, lp);
        top.op = '.';
        top.right = t;

    /* if it's a backref, now is the time to replicate the subNFA */
        if (atomtype == BACKREF) {
            assert atom.begin.nouts == 1; /* just the EMPTY */
            delsub(nfa, atom.begin, atom.end);
            assert subs.get(subno - 1) != null;
        /* and here's why the recursion got postponed:  it must */
        /* wait until the skeleton is filled in, because it may */
        /* hit a backref that wants to copy the filled-in skeleton */
            nfa.dupnfa(subs.get(subno - 1).begin, subs.get(subno - 1).end,
                    atom.begin, atom.end);
        }

    /* it's quantifier time; first, turn x{0,...} into x{1,...}|empty */
        if (m == 0) {
            nfa.emptyarc(s2, atom.end);     /* the bypass */
            assert Subre.pref(qprefer) != 0;
            f = Subre.combine(qprefer, atom.flags);
            t = new Subre('|', f, lp, atom.end);
            t.left = atom;
            t.right = new Subre('|', Subre.pref(f), s2, atom.end);
            t.right.left = new Subre('=', 0, s2, atom.end);

            atomp.set(t);
            final Subre target2 = t;
            atomp = new AtomSetter() {
                @Override
                public void set(Subre s) {
                    target2.left = s;
                }
            };
            m = 1;
        }

    /* deal with the rest of the quantifier */
        if (atomtype == BACKREF) {
        /* special case:  backrefs have internal quantifiers */
            nfa.emptyarc(s, atom.begin);    /* empty prefix */
        /* just stuff everything into atom */
            repeat(atom.begin, atom.end, m, n);
            atom.min = (short)m;
            atom.max = (short)n;
            atom.flags |= Subre.combine(qprefer, atom.flags);
        } else if (m == 1 && n == 1) {
        /* no/vacuous quantifier:  done */
            nfa.emptyarc(s, atom.begin);    /* empty prefix */
        } else {
        /* turn x{m,n} into x{m-1,n-1}x, with capturing */
        /*  parens in only second x */
            nfa.dupnfa(atom.begin, atom.end, s, atom.begin);
            assert m >= 1 && m != INFINITY && n >= 1;
            repeat(s, atom.begin, m - 1, (n == INFINITY) ? n : n - 1);
            f = Subre.combine(qprefer, atom.flags);
            t = new Subre('.', f, s, atom.end); /* prefix and atom */
            t.left = new Subre('=', Subre.pref(f), s, atom.begin);
            t.right = atom;
            atomp.set(t);
        }

    /* and finally, look after that postponed recursion */
        t = top.right;
        if (!(see('|') || see(stopper) || see(EOS))) {
            t.right = parsebranch(stopper, type, atom.end, rp, true);
        } else {
            nfa.emptyarc(atom.end, rp);
            t.right = new Subre('=', 0, atom.end, rp);
        }
        assert see('|') || see(stopper) || see(EOS);
        t.flags |= Subre.combine(t.flags, t.right.flags);
        top.flags |= Subre.combine(top.flags, t.flags);
    }
private void parseqatom(int stopper, int type, State lp, State rp, Subre top) throws RegexException {
        State s;    /* temporaries for new states */
        State s2;
        int m;
        int n;
        Subre atom; /* atom's subtree */
        Subre t;

        boolean cap;    /* capturing parens? */
        int pos;        /* positive lookahead? */
        int subno;      /* capturing-parens or backref number */
        int atomtype;
        int qprefer;        /* quantifier short/long preference */
        int f;
        AtomSetter atomp;

    /* initial bookkeeping */
        atom = null;
        assert lp.nouts == 0;   /* must string new code */
        assert rp.nins == 0;    /*  between lp and rp */
        subno = 0;      /* just to shut lint up */

    /* an atom or constraint... */
        atomtype = nexttype;
        switch (atomtype) {
    /* first, constraints, which end by returning */
        case '^':
            nfa.newarc('^', (short)1, lp, rp);
            if (0 != (cflags & Flags.REG_NLANCH)) {
                nfa.newarc(BEHIND, nlcolor, lp, rp);
            }
            lex.next();
            return;

        case '$':
            nfa.newarc('$', (short)1, lp, rp);
            if (0 != (cflags & Flags.REG_NLANCH)) {
                nfa.newarc(AHEAD, nlcolor, lp, rp);
            }
            lex.next();
            return;

        case SBEGIN:
            nfa.newarc('^', (short)1, lp, rp);  /* BOL */
            nfa.newarc('^', (short)0, lp, rp);  /* or BOS */
            lex.next();
            return;

        case SEND:
            nfa.newarc('$', (short)1, lp, rp);  /* EOL */
            nfa.newarc('$', (short)0, lp, rp);  /* or EOS */
            lex.next();
            return;

        case '<':
            wordchrs(); /* does next() */
            s = nfa.newstate();
            nonword(BEHIND, lp, s);
            word(AHEAD, s, rp);
            return;

        case '>':
            wordchrs(); /* does next() */
            s = nfa.newstate();
            word(BEHIND, lp, s);
            nonword(AHEAD, s, rp);
            return;

        case WBDRY:
            wordchrs(); /* does next() */
            s = nfa.newstate();
            nonword(BEHIND, lp, s);
            word(AHEAD, s, rp);
            s = nfa.newstate();
            word(BEHIND, lp, s);
            nonword(AHEAD, s, rp);
            return;

        case NWBDRY:
            wordchrs(); /* does next() */
            s = nfa.newstate();
            word(BEHIND, lp, s);
            word(AHEAD, s, rp);
            s = nfa.newstate();
            nonword(BEHIND, lp, s);
            nonword(AHEAD, s, rp);
            return;

        case LACON: /* lookahead constraint */
            pos = nextvalue;
            lex.next();
            s = nfa.newstate();
            s2 = nfa.newstate();
            parse(')', LACON, s, s2); // parse for side-effect.
            assert see(')');
            lex.next();
            n = newlacon(s, s2, pos);
            nfa.newarc(LACON, (short)n, lp, rp);
            return;

    /* then errors, to get them out of the way */
        case '*':
        case '+':
        case '?':
        case '{':
            throw new RegexException("Pattern syntax error (*+?} misplaced).");

    /* then plain characters, and minor variants on that theme */
        case ')':       /* unbalanced paren */
            if ((cflags & Flags.REG_ADVANCED) != Flags.REG_EXTENDED) {
                throw new RegexException("Unbalanced parenthesis.");
            }
        /* legal in EREs due to specification botch */
            note(Flags.REG_UPBOTCH);
        /* fallthrough into case PLAIN */
        case PLAIN:
            // look out for surrogates as ordinary chars.
            if (nextvalue < Character.MAX_VALUE && Character.isHighSurrogate((char)nextvalue)) {
                char high = (char)nextvalue;
                lex.next();
                char low = (char)nextvalue;
                int codepoint = Character.toCodePoint(high, low);
                onechr(codepoint, lp, rp);
            } else {
                onechr(nextvalue, lp, rp);
            }
            cm.okcolors(nfa);
            lex.next();
            break;
        case '[':
            if (nextvalue == 1) {
                bracket(lp, rp);
            } else {
                cbracket(lp, rp);
            }
            assert see(']');
            lex.next();
            break;
        case '.':
            cm.rainbow(nfa, PLAIN,
                    (0 != (cflags & Flags.REG_NLSTOP)) ? nlcolor : Constants.COLORLESS,
                    lp, rp);
            lex.next();
            break;
    /* and finally the ugly stuff */
        case '(':   /* value flags as capturing or non */
            if (type == LACON) {
                cap = false;
            } else {
                cap = nextvalue != 0;
            }
            if (cap) {
                subno = subs.size() + 1; // first subno is 1.
                /*
                 * This recurses via a call to parse just below.
                 * So, the size() just above has to reflect this new sub,
                 * even though we won't create the object until a little further
                 * down.
                 */
                subs.add(null); 
            } else {
                atomtype = PLAIN;   /* something that's not '(' */
            }
            lex.next();
        /* need new endpoints because tree will contain pointers */
            s = nfa.newstate();
            s2 = nfa.newstate();

            nfa.emptyarc(lp, s);
            nfa.emptyarc(s2, rp);

            atom = parse(')', PLAIN, s, s2);
            assert see(')');
            lex.next();

            if (cap) {
                // we can't assert anything about the size of 'subs', recursion may have added to it.
                // but we can check that nothing has used our slot.
                assert subs.get(subno - 1) == null;
                subs.set(subno - 1, atom);
                t = new Subre('(', atom.flags | Subre.CAP, lp, rp);
                t.subno = subno;
                t.left = atom;
                atom = t;
            }
        /* postpone everything else pending possible {0} */
            break;
        case BACKREF:   /* the Feature From The Black Lagoon */
            if (type == LACON) {
                throw new RegexException("REG_ESUBREG");
            }
            if (nextvalue > subs.size()) {
                throw new RegexException(String.format("Backreference to %d out of range of defined subexpressions (%d)", nextvalue, subs
                        .size()));
            }
            if (subs.get(nextvalue - 1) == null) { // \1 is first backref, living in slot 0.
                throw new RegexException(String.format("Backreference to %d refers to non-capturing group.", nextvalue));
            }

            assert nextvalue > 0;
            atom = new Subre('b', Subre.BACKR, lp, rp);
            subno = nextvalue ;
            atom.subno = subno;
            nfa.emptyarc(lp, rp);   /* temporarily, so there's something */
            lex.next();
            break;

        default:
            throw new RuntimeException("Impossible type in lex");
        }

    /* ...and an atom may be followed by a quantifier */
        switch (nexttype) {
        case '*':
            m = 0;
            n = INFINITY;
            qprefer = (nextvalue != 0) ? Subre.LONGER : Subre.SHORTER;
            lex.next();
            break;
        case '+':
            m = 1;
            n = INFINITY;
            qprefer = (nextvalue != 0) ? Subre.LONGER : Subre.SHORTER;
            lex.next();
            break;
        case '?':
            m = 0;
            n = 1;
            qprefer = (nextvalue != 0) ? Subre.LONGER : Subre.SHORTER;
            lex.next();
            break;
        case '{':
            lex.next();
            m = scannum();
            if (eat(',')) {
                if (see(DIGIT)) {
                    n = scannum();
                } else {
                    n = INFINITY;
                }
                if (m > n) {
                    throw new RegexException("First quantity is larger than second quantity in {m,n} quantifier.");
                }
            /* {m,n} exercises preference, even if it's {m,m} */
                qprefer = (nextvalue != 0) ? Subre.LONGER : Subre.SHORTER;
            } else {
                n = m;
            /* {m} passes operand's preference through */
                qprefer = 0;
            }
            if (!see('}')) {    /* catches errors too */
                throw new RegexException("Invalid syntax for {m,n} quantifier.");
            }
            lex.next();
            break;

        default:        /* no quantifier */
            m = 1;
            n = 1;
            qprefer = 0;
            break;
        }

    /* annoying special case:  {0} or {0,0} cancels everything */
        if (m == 0 && n == 0) {
            if (atomtype == '(') {
                assert subno == subs.size() - 1;
                subs.remove(subs.size() - 1);
            }
            delsub(nfa, lp, rp);
            nfa.emptyarc(lp, rp);
            return;
        }

    /* if not a messy case, avoid hard part */
        assert !messy(top.flags);
        f = top.flags | qprefer | ((atom != null) ? atom.flags : 0);
        if (atomtype != '(' && atomtype != BACKREF && !messy(up(f))) {
            if (!(m == 1 && n == 1)) {
                repeat(lp, rp, m, n);
            }
            top.flags = f;
            return;
        }

    /*
     * hard part:  something messy
     * That is, capturing parens, back reference, short/long clash, or
     * an atom with substructure containing one of those.
     */

    /* now we'll need a subre for the contents even if they're boring */
        if (atom == null) {
            atom = new Subre('=', 0, lp, rp);
        }

    /*
     * prepare a general-purpose state skeleton
     *
     *    --. [s] ---prefix--. [begin] ---atom--. [end] ----rest--. [rp]
     *   /                                            /
     * [lp] ---. [s2] ----bypass---------------------
     *
     * where bypass is an empty, and prefix is some repetitions of atom
     */
        s = nfa.newstate();     /* first, new endpoints for the atom */
        s2 = nfa.newstate();
        nfa.moveouts(lp, s);
        nfa.moveins(rp, s2);
        atom.begin = s;
        atom.end = s2;
        s = nfa.newstate();     /* and spots for prefix and bypass */
        s2 = nfa.newstate();
        nfa.emptyarc(lp, s);
        nfa.emptyarc(lp, s2);

    /* break remaining subRE into x{...} and what follows */
        t = new Subre('.', Subre.combine(qprefer, atom.flags), lp, rp);
        t.left = atom;

        final Subre target = t;
        atomp = new AtomSetter() {

            @Override
            public void set(Subre s) {
                target.left = s;
            }
        };

    /* here we should recurse... but we must postpone that to the end */

    /* split top into prefix and remaining */
        assert top.op == '=' && top.left == null && top.right == null;
        top.left = new Subre('=', top.flags, top.begin, lp);
        top.op = '.';
        top.right = t;

    /* if it's a backref, now is the time to replicate the subNFA */
        if (atomtype == BACKREF) {
            assert atom.begin.nouts == 1; /* just the EMPTY */
            delsub(nfa, atom.begin, atom.end);
            assert subs.get(subno - 1) != null;
        /* and here's why the recursion got postponed:  it must */
        /* wait until the skeleton is filled in, because it may */
        /* hit a backref that wants to copy the filled-in skeleton */
            nfa.dupnfa(subs.get(subno - 1).begin, subs.get(subno - 1).end,
                    atom.begin, atom.end);
        }

    /* it's quantifier time; first, turn x{0,...} into x{1,...}|empty */
        if (m == 0) {
            nfa.emptyarc(s2, atom.end);     /* the bypass */
            assert Subre.pref(qprefer) != 0;
            f = Subre.combine(qprefer, atom.flags);
            t = new Subre('|', f, lp, atom.end);
            t.left = atom;
            t.right = new Subre('|', Subre.pref(f), s2, atom.end);
            t.right.left = new Subre('=', 0, s2, atom.end);

            atomp.set(t);
            final Subre target2 = t;
            atomp = new AtomSetter() {
                @Override
                public void set(Subre s) {
                    target2.left = s;
                }
            };
            m = 1;
        }

    /* deal with the rest of the quantifier */
        if (atomtype == BACKREF) {
        /* special case:  backrefs have internal quantifiers */
            nfa.emptyarc(s, atom.begin);    /* empty prefix */
        /* just stuff everything into atom */
            repeat(atom.begin, atom.end, m, n);
            atom.min = (short)m;
            atom.max = (short)n;
            atom.flags |= Subre.combine(qprefer, atom.flags);
        } else if (m == 1 && n == 1) {
        /* no/vacuous quantifier:  done */
            nfa.emptyarc(s, atom.begin);    /* empty prefix */
        } else {
        /* turn x{m,n} into x{m-1,n-1}x, with capturing */
        /*  parens in only second x */
            nfa.dupnfa(atom.begin, atom.end, s, atom.begin);
            assert m >= 1 && m != INFINITY && n >= 1;
            repeat(s, atom.begin, m - 1, (n == INFINITY) ? n : n - 1);
            f = Subre.combine(qprefer, atom.flags);
            t = new Subre('.', f, s, atom.end); /* prefix and atom */
            t.left = new Subre('=', Subre.pref(f), s, atom.begin);
            t.right = atom;
            atomp.set(t);
        }

    /* and finally, look after that postponed recursion */
        t = top.right;
        if (!(see('|') || see(stopper) || see(EOS))) {
            t.right = parsebranch(stopper, type, atom.end, rp, true);
        } else {
            nfa.emptyarc(atom.end, rp);
            t.right = new Subre('=', 0, atom.end, rp);
        }
        assert see('|') || see(stopper) || see(EOS);
        t.flags |= Subre.combine(t.flags, t.right.flags);
        top.flags |= Subre.combine(top.flags, t.flags);
    }
private void parseqatom ( int stopper , int type , State lp , State rp , Subre top ) throws RegexException { State s ; /* temporaries for new states */ State s2 ; int m ; int n ; Subre atom ; /* atom's subtree */ Subre t ; boolean cap ; /* capturing parens? */ int pos ; /* positive lookahead? */ int subno ; /* capturing-parens or backref number */ int atomtype ; int qprefer ; /* quantifier short/long preference */ int f ; AtomSetter atomp ; /* initial bookkeeping */ atom = null ; assert lp . nouts == 0 ; /* must string new code */ assert rp . nins == 0 ; /*  between lp and rp */ subno = 0 ; /* just to shut lint up */ /* an atom or constraint... */ atomtype = nexttype ; switch ( atomtype ) { /* first, constraints, which end by returning */ case ' ' : nfa . newarc ( ' ' , ( short ) 1 , lp , rp ) ; if ( 0 != ( cflags & Flags . REG_NLANCH ) ) { nfa . newarc ( BEHIND , nlcolor , lp , rp ) ; } lex . next ( ) ; return ; case ' ' : nfa . newarc ( ' ' , ( short ) 1 , lp , rp ) ; if ( 0 != ( cflags & Flags . REG_NLANCH ) ) { nfa . newarc ( AHEAD , nlcolor , lp , rp ) ; } lex . next ( ) ; return ; case SBEGIN : nfa . newarc ( ' ' , ( short ) 1 , lp , rp ) ; /* BOL */ nfa . newarc ( ' ' , ( short ) 0 , lp , rp ) ; /* or BOS */ lex . next ( ) ; return ; case SEND : nfa . newarc ( ' ' , ( short ) 1 , lp , rp ) ; /* EOL */ nfa . newarc ( ' ' , ( short ) 0 , lp , rp ) ; /* or EOS */ lex . next ( ) ; return ; case ' ' : wordchrs ( ) ; /* does next() */ s = nfa . newstate ( ) ; nonword ( BEHIND , lp , s ) ; word ( AHEAD , s , rp ) ; return ; case ' ' : wordchrs ( ) ; /* does next() */ s = nfa . newstate ( ) ; word ( BEHIND , lp , s ) ; nonword ( AHEAD , s , rp ) ; return ; case WBDRY : wordchrs ( ) ; /* does next() */ s = nfa . newstate ( ) ; nonword ( BEHIND , lp , s ) ; word ( AHEAD , s , rp ) ; s = nfa . newstate ( ) ; word ( BEHIND , lp , s ) ; nonword ( AHEAD , s , rp ) ; return ; case NWBDRY : wordchrs ( ) ; /* does next() */ s = nfa . newstate ( ) ; word ( BEHIND , lp , s ) ; word ( AHEAD , s , rp ) ; s = nfa . newstate ( ) ; nonword ( BEHIND , lp , s ) ; nonword ( AHEAD , s , rp ) ; return ; case LACON : /* lookahead constraint */ pos = nextvalue ; lex . next ( ) ; s = nfa . newstate ( ) ; s2 = nfa . newstate ( ) ; parse ( ' ' , LACON , s , s2 ) ; // parse for side-effect. assert see ( ' ' ) ; lex . next ( ) ; n = newlacon ( s , s2 , pos ) ; nfa . newarc ( LACON , ( short ) n , lp , rp ) ; return ; /* then errors, to get them out of the way */ case ' ' : case ' ' : case ' ' : case ' ' : throw new RegexException ( "Pattern syntax error (*+?} misplaced)." ) ; /* then plain characters, and minor variants on that theme */ case ' ' : /* unbalanced paren */ if ( ( cflags & Flags . REG_ADVANCED ) != Flags . REG_EXTENDED ) { throw new RegexException ( "Unbalanced parenthesis." ) ; } /* legal in EREs due to specification botch */ note ( Flags . REG_UPBOTCH ) ; /* fallthrough into case PLAIN */ case PLAIN : // look out for surrogates as ordinary chars. if ( nextvalue < Character . MAX_VALUE && Character . isHighSurrogate ( ( char ) nextvalue ) ) { char high = ( char ) nextvalue ; lex . next ( ) ; char low = ( char ) nextvalue ; int codepoint = Character . toCodePoint ( high , low ) ; onechr ( codepoint , lp , rp ) ; } else { onechr ( nextvalue , lp , rp ) ; } cm . okcolors ( nfa ) ; lex . next ( ) ; break ; case ' ' : if ( nextvalue == 1 ) { bracket ( lp , rp ) ; } else { cbracket ( lp , rp ) ; } assert see ( ' ' ) ; lex . next ( ) ; break ; case ' ' : cm . rainbow ( nfa , PLAIN , ( 0 != ( cflags & Flags . REG_NLSTOP ) ) ? nlcolor : Constants . COLORLESS , lp , rp ) ; lex . next ( ) ; break ; /* and finally the ugly stuff */ case ' ' : /* value flags as capturing or non */ if ( type == LACON ) { cap = false ; } else { cap = nextvalue != 0 ; } if ( cap ) { subno = subs . size ( ) + 1 ; // first subno is 1. /*
                 * This recurses via a call to parse just below.
                 * So, the size() just above has to reflect this new sub,
                 * even though we won't create the object until a little further
                 * down.
                 */ subs . add ( null ) ; } else { atomtype = PLAIN ; /* something that's not '(' */ } lex . next ( ) ; /* need new endpoints because tree will contain pointers */ s = nfa . newstate ( ) ; s2 = nfa . newstate ( ) ; nfa . emptyarc ( lp , s ) ; nfa . emptyarc ( s2 , rp ) ; atom = parse ( ' ' , PLAIN , s , s2 ) ; assert see ( ' ' ) ; lex . next ( ) ; if ( cap ) { // we can't assert anything about the size of 'subs', recursion may have added to it. // but we can check that nothing has used our slot. assert subs . get ( subno - 1 ) == null ; subs . set ( subno - 1 , atom ) ; t = new Subre ( ' ' , atom . flags | Subre . CAP , lp , rp ) ; t . subno = subno ; t . left = atom ; atom = t ; } /* postpone everything else pending possible {0} */ break ; case BACKREF : /* the Feature From The Black Lagoon */ if ( type == LACON ) { throw new RegexException ( "REG_ESUBREG" ) ; } if ( nextvalue > subs . size ( ) ) { throw new RegexException ( String . format ( "Backreference to %d out of range of defined subexpressions (%d)" , nextvalue , subs . size ( ) ) ) ; } if ( subs . get ( nextvalue - 1 ) == null ) { // \1 is first backref, living in slot 0. throw new RegexException ( String . format ( "Backreference to %d refers to non-capturing group." , nextvalue ) ) ; } assert nextvalue > 0 ; atom = new Subre ( ' ' , Subre . BACKR , lp , rp ) ; subno = nextvalue ; atom . subno = subno ; nfa . emptyarc ( lp , rp ) ; /* temporarily, so there's something */ lex . next ( ) ; break ; default : throw new RuntimeException ( "Impossible type in lex" ) ; } /* ...and an atom may be followed by a quantifier */ switch ( nexttype ) { case ' ' : m = 0 ; n = INFINITY ; qprefer = ( nextvalue != 0 ) ? Subre . LONGER : Subre . SHORTER ; lex . next ( ) ; break ; case ' ' : m = 1 ; n = INFINITY ; qprefer = ( nextvalue != 0 ) ? Subre . LONGER : Subre . SHORTER ; lex . next ( ) ; break ; case ' ' : m = 0 ; n = 1 ; qprefer = ( nextvalue != 0 ) ? Subre . LONGER : Subre . SHORTER ; lex . next ( ) ; break ; case ' ' : lex . next ( ) ; m = scannum ( ) ; if ( eat ( ' ' ) ) { if ( see ( DIGIT ) ) { n = scannum ( ) ; } else { n = INFINITY ; } if ( m > n ) { throw new RegexException ( "First quantity is larger than second quantity in {m,n} quantifier." ) ; } /* {m,n} exercises preference, even if it's {m,m} */ qprefer = ( nextvalue != 0 ) ? Subre . LONGER : Subre . SHORTER ; } else { n = m ; /* {m} passes operand's preference through */ qprefer = 0 ; } if ( ! see ( ' ' ) ) { /* catches errors too */ throw new RegexException ( "Invalid syntax for {m,n} quantifier." ) ; } lex . next ( ) ; break ; default : /* no quantifier */ m = 1 ; n = 1 ; qprefer = 0 ; break ; } /* annoying special case:  {0} or {0,0} cancels everything */ if ( m == 0 && n == 0 ) { if ( atomtype == ' ' ) { assert subno == subs . size ( ) - 1 ; subs . remove ( subs . size ( ) - 1 ) ; } delsub ( nfa , lp , rp ) ; nfa . emptyarc ( lp , rp ) ; return ; } /* if not a messy case, avoid hard part */ assert ! messy ( top . flags ) ; f = top . flags | qprefer | ( ( atom != null ) ? atom . flags : 0 ) ; if ( atomtype != ' ' && atomtype != BACKREF && ! messy ( up ( f ) ) ) { if ( ! ( m == 1 && n == 1 ) ) { repeat ( lp , rp , m , n ) ; } top . flags = f ; return ; } /*
     * hard part:  something messy
     * That is, capturing parens, back reference, short/long clash, or
     * an atom with substructure containing one of those.
     */ /* now we'll need a subre for the contents even if they're boring */ if ( atom == null ) { atom = new Subre ( ' ' , 0 , lp , rp ) ; } /*
     * prepare a general-purpose state skeleton
     *
     *    --. [s] ---prefix--. [begin] ---atom--. [end] ----rest--. [rp]
     *   /                                            /
     * [lp] ---. [s2] ----bypass---------------------
     *
     * where bypass is an empty, and prefix is some repetitions of atom
     */ s = nfa . newstate ( ) ; /* first, new endpoints for the atom */ s2 = nfa . newstate ( ) ; nfa . moveouts ( lp , s ) ; nfa . moveins ( rp , s2 ) ; atom . begin = s ; atom . end = s2 ; s = nfa . newstate ( ) ; /* and spots for prefix and bypass */ s2 = nfa . newstate ( ) ; nfa . emptyarc ( lp , s ) ; nfa . emptyarc ( lp , s2 ) ; /* break remaining subRE into x{...} and what follows */ t = new Subre ( ' ' , Subre . combine ( qprefer , atom . flags ) , lp , rp ) ; t . left = atom ; final Subre target = t ; atomp = new AtomSetter ( ) { @ Override public void set ( Subre s ) { target . left = s ; } } ; /* here we should recurse... but we must postpone that to the end */ /* split top into prefix and remaining */ assert top . op == ' ' && top . left == null && top . right == null ; top . left = new Subre ( ' ' , top . flags , top . begin , lp ) ; top . op = ' ' ; top . right = t ; /* if it's a backref, now is the time to replicate the subNFA */ if ( atomtype == BACKREF ) { assert atom . begin . nouts == 1 ; /* just the EMPTY */ delsub ( nfa , atom . begin , atom . end ) ; assert subs . get ( subno - 1 ) != null ; /* and here's why the recursion got postponed:  it must */ /* wait until the skeleton is filled in, because it may */ /* hit a backref that wants to copy the filled-in skeleton */ nfa . dupnfa ( subs . get ( subno - 1 ) . begin , subs . get ( subno - 1 ) . end , atom . begin , atom . end ) ; } /* it's quantifier time; first, turn x{0,...} into x{1,...}|empty */ if ( m == 0 ) { nfa . emptyarc ( s2 , atom . end ) ; /* the bypass */ assert Subre . pref ( qprefer ) != 0 ; f = Subre . combine ( qprefer , atom . flags ) ; t = new Subre ( ' ' , f , lp , atom . end ) ; t . left = atom ; t . right = new Subre ( ' ' , Subre . pref ( f ) , s2 , atom . end ) ; t . right . left = new Subre ( ' ' , 0 , s2 , atom . end ) ; atomp . set ( t ) ; final Subre target2 = t ; atomp = new AtomSetter ( ) { @ Override public void set ( Subre s ) { target2 . left = s ; } } ; m = 1 ; } /* deal with the rest of the quantifier */ if ( atomtype == BACKREF ) { /* special case:  backrefs have internal quantifiers */ nfa . emptyarc ( s , atom . begin ) ; /* empty prefix */ /* just stuff everything into atom */ repeat ( atom . begin , atom . end , m , n ) ; atom . min = ( short ) m ; atom . max = ( short ) n ; atom . flags |= Subre . combine ( qprefer , atom . flags ) ; } else if ( m == 1 && n == 1 ) { /* no/vacuous quantifier:  done */ nfa . emptyarc ( s , atom . begin ) ; /* empty prefix */ } else { /* turn x{m,n} into x{m-1,n-1}x, with capturing */ /*  parens in only second x */ nfa . dupnfa ( atom . begin , atom . end , s , atom . begin ) ; assert m >= 1 && m != INFINITY && n >= 1 ; repeat ( s , atom . begin , m - 1 , ( n == INFINITY ) ? n : n - 1 ) ; f = Subre . combine ( qprefer , atom . flags ) ; t = new Subre ( ' ' , f , s , atom . end ) ; /* prefix and atom */ t . left = new Subre ( ' ' , Subre . pref ( f ) , s , atom . begin ) ; t . right = atom ; atomp . set ( t ) ; } /* and finally, look after that postponed recursion */ t = top . right ; if ( ! ( see ( ' ' ) || see ( stopper ) || see ( EOS ) ) ) { t . right = parsebranch ( stopper , type , atom . end , rp , true ) ; } else { nfa . emptyarc ( atom . end , rp ) ; t . right = new Subre ( ' ' , 0 , atom . end , rp ) ; } assert see ( ' ' ) || see ( stopper ) || see ( EOS ) ; t . flags |= Subre . combine ( t . flags , t . right . flags ) ; top . flags |= Subre . combine ( top . flags , t . flags ) ; }
CHECKSTYLE:OFF
CHECKSTYLE : OFF