src/org/joni/Option.java
public static boolean isIgnoreCase(int option) {
    return (option & IGNORECASE) != 0;
}
src/org/joni/ast/CTypeNode.java
public final class CTypeNode extends Node {
    public final int ctype;
    public final boolean not;
    public final boolean asciiRange;

    public CTypeNode(int type, boolean not, boolean asciiRange) {
        super(CTYPE);
        this.ctype= type;
        this.not = not;
        this.asciiRange = asciiRange;
    }

    @Override
    public String getName() {
        return "Character Type";
    }

    @Override
    public String toString(int level) {
        StringBuilder value = new StringBuilder();
        value.append("\n  ctype: " + ctype);
        value.append(", not: " + not);
        value.append(", ascii: " + asciiRange);
        return value.toString();
    }

}
src/org/joni/BitStatus.java
public static int bsOnAt(int stats, int n) {
    if (n < BIT_STATUS_BITS_NUM) {
        stats |= (1 << n);
    } else {
        stats |= 1;
    }
    return stats;
}
src/org/joni/ast/Node.java
public abstract class Node implements NodeType {
    public Node parent;
    protected int type;

    Node(int type) {
        this.type = type;
    }

    public final int getType() {
        return type;
    }

    public final int getType2Bit() {
        return 1 << getType();
    }

    protected void setChild(Node tgt){
        // default definition
    }

    protected Node getChild(){
        // default definition
        return null;
    };

    public void replaceWith(Node with) {
        with.parent = parent;
        parent.setChild(with);
        parent = null;
    }

    public abstract String getName();
    protected abstract String toString(int level);

    public String getAddressName() {
        return getName() + ":0x" + Integer.toHexString(System.identityHashCode(this));
    }

    public final String toString() {
        StringBuilder s = new StringBuilder();
        s.append("<" + getAddressName() + " (" + (parent == null ? "NULL" : parent.getAddressName())  + ")>");
        return s + toString(0);
    }

    protected static String pad(Object value, int level) {
        if (value == null) return "NULL";

        StringBuilder pad = new StringBuilder("  ");
        for (int i=0; i<level; i++) pad.append(pad);

        return value.toString().replace("\n",  "\n" + pad);
    }

    public final boolean isSimple() {
        return (getType2Bit() & SIMPLE) != 0;
    }

    public static TopNode newTop(Node root) {
        return new TopNode(root);
    }

    public static final class TopNode extends Node {
        private Node root;

        TopNode(Node root) {
            super(-1);
            root.parent = this;
            setChild(root);
        }

        public Node getRoot() {
            return root;
        }

        @Override
        public void setChild(Node node) {
            node.parent = this;
            root = node;
        }

        @Override
        public Node getChild() {
            return root;
        }

        @Override
        public String getName() {
            return "ROOT";
        }

        @Override
        public String toString(int level) {
            return "\n" + pad(root, level + 1);
        }
    }
}
src/org/joni/ast/QuantifierNode.java
public static boolean isRepeatInfinite(int n) {
    return n == REPEAT_INFINITE;
}
src/org/joni/constants/internal/EncloseType.java
public interface EncloseType {
    final int MEMORY                = 1<<0;
    final int OPTION                = 1<<1;
    final int STOP_BACKTRACK        = 1<<2;
    final int CONDITION             = 1<<3;
    final int ABSENT                = 1<<4;

    final int ALLOWED_IN_LB         = MEMORY | OPTION;
    final int ALLOWED_IN_LB_NOT     = OPTION;
}
src/org/joni/BitStatus.java
public static int bsClear() {
    return 0;
}
src/org/joni/ast/AnchorNode.java
public final class AnchorNode extends Node {
    public final int type;
    public final boolean asciiRange;
    public Node target;
    public int charLength;

    public AnchorNode(int type, boolean asciiRange) {
        super(ANCHOR);
        this.type = type;
        charLength = -1;
        this.asciiRange = asciiRange;
    }

    public AnchorNode(int type) {
        this(type, false);
    }

    @Override
    protected void setChild(Node child) {
        target = child;
    }

    @Override
    protected Node getChild() {
        return target;
    }

    public void setTarget(Node tgt) {
        target = tgt;
        tgt.parent = this;
    }

    @Override
    public String getName() {
        return "Anchor";
    }

    @Override
    public String toString(int level) {
        StringBuilder value = new StringBuilder();
        value.append("\n  type: " + typeToString());
        value.append(", ascii: " + asciiRange);
        value.append("\n  target: " + pad(target, level + 1));
        return value.toString();
    }

    public String typeToString() {
        StringBuilder type = new StringBuilder();
        if (isType(AnchorType.BEGIN_BUF)) type.append("BEGIN_BUF ");
        if (isType(AnchorType.BEGIN_LINE)) type.append("BEGIN_LINE ");
        if (isType(AnchorType.BEGIN_POSITION)) type.append("BEGIN_POSITION ");
        if (isType(AnchorType.END_BUF)) type.append("END_BUF ");
        if (isType(AnchorType.SEMI_END_BUF)) type.append("SEMI_END_BUF ");
        if (isType(AnchorType.END_LINE)) type.append("END_LINE ");
        if (isType(AnchorType.WORD_BOUND)) type.append("WORD_BOUND ");
        if (isType(AnchorType.NOT_WORD_BOUND)) type.append("NOT_WORD_BOUND ");
        if (isType(AnchorType.WORD_BEGIN)) type.append("WORD_BEGIN ");
        if (isType(AnchorType.WORD_END)) type.append("WORD_END ");
        if (isType(AnchorType.PREC_READ)) type.append("PREC_READ ");
        if (isType(AnchorType.PREC_READ_NOT)) type.append("PREC_READ_NOT ");
        if (isType(AnchorType.LOOK_BEHIND)) type.append("LOOK_BEHIND ");
        if (isType(AnchorType.LOOK_BEHIND_NOT)) type.append("LOOK_BEHIND_NOT ");
        if (isType(AnchorType.ANYCHAR_STAR)) type.append("ANYCHAR_STAR ");
        if (isType(AnchorType.ANYCHAR_STAR_ML)) type.append("ANYCHAR_STAR_ML ");
        return type.toString();
    }

    private boolean isType(int type) {
        return (this.type & type) != 0;
    }

}
src/org/joni/ast/CallNode.java
public final class CallNode extends StateNode {
    public final byte[]name;
    public final int nameP;
    public final int nameEnd;

    public int groupNum;
    public EncloseNode target;
    public UnsetAddrList unsetAddrList;

