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);
    }
CHECKSTYLE:OFF