    public CallNode(byte[]name, int nameP, int nameEnd, int gnum) {
        super(CALL);
        this.name = name;
        this.nameP = nameP;
        this.nameEnd = nameEnd;
        this.groupNum = gnum; /* call by number if gnum != 0 */
    }

    @Override
    protected void setChild(Node newChild) {
        target = (EncloseNode)newChild;
    }

    @Override
    protected Node getChild() {
        return target;
    }

    public void setTarget(EncloseNode tgt) {
        target = tgt;
        tgt.parent = this;
    }

    @Override
    public String getName() {
        return "Call";
    }

    @Override
    public String toString(int level) {
        StringBuilder value = new StringBuilder(super.toString(level));
        value.append("\n  name: " + new String(name, nameP, nameEnd - nameP));
        value.append(", groupNum: " + groupNum);
        value.append("\n  unsetAddrList: " + pad(unsetAddrList, level + 1));
        value.append("\n  target: " + pad(target.getAddressName(), level + 1));
        return value.toString();
    }

}
src/org/joni/constants/internal/AnchorType.java
public interface AnchorType {
    final int BEGIN_BUF         = (1<<0);
    final int BEGIN_LINE        = (1<<1);
    final int BEGIN_POSITION    = (1<<2);
    final int END_BUF           = (1<<3);
    final int SEMI_END_BUF      = (1<<4);
    final int END_LINE          = (1<<5);

    final int WORD_BOUND        = (1<<6);
    final int NOT_WORD_BOUND    = (1<<7);
    final int WORD_BEGIN        = (1<<8);
    final int WORD_END          = (1<<9);
    final int PREC_READ         = (1<<10);
    final int PREC_READ_NOT     = (1<<11);
    final int LOOK_BEHIND       = (1<<12);
    final int LOOK_BEHIND_NOT   = (1<<13);

    final int ANYCHAR_STAR      = (1<<14);   /* ".*" optimize info */
    final int ANYCHAR_STAR_ML   = (1<<15);   /* ".*" optimize info (multi-line) */

    final int ANYCHAR_STAR_MASK = (ANYCHAR_STAR | ANYCHAR_STAR_ML);
    final int END_BUF_MASK      = (END_BUF | SEMI_END_BUF);

    final int KEEP              = (1<<16);

    final int ALLOWED_IN_LB =     ( LOOK_BEHIND |
                                    LOOK_BEHIND_NOT |
                                    BEGIN_LINE |
                                    END_LINE |
                                    BEGIN_BUF |
                                    BEGIN_POSITION |
                                    KEEP |
                                    WORD_BOUND |
                                    NOT_WORD_BOUND |
                                    WORD_BEGIN |
                                    WORD_END );


    final int ALLOWED_IN_LB_NOT = ( LOOK_BEHIND |
                                    LOOK_BEHIND_NOT |
                                    BEGIN_LINE |
                                    END_LINE |
                                    BEGIN_BUF |
                                    BEGIN_POSITION |
                                    KEEP |
                                    WORD_BOUND |
                                    NOT_WORD_BOUND |
                                    WORD_BEGIN |
                                    WORD_END );
}
src/org/joni/ast/BackRefNode.java
public final class BackRefNode extends StateNode {
    public final int back[];
    public int backNum;
    public int nestLevel;

    private BackRefNode(int backNum, int[]backRefs, boolean byName, ScanEnvironment env) {
        super(BREF);
        this.backNum = backNum;
        if (byName) setNameRef();

        for (int i=0; i<backNum; i++) {
            if (backRefs[i] <= env.numMem && env.memNodes[backRefs[i]] == null) {
                setRecursion(); /* /...(\1).../ */
                break;
            }
        }
        back = backRefs;
    }

    public BackRefNode(int backNum, int[]backRefs, boolean byName, boolean existLevel, int nestLevel, ScanEnvironment env) {
        this(backNum, backRefs, byName, env);

        if (Config.USE_BACKREF_WITH_LEVEL && existLevel) {
            setNestLevel();
            this.nestLevel = nestLevel;
        }
    }

    public void renumber(int[]map) {
        if (!isNameRef()) throw new ValueException(ErrorMessages.NUMBERED_BACKREF_OR_CALL_NOT_ALLOWED);

        int oldNum = backNum;

        int pos = 0;
        for (int i=0; i<oldNum; i++) {
            int n = map[back[i]];
            if (n > 0) {
                back[pos] = n;
                pos++;
            }
        }
        backNum = pos;
    }

    @Override
    public String getName() {
        return "Back Ref";
    }

    @Override
    public String toString(int level) {
        StringBuilder sb = new StringBuilder(super.toString(level));
        sb.append("\n  backNum: " + backNum);
        String backs = "";
        for (int i=0; i<back.length; i++) backs += back[i] + ", ";
        sb.append("\n  back: " + backs);
        sb.append("\n  nextLevel: " + nestLevel);
        return sb.toString();
    }
}
src/org/joni/ast/ListNode.java
public final class ListNode extends Node {
    public Node value;
    public ListNode tail;

    private ListNode(Node value, ListNode tail, int type) {
        super(type);
        this.value = value;
        if (value != null) value.parent = this;
        this.tail = tail;
        if (tail != null) tail.parent = this;
    }

    public static ListNode newAlt(Node value, ListNode tail) {
        return new ListNode(value, tail, ALT);
    }

    public static ListNode newList(Node value, ListNode tail) {
        return new ListNode(value, tail, LIST);
    }

    public static ListNode listAdd(ListNode list, Node value) {
        ListNode n = newList(value, null);
        if (list != null) {
            while (list.tail != null) list = list.tail;
            list.setTail(n);
        }
        return n;
    }

    public void toListNode() {
        type = LIST;
    }

    @Override
    protected void setChild(Node child) {
        value = child;
    }

    @Override
    protected Node getChild() {
        return value;
    }

    public void setValue(Node value) {
        this.value = value;
        value.parent = this;
    }

    public void setTail(ListNode tail) {
        this.tail = tail;
    }

    @Override
    public String getName() {
        switch (type) {
        case ALT:
            return "Alt";
        case LIST:
            return "List";
        default:
            throw new InternalException(ErrorMessages.PARSER_BUG);
        }
    }

    @Override
    public String toString(int level) {
        StringBuilder sb = new StringBuilder();
        sb.append("\n  value: " + pad(value, level + 1));
        sb.append("\n  tail: " + (tail == null ? "NULL" : tail.toString()));
        return sb.toString();
    }
}
src/org/joni/Option.java
public static boolean isFindCondition(int option) {
    return (option & (FIND_LONGEST | FIND_NOT_EMPTY)) != 0;
}
src/org/joni/constants/internal/StackPopLevel.java
public interface StackPopLevel {
    final int FREE      = 0;
    final int MEM_START = 1;
    final int ALL       = 2;

}
src/org/joni/ast/ListNode.java
public static ListNode newList(Node value, ListNode tail) {
    return new ListNode(value, tail, LIST);
}
src/org/joni/Option.java
public static boolean isMultiline(int option) {
    return (option & MULTILINE) != 0;
}
src/org/joni/constants/internal/TargetInfo.java
public interface TargetInfo {
    final int ISNOT_EMPTY   = 0;
    final int IS_EMPTY      = 1;
    final int IS_EMPTY_MEM  = 2;
    final int IS_EMPTY_REC  = 3;
}
src/org/joni/ast/ListNode.java
public static ListNode newAlt(Node value, ListNode tail) {
    return new ListNode(value, tail, ALT);
}
src/org/joni/constants/internal/NodeType.java
public interface NodeType {
    /* node type */
    final int  STR        = 0;
    final int  CCLASS     = 1;
    final int  CTYPE      = 2;
    final int  CANY       = 3;
    final int  BREF       = 4;
    final int  QTFR       = 5;
    final int  ENCLOSE    = 6;
    final int  ANCHOR     = 7;
    final int  LIST       = 8;
    final int  ALT        = 9;
    final int  CALL       = 10;

    final int BIT_STR        = 1 << STR;
    final int BIT_CCLASS     = 1 << CCLASS;
    final int BIT_CTYPE      = 1 << CTYPE;
    final int BIT_CANY       = 1 << CANY;
    final int BIT_BREF       = 1 << BREF;
    final int BIT_QTFR       = 1 << QTFR;
    final int BIT_ENCLOSE    = 1 << ENCLOSE;
    final int BIT_ANCHOR     = 1 << ANCHOR;
    final int BIT_LIST       = 1 << LIST;
    final int BIT_ALT        = 1 << ALT;
    final int BIT_CALL       = 1 << CALL;

    /* allowed node types in look-behind */
    final int ALLOWED_IN_LB = ( BIT_LIST |
                                BIT_ALT |
                                BIT_STR |
                                BIT_CCLASS |
                                BIT_CTYPE |
                                BIT_CANY |
                                BIT_ANCHOR |
                                BIT_ENCLOSE |
                                BIT_QTFR |
                                BIT_CALL );

    final int SIMPLE =        ( BIT_STR |
                                BIT_CCLASS |
                                BIT_CTYPE |
                                BIT_CANY |
                                BIT_BREF);

}
src/org/joni/ast/QuantifierNode.java
public final class QuantifierNode extends StateNode {
    public static final int REPEAT_INFINITE = -1;
    public Node target;
    public int lower;
    public int upper;
    public boolean greedy;

    public int targetEmptyInfo;

    public Node headExact;
    public Node nextHeadExact;
    public boolean isRefered;           /* include called node. don't eliminate even if {0} */

    // USE_COMBINATION_EXPLOSION_CHECK
    public int  combExpCheckNum;        /* 1,2,3...: check,  0: no check  */

    public QuantifierNode(int lower, int upper, boolean byNumber) {
        super(QTFR);
        this.lower = lower;
        this.upper = upper;
        greedy = true;
        targetEmptyInfo = TargetInfo.ISNOT_EMPTY;

        if (byNumber) setByNumber();
    }

    @Override
    protected void setChild(Node child) {
        target = child;
    }

    @Override
    protected Node getChild() {
        return target;
    }

    public void setTarget(Node tgt) {
        target = tgt;
        tgt.parent = this;
    }

    @Override
    public String getName() {
        return "Quantifier";
    }

    @Override
    public String toString(int level) {
        StringBuilder value = new StringBuilder(super.toString(level));
        value.append("\n  lower: " + lower);
        value.append(", upper: " + upper);
        value.append(", greedy: " + greedy);
        value.append(", isRefered: " + isRefered);
        value.append(", targetEmptyInfo: " + targetEmptyInfo);
        value.append(", combExpCheckNum: " + combExpCheckNum);
        value.append("\n  headExact: " + pad(headExact, level + 1));
        value.append("\n  nextHeadExact: " + pad(nextHeadExact, level + 1));
        value.append("\n  target: " + pad(target, level + 1));
        return value.toString();
    }

    public boolean isAnyCharStar() {
        return greedy && isRepeatInfinite(upper) && target.getType() == CANY;
    }

    /* ?:0, *:1, +:2, ??:3, *?:4, +?:5 */
    protected int popularNum() {
        if (greedy) {
            if (lower == 0) {
                if (upper == 1) return 0;
                else if (isRepeatInfinite(upper)) return 1;
            } else if (lower == 1) {
                if (isRepeatInfinite(upper)) return 2;
            }
        } else {
            if (lower == 0) {
                if (upper == 1) return 3;
                else if (isRepeatInfinite(upper)) return 4;
            } else if (lower == 1) {
                if (isRepeatInfinite(upper)) return 5;
            }
        }
        return -1;
    }

    protected void copy(QuantifierNode other) {
        state = other.state;
        setTarget(other.target);
        other.target = null;
        lower = other.lower;
        upper = other.upper;
        greedy = other.greedy;
        targetEmptyInfo = other.targetEmptyInfo;
        headExact = other.headExact;
        nextHeadExact = other.nextHeadExact;
        isRefered = other.isRefered;
        combExpCheckNum = other.combExpCheckNum;
    }

    static enum ReduceType {
        ASIS,       /* as is */
        DEL,        /* delete parent */
        A,          /* to '*'    */
        AQ,         /* to '*?'   */
        QQ,         /* to '??'   */
        P_QQ,       /* to '+)??' */
    }

    static final ReduceType[][]REDUCE_TABLE = {
      {DEL,     A,      A,      QQ,     AQ,     ASIS}, /* '?'  */
      {DEL,     DEL,    DEL,    P_QQ,   P_QQ,   DEL},  /* '*'  */
      {A,       A,      DEL,    ASIS,   P_QQ,   DEL},  /* '+'  */
      {DEL,     AQ,     AQ,     DEL,    AQ,     AQ},   /* '??' */
      {DEL,     DEL,    DEL,    DEL,    DEL,    DEL},  /* '*?' */
      {ASIS,    ASIS,   ASIS,   AQ,     AQ,     DEL}   /* '+?' */
    };


    public void reduceNestedQuantifier(QuantifierNode other) {
        int pnum = popularNum();
        int cnum = other.popularNum();

        if (pnum < 0 || cnum < 0) return;

        switch(REDUCE_TABLE[cnum][pnum]) {
        case DEL:
            // no need to set the parent here...
            copy(other);
            break;

        case A:
            setTarget(other.target);
            lower = 0;
            upper = REPEAT_INFINITE;
            greedy = true;
            break;

        case AQ:
            setTarget(other.target);
            lower = 0;
            upper = REPEAT_INFINITE;
            greedy = false;
            break;

        case QQ:
            setTarget(other.target);
            lower = 0;
            upper = 1;
            greedy = false;
            break;

        case P_QQ:
            setTarget(other);
            lower = 0;
            upper = 1;
            greedy = false;
            other.lower = 1;
            other.upper = REPEAT_INFINITE;
            other.greedy = true;
            return;

        case ASIS:
            setTarget(other);
            return;
        }
        // ??? remove the parent from target ???
        other.target = null; // remove target from reduced quantifier
    }

    static final String PopularQStr[] = new String[] {"?", "*", "+", "??", "*?", "+?"};
    static final String ReduceQStr[] = new String[] {"", "", "*", "*?", "??", "+ and ??", "+? and ?"};

    public int setQuantifier(Node tgt, boolean group, ScanEnvironment env, byte[]bytes, int p, int end) {
        if (lower == 1 && upper == 1) {
            if (env.syntax.op3OptionECMAScript()) {
                setTarget(tgt);
            }
            return 1;
        }

        switch(tgt.getType()) {

        case STR:
            if (!group) {
                StringNode sn = (StringNode)tgt;
                if (sn.canBeSplit(env.enc)) {
                    StringNode n = sn.splitLastChar(env.enc);
                    if (n != null) {
                        setTarget(n);
                        return 2;
                    }
                }
            }
            break;

        case QTFR:
            /* check redundant double repeat. */
            /* verbose warn (?:.?)? etc... but not warn (.?)? etc... */
            QuantifierNode qnt = (QuantifierNode)tgt;
            int nestQNum = popularNum();
            int targetQNum = qnt.popularNum();

            if (Config.USE_WARNING_REDUNDANT_NESTED_REPEAT_OPERATOR) {
                if (nestQNum >= 0 && targetQNum >= 0 && env.syntax.warnReduntantNestedRepeat()) {
                    switch(REDUCE_TABLE[targetQNum][nestQNum]) {
                    case ASIS:
                        break;
                    case DEL:
                        env.warnings.warn("regular expression has redundant nested repeat operator " + PopularQStr[targetQNum] + " /" + new String(bytes, p, end) + "/");
                        break;
                    default:
                        env.warnings.warn("nested repeat operator '" + PopularQStr[targetQNum] + "' and '" + PopularQStr[nestQNum] +
                                "' was replaced with '" + ReduceQStr[REDUCE_TABLE[targetQNum][nestQNum].ordinal()] + "' in regular expression " + "/" + new String(bytes, p, end) + "/");
                    }
                }
            } // USE_WARNING_REDUNDANT_NESTED_REPEAT_OPERATOR

            if (targetQNum >= 0) {
                if (nestQNum >= 0) {
                    reduceNestedQuantifier(qnt);
                    return 0;
                } else if (targetQNum == 1 || targetQNum == 2) { /* * or + */
                    /* (?:a*){n,m}, (?:a+){n,m} => (?:a*){n,n}, (?:a+){n,n} */
                    if (!isRepeatInfinite(upper) && upper > 1 && greedy) {
                        upper = lower == 0 ? 1 : lower;
                    }
                }
            }

        default:
            break;
        }

        setTarget(tgt);
        return 0;
    }

    public static boolean isRepeatInfinite(int n) {
        return n == REPEAT_INFINITE;
    }
}
src/org/joni/ast/StringNode.java
public final class StringNode extends Node implements StringType {
    private static final int NODE_STR_MARGIN = 16;
    private static final int NODE_STR_BUF_SIZE = 24;
    public static final StringNode EMPTY = new StringNode(null, Integer.MAX_VALUE, Integer.MAX_VALUE);

    public byte[]bytes;
    public int p;
    public int end;
    public int flag;

    public StringNode(int size) {
        super(STR);
        this.bytes = new byte[size];
    }

    public StringNode() {
        this(NODE_STR_BUF_SIZE);
    }

    public static StringNode fromCodePoint(int code, Encoding enc) {
        StringNode str = new StringNode(Config.ENC_CODE_TO_MBC_MAXLEN);
        str.end = enc.codeToMbc(code, str.bytes, str.p);
        return str;
    }

    public StringNode(byte[]bytes, int p, int end) {
        super(STR);
        this.bytes = bytes;
        this.p = p;
        this.end = end;
        setShared();
    }

    /* Ensure there is ahead bytes available in node's buffer
     * (assumes that the node is not shared)
     */
    private void ensure(int ahead) {
        int len = (end - p) + ahead;
        if (len >= bytes.length) {
            byte[]tmp = new byte[len + NODE_STR_MARGIN];
            System.arraycopy(bytes, p, tmp, 0, end - p);
            bytes = tmp;
        }
    }

    /* COW and/or ensure there is ahead bytes available in node's buffer
     */
    private void modifyEnsure(int ahead) {
        if (isShared()) {
            int len = (end - p) + ahead;
            byte[]tmp = new byte[len + NODE_STR_MARGIN];
            System.arraycopy(bytes, p, tmp, 0, end - p);
            bytes = tmp;
            end = end - p;
            p = 0;
            clearShared();
        } else {
            ensure(ahead);
        }
    }

    @Override
    public String getName() {
        return "String";
    }

    public int length() {
        return end - p;
    }

    public int length(Encoding enc) {
        return enc.strLength(bytes, p, end);
    }

    public StringNode splitLastChar(Encoding enc) {
        StringNode n = null;
        if (end > p) {
            int prev = enc.prevCharHead(bytes, p, end, end);
            if (prev != -1 && prev > p) { /* can be split */
                n = new StringNode(bytes, prev, end);
                if (isRaw()) n.setRaw();
                end = prev;
            }
        }
        return n;
    }

    public boolean canBeSplit(Encoding enc) {
        if (end > p) {
            return enc.length(bytes, p, end) < (end - p);
        }
        return false;
    }

    public void set(byte[]bytes, int p, int end) {
        this.bytes = bytes;
        this.p = p;
        this.end = end;
        setShared();
    }

    public void catBytes(byte[]cat, int catP, int catEnd) {
        int len = catEnd - catP;
        modifyEnsure(len);
        System.arraycopy(cat, catP, bytes, end, len);
        end += len;
    }

    public void catByte(byte c) {
        modifyEnsure(1);
        bytes[end++] = c;
    }

    public void catCode(int code, Encoding enc) {
        modifyEnsure(Config.ENC_CODE_TO_MBC_MAXLEN);
        end += enc.codeToMbc(code, bytes, end);
    }

    public void setRaw() {
        flag |= NSTR_RAW;
    }

    public void clearRaw() {
        flag &= ~NSTR_RAW;
    }

    public boolean isRaw() {
        return (flag & NSTR_RAW) != 0;
    }

    public void setAmbig() {
        flag |= NSTR_AMBIG;
    }

    public void clearAmbig() {
        flag &= ~NSTR_AMBIG;
    }

    public boolean isAmbig() {
        return (flag & NSTR_AMBIG) != 0;
    }

    public void setDontGetOptInfo() {
        flag |= NSTR_DONT_GET_OPT_INFO;
    }

    public void clearDontGetOptInfo() {
        flag &= ~NSTR_DONT_GET_OPT_INFO;
    }

    public boolean isDontGetOptInfo() {
        return (flag & NSTR_DONT_GET_OPT_INFO) != 0;
    }

    public void setShared() {
        flag |= NSTR_SHARED;
    }

    public void clearShared() {
        flag &= ~NSTR_SHARED;
    }

    public boolean isShared() {
        return (flag & NSTR_SHARED) != 0;
    }

    public String flagsToString() {
        StringBuilder flags = new StringBuilder();
        if (isRaw()) flags.append("RAW ");
        if (isAmbig()) flags.append("AMBIG ");
        if (isDontGetOptInfo()) flags.append("DONT_GET_OPT_INFO ");
        if (isShared()) flags.append("SHARED ");
        return flags.toString();
    }

    @Override
    public String toString(int level) {
        StringBuilder sb = new StringBuilder();
        sb.append("\n  flags: " + flagsToString());
        sb.append("\n  bytes: '");
        for (int i=p; i<end; i++) {
            if ((bytes[i] & 0xff) >= 0x20 && (bytes[i] & 0xff) < 0x7f) {
                sb.append((char)bytes[i]);
            } else {
                sb.append(String.format("[0x%02x]", bytes[i]));
            }
        }
        sb.append("'");
        return sb.toString();
    }
}
src/org/joni/BitStatus.java
public static boolean bsAt(int stats, int n) {
    return (n < BIT_STATUS_BITS_NUM ? stats & (1 << n) : (stats & 1)) != 0;
}
src/org/joni/BitStatus.java
public static int bsAll() {
    return -1;
}
src/org/joni/Option.java
public static boolean isCaptureGroup(int option) {
    return (option & CAPTURE_GROUP) != 0;
}
src/org/joni/ast/EncloseNode.java
public final class EncloseNode extends StateNode implements EncloseType {
    public final int type;          // enclose type
    public int regNum;
    public int option;
    public Node target;             /* EncloseNode : ENCLOSE_MEMORY */
    public int callAddr;            // AbsAddrType
    public int minLength;           // OnigDistance
    public int maxLength;           // OnigDistance
    public int charLength;
    public int optCount;            // referenced count in optimize_node_left()
    public Node containingAnchor;   //

    // node_new_enclose / onig_node_new_enclose
    public EncloseNode(int type) {
        super(ENCLOSE);
        this.type = type;
        callAddr = -1;
    }

    public static EncloseNode newMemory(int option, boolean isNamed) {
        EncloseNode en = new EncloseNode(MEMORY);
        if (Config.USE_SUBEXP_CALL) en.option = option;
        if (isNamed) en.setNamedGroup();
        return en;
    }

    public static EncloseNode newOption(int option) {
        EncloseNode en = new EncloseNode(OPTION);
        en.option = option;
        return en;
    }

    @Override
    protected void setChild(Node child) {
        target = child;
    }

    @Override
    protected Node getChild() {
        return target;
    }

    public void setTarget(Node tgt) {
        target = tgt;
        tgt.parent = this;
    }

    @Override
    public String getName() {
        return "Enclose";
    }

    @Override
    public String toString(int level) {
        StringBuilder value = new StringBuilder(super.toString(level));
        value.append("\n  type: " + typeToString());
        value.append("\n  regNum: " + regNum);
        value.append(", option: " + Option.toString(option));
        value.append(", callAddr: " + callAddr);
        value.append(", minLength: " + minLength);
        value.append(", maxLength: " + maxLength);
        value.append(", charLength: " + charLength);
        value.append(", optCount: " + optCount);
        value.append("\n  target: " + pad(target, level + 1));
        return value.toString();
    }

    public String typeToString() {
        StringBuilder types = new StringBuilder();
        if (isStopBacktrack()) types.append("STOP_BACKTRACK ");
        if (isMemory()) types.append("MEMORY ");
        if (isOption()) types.append("OPTION ");
        if (isCondition()) types.append("CONDITION ");
        if (isAbsent()) types.append("ABSENT ");
        return types.toString();
    }

    public void setEncloseStatus(int flag) {
        state |= flag;
    }

    public void clearEncloseStatus(int flag) {
        state &= ~flag;
    }

    public boolean isMemory() {
        return (type & MEMORY) != 0;
    }

    public boolean isOption() {
        return (type & OPTION) != 0;
    }

    public boolean isCondition() {
        return (type & CONDITION) != 0;
    }

    public boolean isStopBacktrack() {
        return (type & STOP_BACKTRACK) != 0;
    }

    public boolean isAbsent() {
        return (type & ABSENT) != 0;
    }
}
src/org/joni/BitStatus.java
public static int bsOnAtSimple(int stats, int n) {
    if (n < BIT_STATUS_BITS_NUM) stats |= (1 << n);
    return stats;
}
src/org/joni/ast/CClassNode.java
public final class CClassNode extends Node {
    private static final int FLAG_NCCLASS_NOT = 1 << 0;

    private int flags;
    public final BitSet bs = new BitSet();  // conditional creation ?
    public CodeRangeBuffer mbuf;            /* multi-byte info or NULL */

    // node_new_cclass
    public CClassNode() {
        super(CCLASS);
    }

    public void clear() {
        bs.clear();
        flags = 0;
        mbuf = null;
    }

    @Override
    public String getName() {
        return "Character Class";
    }

    @Override
    public String toString(int level) {
        StringBuilder value = new StringBuilder();
        value.append("\n  flags: " + flagsToString());
        value.append("\n  bs: " + pad(bs, level + 1));
        value.append("\n  mbuf: " + pad(mbuf, level + 1));
        return value.toString();
    }

    public String flagsToString() {
        StringBuilder flags = new StringBuilder();
        if (isNot()) flags.append("NOT ");
        return flags.toString();
    }

    public boolean isEmpty() {
        return mbuf == null && bs.isEmpty();
    }

    void addCodeRangeToBuf(ScanEnvironment env, int from, int to) {
        addCodeRangeToBuf(env, from, to, true);
    }

    void addCodeRangeToBuf(ScanEnvironment env, int from, int to, boolean checkDup) {
        mbuf = CodeRangeBuffer.addCodeRangeToBuff(mbuf, env, from, to, checkDup);
    }

    // add_code_range, be aware of it returning null!
    public void addCodeRange(ScanEnvironment env, int from, int to) {
        addCodeRange(env, from, to, true);
    }

    public void addCodeRange(ScanEnvironment env, int from, int to, boolean checkDup) {
        mbuf = CodeRangeBuffer.addCodeRange(mbuf, env, from, to, checkDup);
    }

    void addAllMultiByteRange(ScanEnvironment env) {
        mbuf = CodeRangeBuffer.addAllMultiByteRange(env, mbuf);
    }

    public void clearNotFlag(ScanEnvironment env) {
        if (isNot()) {
            bs.invert();
            if (!env.enc.isSingleByte()) {
                mbuf = CodeRangeBuffer.notCodeRangeBuff(env, mbuf);
            }
            clearNot();
        }
    }

    public int isOneChar() {
        if (isNot()) return -1;
        int c = -1;
        if (mbuf != null) {
            int[]range = mbuf.getCodeRange();
            c = range[1];
            if (range[0] == 1 && c == range[2]) {
                if (c < BitSet.SINGLE_BYTE_SIZE && bs.at(c)) {
                    c = -1;
                }
            } else {
                return -1;
            }
        }

        for (int i = 0; i < BitSet.BITSET_SIZE; i++) {
            int b1 = bs.bits[i];
            if (b1 != 0) {
                if ((b1 & (b1 - 1)) == 0 && c == -1) {
                    c = BitSet.BITS_IN_ROOM * i + Integer.bitCount(b1 - 1);
                } else {
                    return -1;
                }
            }
        }
        return c;
    }

    // and_cclass
    public void and(CClassNode other, ScanEnvironment env) {
        boolean not1 = isNot();
        BitSet bsr1 = bs;
        CodeRangeBuffer buf1 = mbuf;
        boolean not2 = other.isNot();
        BitSet bsr2 = other.bs;
        CodeRangeBuffer buf2 = other.mbuf;

        if (not1) {
            BitSet bs1 = new BitSet();
            bsr1.invertTo(bs1);
            bsr1 = bs1;
        }

        if (not2) {
            BitSet bs2 = new BitSet();
            bsr2.invertTo(bs2);
            bsr2 = bs2;
        }

        bsr1.and(bsr2);

        if (bsr1 != bs) {
            bs.copy(bsr1);
            bsr1 = bs;
        }

        if (not1) {
            bs.invert();
        }

        CodeRangeBuffer pbuf = null;

        if (!env.enc.isSingleByte()) {
            if (not1 && not2) {
                pbuf = CodeRangeBuffer.orCodeRangeBuff(env, buf1, false, buf2, false);
            } else {
                pbuf = CodeRangeBuffer.andCodeRangeBuff(buf1, not1, buf2, not2, env);

                if (not1) {
                    pbuf = CodeRangeBuffer.notCodeRangeBuff(env, pbuf);
                }
            }
            mbuf = pbuf;
        }

    }

    // or_cclass
    public void or(CClassNode other, ScanEnvironment env) {
        boolean not1 = isNot();
        BitSet bsr1 = bs;
        CodeRangeBuffer buf1 = mbuf;
        boolean not2 = other.isNot();
        BitSet bsr2 = other.bs;
        CodeRangeBuffer buf2 = other.mbuf;

        if (not1) {
            BitSet bs1 = new BitSet();
            bsr1.invertTo(bs1);
            bsr1 = bs1;
        }

        if (not2) {
            BitSet bs2 = new BitSet();
            bsr2.invertTo(bs2);
            bsr2 = bs2;
        }

        bsr1.or(bsr2);

        if (bsr1 != bs) {
            bs.copy(bsr1);
            bsr1 = bs;
        }

        if (not1) {
            bs.invert();
        }

        if (!env.enc.isSingleByte()) {
            CodeRangeBuffer pbuf = null;
            if (not1 && not2) {
                pbuf = CodeRangeBuffer.andCodeRangeBuff(buf1, false, buf2, false, env);
            } else {
                pbuf = CodeRangeBuffer.orCodeRangeBuff(env, buf1, not1, buf2, not2);
                if (not1) {
                    pbuf = CodeRangeBuffer.notCodeRangeBuff(env, pbuf);
                }
            }
            mbuf = pbuf;
        }
    }

    // add_ctype_to_cc_by_range // Encoding out!
    public void addCTypeByRange(int ctype, boolean not, ScanEnvironment env, int sbOut, int mbr[]) {
        int n = mbr[0];
        int i;

        if (!not) {
            for (i=0; i<n; i++) {
                for (int j=CR_FROM(mbr, i); j<=CR_TO(mbr, i); j++) {
                    if (j >= sbOut) {
                        if (j > CR_FROM(mbr, i)) {
                            addCodeRangeToBuf(env, j, CR_TO(mbr, i));
                            i++;
                        }
                        // !goto sb_end!, remove duplication!
                        for (; i<n; i++) {
                            addCodeRangeToBuf(env, CR_FROM(mbr, i), CR_TO(mbr, i));
                        }
                        return;
                    }
                    bs.set(env, j);
                }
            }
            // !sb_end:!
            for (; i<n; i++) {
                addCodeRangeToBuf(env, CR_FROM(mbr, i), CR_TO(mbr, i));
            }

        } else {
            int prev = 0;

            for (i=0; i<n; i++) {
                for (int j=prev; j < CR_FROM(mbr, i); j++) {
                    if (j >= sbOut) {
                        // !goto sb_end2!, remove duplication
                        prev = sbOut;
                        for (i=0; i<n; i++) {
                            if (prev < CR_FROM(mbr, i)) addCodeRangeToBuf(env, prev, CR_FROM(mbr, i) - 1);
                            prev = CR_TO(mbr, i) + 1;
                        }
                        if (prev < 0x7fffffff/*!!!*/) addCodeRangeToBuf(env, prev, 0x7fffffff);
                        return;
                    }
                    bs.set(env, j);
                }
                prev = CR_TO(mbr, i) + 1;
            }

            for (int j=prev; j<sbOut; j++) {
                bs.set(env, j);
            }

            // !sb_end2:!
            prev = sbOut;
            for (i=0; i<n; i++) {
                if (prev < CR_FROM(mbr, i)) addCodeRangeToBuf(env, prev, CR_FROM(mbr, i) - 1);
                prev = CR_TO(mbr, i) + 1;
            }
            if (prev < 0x7fffffff/*!!!*/) addCodeRangeToBuf(env, prev, 0x7fffffff);
        }
    }

    private static int CR_FROM(int[] range, int i) {
        return range[(i * 2) + 1];
    }

    private static int CR_TO(int[] range, int i) {
        return range[(i * 2) + 2];
    }

    // add_ctype_to_cc
    public void addCType(int ctype, boolean not, boolean asciiRange, ScanEnvironment env, IntHolder sbOut) {
        Encoding enc = env.enc;
        int[]ranges = enc.ctypeCodeRange(ctype, sbOut);
        if (ranges != null) {
            if (asciiRange) {
                CClassNode ccWork = new CClassNode();
                ccWork.addCTypeByRange(ctype, not, env, sbOut.value, ranges);
                if (not) {
                    ccWork.addCodeRangeToBuf(env, 0x80, CodeRangeBuffer.LAST_CODE_POINT, false);
                } else {
                    CClassNode ccAscii = new CClassNode();
                    if (enc.minLength() > 1) {
                        ccAscii.addCodeRangeToBuf(env, 0x00, 0x7F);
                    } else {
                        ccAscii.bs.setRange(env, 0x00, 0x7F);
                    }
                    ccWork.and(ccAscii, env);
                }
                or(ccWork, env);
            } else {
                addCTypeByRange(ctype, not, env, sbOut.value, ranges);
            }
            return;
        }

        int maxCode = asciiRange ? 0x80 : BitSet.SINGLE_BYTE_SIZE;
        switch(ctype) {
        case CharacterType.ALPHA:
        case CharacterType.BLANK:
        case CharacterType.CNTRL:
        case CharacterType.DIGIT:
        case CharacterType.LOWER:
        case CharacterType.PUNCT:
        case CharacterType.SPACE:
        case CharacterType.UPPER:
        case CharacterType.XDIGIT:
        case CharacterType.ASCII:
        case CharacterType.ALNUM:
            if (not) {
                for (int c=0; c<BitSet.SINGLE_BYTE_SIZE; c++) {
                    if (!enc.isCodeCType(c, ctype)) bs.set(env, c);
                }
                addAllMultiByteRange(env);
            } else {
                for (int c=0; c<BitSet.SINGLE_BYTE_SIZE; c++) {
                    if (enc.isCodeCType(c, ctype)) bs.set(env, c);
                }
            }
            break;

        case CharacterType.GRAPH:
        case CharacterType.PRINT:
            if (not) {
                for (int c=0; c<BitSet.SINGLE_BYTE_SIZE; c++) {
                    if (!enc.isCodeCType(c, ctype) || c >= maxCode) bs.set(env, c);
                }
                if (asciiRange) addAllMultiByteRange(env);
            } else {
                for (int c=0; c<maxCode; c++) {
                    if (enc.isCodeCType(c, ctype)) bs.set(env, c);
                }
                if (!asciiRange) addAllMultiByteRange(env);
            }
            break;

        case CharacterType.WORD:
            if (!not) {
                for (int c=0; c<maxCode; c++) {
                    if (enc.isSbWord(c)) bs.set(env, c);
                }
                if (!asciiRange) addAllMultiByteRange(env);
            } else {
                for (int c=0; c<BitSet.SINGLE_BYTE_SIZE; c++) {
                    if (enc.codeToMbcLength(c) > 0 && /* check invalid code point */
                            !(enc.isWord(c) || c >= maxCode)) bs.set(env, c);
                }
                if (asciiRange) addAllMultiByteRange(env);
            }
            break;

        default:
            throw new InternalException(ErrorMessages.PARSER_BUG);
        } // switch
    }

    public static enum CCVALTYPE {
        SB,
        CODE_POINT,
        CLASS
    }

    public static enum CCSTATE {
        VALUE,
        RANGE,
        COMPLETE,
        START
    }

    public static final class CCStateArg {
        public int from;
        public int to;
        public boolean fromIsRaw;
        public boolean toIsRaw;
        public CCVALTYPE inType;
        public CCVALTYPE type;
        public CCSTATE state;
    }

    public void nextStateClass(CCStateArg arg, CClassNode ascCC, ScanEnvironment env) {
        if (arg.state == CCSTATE.RANGE) throw new SyntaxException(ErrorMessages.CHAR_CLASS_VALUE_AT_END_OF_RANGE);

        if (arg.state == CCSTATE.VALUE && arg.type != CCVALTYPE.CLASS) {
            if (arg.type == CCVALTYPE.SB) {
                bs.set(env, arg.from);
                if (ascCC != null) ascCC.bs.set(arg.from);
            } else if (arg.type == CCVALTYPE.CODE_POINT) {
                addCodeRange(env, arg.from, arg.from);
                if (ascCC != null) ascCC.addCodeRange(env, arg.from, arg.from, false);
            }
        }
        arg.state = CCSTATE.VALUE;
        arg.type = CCVALTYPE.CLASS;
    }

    public void nextStateValue(CCStateArg arg, CClassNode ascCc, ScanEnvironment env) {
        switch(arg.state) {
        case VALUE:
            if (arg.type == CCVALTYPE.SB) {
                bs.set(env, arg.from);
                if (ascCc != null) ascCc.bs.set(arg.from);
            } else if (arg.type == CCVALTYPE.CODE_POINT) {
                addCodeRange(env, arg.from, arg.from);
                if (ascCc != null) ascCc.addCodeRange(env, arg.from, arg.from, false);
            }
            break;

        case RANGE:
            if (arg.inType == arg.type) {
                if (arg.inType == CCVALTYPE.SB) {
                    if (arg.from > 0xff || arg.to > 0xff) throw new ValueException(ErrorMessages.ERR_INVALID_CODE_POINT_VALUE);

                    if (arg.from > arg.to) {
                        if (env.syntax.allowEmptyRangeInCC()) {
                            // goto ccs_range_end
                            arg.state = CCSTATE.COMPLETE;
                            break;
                        } else {
                            throw new ValueException(ErrorMessages.EMPTY_RANGE_IN_CHAR_CLASS);
                        }
                    }
                    bs.setRange(env, arg.from, arg.to);
                    if (ascCc != null) ascCc.bs.setRange(null, arg.from, arg.to);
                } else {
                    addCodeRange(env, arg.from, arg.to);
                    if (ascCc != null) ascCc.addCodeRange(env, arg.from, arg.to, false);
                }
            } else {
                if (arg.from > arg.to) {
                    if (env.syntax.allowEmptyRangeInCC()) {
                        // goto ccs_range_end
                        arg.state = CCSTATE.COMPLETE;
                        break;
                    } else {
                        throw new ValueException(ErrorMessages.EMPTY_RANGE_IN_CHAR_CLASS);
                    }
                }
                bs.setRange(env, arg.from, arg.to < 0xff ? arg.to : 0xff);
                addCodeRange(env, arg.from, arg.to);
                if (ascCc != null) {
                    ascCc.bs.setRange(null, arg.from, arg.to < 0xff ? arg.to : 0xff);
                    ascCc.addCodeRange(env, arg.from, arg.to, false);
                }
            }
            // ccs_range_end:
            arg.state = CCSTATE.COMPLETE;
            break;

        case COMPLETE:
        case START:
            arg.state = CCSTATE.VALUE;
            break;

        default:
            break;

        } // switch

        arg.fromIsRaw = arg.toIsRaw;
        arg.from = arg.to;
        arg.type = arg.inType;
    }

    // onig_is_code_in_cc_len
    boolean isCodeInCCLength(int encLength, int code) {
        boolean found;

        if (encLength > 1 || code >= BitSet.SINGLE_BYTE_SIZE) {
            if (mbuf == null) {
                found = false;
            } else {
                found = CodeRange.isInCodeRange(mbuf.getCodeRange(), code);
            }
        } else {
            found = bs.at(code);
        }

        if (isNot()) {
            return !found;
        } else {
            return found;
        }
    }

    // onig_is_code_in_cc
    public boolean isCodeInCC(Encoding enc, int code) {
        int len;
        if (enc.minLength() > 1) {
            len = 2;
        } else {
            len = enc.codeToMbcLength(code);
        }
        return isCodeInCCLength(len, code);
    }

    public void setNot() {
        flags |= FLAG_NCCLASS_NOT;
    }

    public void clearNot() {
        flags &= ~FLAG_NCCLASS_NOT;
    }

    public boolean isNot() {
        return (flags & FLAG_NCCLASS_NOT) != 0;
    }
}


Question: import static org.joni.BitStatus.bsAll;
import static org.joni.BitStatus.bsAt;
import static org.joni.BitStatus.bsClear;
import static org.joni.BitStatus.bsOnAt;
import static org.joni.BitStatus.bsOnAtSimple;
import static org.joni.Option.isCaptureGroup;
import static org.joni.Option.isFindCondition;
import static org.joni.Option.isIgnoreCase;
import static org.joni.Option.isMultiline;
import static org.joni.ast.ListNode.newAlt;
import static org.joni.ast.ListNode.newList;
import static org.joni.ast.QuantifierNode.isRepeatInfinite;
import java.util.IllegalFormatConversionException;
import org.jcodings.CaseFoldCodeItem;
import org.jcodings.ObjPtr;
import org.jcodings.Ptr;
import org.jcodings.constants.CharacterType;
import org.joni.ast.AnchorNode;
import org.joni.ast.BackRefNode;
import org.joni.ast.CClassNode;
import org.joni.ast.CTypeNode;
import org.joni.ast.CallNode;
import org.joni.ast.EncloseNode;
import org.joni.ast.ListNode;
import org.joni.ast.Node;
import org.joni.ast.QuantifierNode;
import org.joni.ast.StringNode;
import org.joni.constants.internal.AnchorType;
import org.joni.constants.internal.EncloseType;
import org.joni.constants.internal.NodeType;
import org.joni.constants.internal.StackPopLevel;
import org.joni.constants.internal.TargetInfo;
/*
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.joni;




final class Analyser extends Parser {

    protected Analyser(Regex regex, Syntax syntax, byte[]bytes, int p, int end, WarnCallback warnings) {
        super(regex, syntax, bytes, p, end, warnings);
    }

    protected final void compile() {
        if (Config.DEBUG) Config.log.println(encStringToString(bytes, getBegin(), getEnd()));
        reset();

        regex.numMem = 0;
        regex.numRepeat = 0;
        regex.numNullCheck = 0;
        //regex.repeatRangeAlloc = 0;
        regex.repeatRangeLo = null;
        regex.repeatRangeHi = null;
        regex.numCombExpCheck = 0;

        if (Config.USE_CEC) regex.numCombExpCheck = 0;


        Node root = parseRegexp(); // onig_parse_make_tree
        regex.numMem = env.numMem;

        if (Config.USE_NAMED_GROUP) {
            /* mixed use named group and no-named group */
            if (env.numNamed > 0 && syntax.captureOnlyNamedGroup() && !isCaptureGroup(regex.options)) {
                if (env.numNamed != env.numMem) {
                    root = disableNoNameGroupCapture(root);
                } else {
                    numberedRefCheck(root);
                }
            }
        } // USE_NAMED_GROUP

        if (Config.USE_NAMED_GROUP) {
            if (env.numCall > 0) {
                env.unsetAddrList = new UnsetAddrList(env.numCall);
                setupSubExpCall(root);
                // r != 0 ???
                subexpRecursiveCheckTrav(root);
                // r < 0 -< err, FOUND_CALLED_NODE = 1
                subexpInfRecursiveCheckTrav(root);
                // r != 0  recursion infinite ???
                regex.numCall = env.numCall;
            } else {
                regex.numCall = 0;
            }
        } // USE_NAMED_GROUP

        if (Config.DEBUG_PARSE_TREE && Config.DEBUG_PARSE_TREE_RAW) Config.log.println("<RAW TREE>\n" + root + "\n");

        Node.TopNode top = Node.newTop(root);
        setupTree(root, 0);
        root = top.getRoot();

        if (Config.DEBUG_PARSE_TREE) Config.log.println("<TREE>\n" + root + "\n");

        regex.captureHistory = env.captureHistory;
        regex.btMemStart = env.btMemStart;

        if (isFindCondition(regex.options)) {
            regex.btMemEnd = bsAll();
        } else {
            regex.btMemEnd = env.btMemEnd;
            regex.btMemEnd |= regex.captureHistory;
        }

        if (Config.USE_CEC) {
            if (env.backrefedMem == 0 || (Config.USE_SUBEXP_CALL && env.numCall == 0)) {
                setupCombExpCheck(root, 0);

                if (Config.USE_SUBEXP_CALL && env.hasRecursion) {
                    env.numCombExpCheck = 0;
                } else { // USE_SUBEXP_CALL
                    if (env.combExpMaxRegNum > 0) {
                        for (int i=1; i<env.combExpMaxRegNum; i++) {
Answer:                             if (bsAt(env.backrefedMem, i)) {