/*
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/NPL/

 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.

 * The Original Code is Rhino code, released
 * May 6, 1999.

 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
 * Rights Reserved.

 * Contributor(s): 

 * Alternatively, the contents of this file may be used under the
 * terms of the GNU Public License (the "GPL"), in which case the
 * provisions of the GPL are applicable instead of those above.
 * If you wish to allow use of your version of this file only
 * under the terms of the GPL and not to allow others to use your
 * version of this file under the NPL, indicate your decision by
 * deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL.  If you do not delete
 * the provisions above, a recipient may use your version of this
 * file under either the NPL or the GPL.
 */
#ifdef REGEXP
#ifndef _JS_NATIVE_REG_EXP_H_
#define _JS_NATIVE_REG_EXP_H_

#include "js-types.h"
#include "js-scriptableobject.h"
#include "js-function.h"
#include "js-regexpnew.h"
#include "js-regexpimpl.h"
#include "js-substring.h"

/* Classes */
class JSRegExpImpl;
class JSNativeRegExp: public JSScriptableObject, public JSFunction {
    friend class JSNativeRegExpCtor;
    friend class JSRegExpImpl;
    protected:
        class CompilerState {
            public:
                JSContext       *cx;
                JSScriptable    *scope;
                jsString_t      source;
                int             indexBegin;
                int             index;
                int             flags;
                int             parenCount;
                int             progLength;
                int8_t          *prog;

            public:
                CompilerState(jsString_t source, int flags, JSContext *cx,
                        JSScriptable *scope);
        } ;
        class MatchState {
            public:
                jsBool_t        anchoring;      /* true if multiline anchoring
                                                  ^/$ */
                int             pcend;          /* pc limit (fencepost) */
                int             cpbegin, cpend; /* cp base address and limit */
                int             start;          /* offset from cpbegin to start
                                                  at */
                int             skipped;        /* chars skipped anchoring this
                                                  r.e. */
                int8_t          flags;          /* pennants  */
                int             parenCount;     /* number of paren substring
                                                  matches */
                JSSubString     **maybeParens;  /* possible paren substring
                                                  pointers */
                int             maybeParensLength;
                JSSubString     **parens;       /* certain paren substring
                                                  matches */
                int             parensLength;
                JSScriptable    *scope;
                jsString_t      input;
        } ;
        class RENode;
        class GreedyState {
            public:
                MatchState *state;
                RENode *kid;
                RENode *next;
                RENode *stop;
                int kidCount;
                int maxKid;
        } ;
        class RENode: public JSRENew {
            friend class JSNativeRegExp;
            protected:
                int8_t          op;     /* packed r.e. op bytecode */
                int8_t          flags;  /* flags, see below */
                short           offset; /* bytecode offset */
                RENode          *next;  /* next in concatenation order */
                void            *kid;   /* first operand */
                int             kid2;   /* second operand */
                int             num;    /* could be a number */
#if defined(USE_UTF8) || defined(USE_SJIS)
                wchar_t         chr;    /* or a char */
#else
                jsChar_t        chr;    /* or a char */
#endif
                short           min, max; /* or a range */
                short           kidlen; /* length of string at kid, in chars */
                short           bmsize; /* bitmap size, based on max char
                                          code */
                jsString_t      s;      /* if null, use state.source */
                int8_t          *bitmap;/* cclass bitmap */

            public:
                static  const int
                        ANCHORED = 0x01,        /* anchored at the front */
                        SINGLE   = 0x02,        /* matches a single char */
                        NONEMPTY = 0x04,        /* does not match empty
                                                  string */
                        ISNEXT  = 0x08,         /* ren is next after at least
                                                   one node */
                        GOODNEXT = 0x10,        /* ren.next is a tree-like edge
                                                  in the graph */
                        ISJOIN  = 0x20,         /* ren is a join point in the
                                                  graph */
                        REALLOK = 0x40,         /* REOP_FLAT owns tempPool
                                                  space to realloc */
                        MINIMAL = 0x80;         /* un-greedy matching for
                                                  ? * + {} */

            protected:
                void    calcBMSize(JSContext *cx, JSScriptable *scope,
                                jsString_t s, int index, int cp2,
                                jsBool_t fold);
                void    matchBit(int c, int fill);
                void    checkRange(int lastc, int fill);

            public:
                RENode(CompilerState *state, int8_t op, void *kid);

                void    buildBitmap(JSContext *cx, JSNativeRegExp *regExp,
                                MatchState *state, jsString_t s, jsBool_t fold);
        } ;
        friend class RENode;

    protected:
        static  const jsBool_t  debug = FALSE;

        static  const int8_t
                        REOP_EMPTY      = 0,    /* match rest of input against
                                                  rest of r.e. */
                        REOP_ALT        = 1,    /* alternative subexpressions in
                                                  kid and next */
                        REOP_BOL        = 2,    /* beginning of input (or line
                                                  if multiline) */
                        REOP_EOL        = 3,    /* end of input (or line if
                                                  multiline) */
                        REOP_WBDRY      = 4,    /* match "" at word boundary */
                        REOP_WNONBDRY   = 5,    /* match "" at word
                                                   non-boundary */
                        REOP_QUANT      = 6,    /* quantified atom: atom{1,2} */
                        REOP_STAR       = 7,    /* zero or more occurrences of
                                                  kid */
                        REOP_PLUS       = 8,    /* one or more occurrences of
                                                  kid */
                        REOP_OPT        = 9,    /* optional subexpression in
                                                  kid */
                        REOP_LPAREN     = 10,   /* left paren int8_tcode: kid is
                                                  u.num'th sub-regexp */
                        REOP_RPAREN     = 11,   /* right paren int8_tcode */
                        REOP_DOT        = 12,   /* stands for any character */
                        REOP_CCLASS     = 13,   /* character class: [a-f] */
                        REOP_DIGIT      = 14,   /* match a digit char: [0-9] */
                        REOP_NONDIGIT   = 15,   /* match a non-digit char:
                                                  [^0-9] */
                        REOP_ALNUM      = 16,   /* match an alphanumeric char:
                                                  [0-9a-z_A-Z] */
                        REOP_NONALNUM   = 17,   /* match a non-alphanumeric
                                                  char: [^0-9a-z_A-Z] */
                        REOP_SPACE      = 18,   /* match a whitespace char */
                        REOP_NONSPACE   = 19,   /* match a non-whitespace
                                                  char */
                        REOP_BACKREF    = 20,   /* back-reference (e.g., \1) to
                                                 a parenthetical */
                        REOP_FLAT       = 21,   /* match a flat string */
                        REOP_FLAT1      = 22,   /* match a single char */
                        REOP_JUMP       = 23,   /* for deoptimized closure
                                                loops */
                        REOP_DOTSTAR    = 24,   /* optimize .* to use a single
                                                opcode */
                        REOP_ANCHOR     = 25,   /* like .* but skips left
                                                  context to unanchored r.e. */
                        REOP_EOLONLY    = 26,   /* $ not preceded by any
                                                pattern */
                        REOP_UCFLAT     = 27,   /* flat Unicode string; len
                                                  immediate counts chars */
                        REOP_UCFLAT1    = 28,   /* single Unicode char */
                        REOP_UCCLASS    = 29,   /* Unicode character class,
                                                  vector of chars to match */
                        REOP_NUCCLASS   = 30,   /* negated Unicode character
                                                  class */
                        REOP_BACKREFi   = 31,   /* case-independent
                                                  REOP_BACKREF */
                        REOP_FLATi      = 32,   /* case-independent
                                                  REOP_FLAT */
                        REOP_FLAT1i     = 33,   /* case-independent
                                                  REOP_FLAT1 */
                        REOP_UCFLATi    = 34,   /* case-independent
                                                  REOP_UCFLAT */
                        REOP_UCFLAT1i   = 35,   /* case-independent
                                                  REOP_UCFLAT1 */
                        REOP_ANCHOR1    = 36,   /* first-char discriminating
                                                  REOP_ANCHOR */
                        REOP_NCCLASS    = 37,   /* negated 8-bit character
                                                  class */
                        REOP_DOTSTARMIN = 38, /* ungreedy version of
                                                  REOP_DOTSTAR */
                        REOP_LPARENNON  = 39, /* non-capturing version of
                                                  REOP_LPAREN */
                        REOP_RPARENNON  = 40, /* non-capturing version of
                                                  REOP_RPAREN */
                        REOP_ASSERT     = 41,   /* zero width positive
                                                  lookahead assertion */
                        REOP_ASSERT_NOT = 42, /* zero width negative
                                                  lookahead assertion */
                        REOP_END        = 43;

        /* maximum length of FLAT string */
        static  const int REOP_FLATLEN_MAX = 255;

        static  const jsString_t        metachars;
        static  const jsString_t        closurechars;

        static  int             level;
        static  jsString_t      *reopname;
        static  int             reopnameLength;

        jsString_t      source;         /* locked source string, sans // */
        int             lastIndex;      /* index after last match,
                                                for //g iterator */
        int             parenCount;     /* number of parenthesized
                                          submatches */
        int8_t          flags;          /* flags  */
        int8_t          *program;       /* regular expression bytecode */

        RENode          *ren;

        MatchState      *state;

    public:
        static  const jsString_t        className;

        static  const int JS_BITS_PER_BYTE      = 8;

        static  const int GLOB = 0x1;       // 'g' flag: global
        static  const int FOLD = 0x2;       // 'i' flag: fold
        static  const int MULTILINE = 0x4;  // 'm' flag: multiline

        JSZone          zone;

    protected:
        /* JS methods */
        static  jsVal_t exec(JSContext *cx, JSScriptable *thisObj, int numArgs,
                                jsVal_t *args, JSFunction *funObj);
        static  jsVal_t test(JSContext *cx, JSScriptable *thisObj, int numArgs,
                                jsVal_t *args, JSFunction *funObj);
        static  jsVal_t compile(JSContext *cx, JSScriptable *thisVal,
                                int numArgs, jsVal_t *args,
                                JSFunction *funObj);

        static  JSRegExpImpl *
                        getImpl(JSContext *cx);
        static  jsVal_t execSub(JSContext *cx, JSScriptable *thisObj,
                                int numArgs, jsVal_t *args,
                                JSScriptable *scopeObj, jsBool_t test,
                                JSFunction *funObj);
        static  jsChar_t
                        getEscape(JSContext *cx, jsChar_t c);
        static  jsBool_t
                        isWord(jsChar_t c);
#if defined(USE_UTF8) || defined(USE_SJIS)
        static  jsBool_t
                        matchChar(int flags, wchar_t c, wchar_t c2);
#else
        static  jsBool_t
                        matchChar(int flags, jsChar_t c, jsChar_t c2);
#endif

        jsString_t
                getPrintableString(JSContext *cx, jsString_t str);
        void    dumpRegExp(JSContext *cx, CompilerState *state, RENode *ren);
        void    fixNext(JSContext *cx, CompilerState *state, RENode *ren1,
                        RENode *ren2, RENode *oldnext);
        void    setNext(JSContext *cx, CompilerState *state, RENode *ren1,
                        RENode *ren2);
        RENode  *parseRegExp(JSContext *cx, CompilerState *state);
        RENode  *parseAltern(JSContext *cx, CompilerState *state);
        RENode  *parseItem(JSContext *cx, CompilerState *state);
        RENode  *parseQuantAtom(JSContext *cx, CompilerState *state);
        RENode  *parseAtom(JSContext *cx, CompilerState *state);
        int     doOctal(CompilerState *state);
        jsString_t
                tail(jsString_t a, int i);
        int     greedyRecurse(JSContext *cx, GreedyState *grState, int index,
                        int previousKid);
        int     matchGreedyKid(JSContext *cx, MatchState *state, RENode *ren,
                        RENode *stop, int kidCount, int index,
                        int previousKid);
        int     matchNonGreedyKid(JSContext *cx, MatchState *state,
                        RENode *ren, int kidCount, int maxKid, int index);
        int     matchRENodes(JSContext *cx, MatchState *state, RENode *ren,
                        RENode *stop, int index);
        int     matchRegExp(JSContext *cx, MatchState *state, RENode *ren,
                        int index);
        jsVal_t executeRegExp(JSContext *cx, JSScriptable *scopeObj,
                        jsString_t str, int *indexp, jsBool_t test);
        void    reportError(JSContext *cx, jsString_t msg, jsString_t arg,
                        CompilerState *state);
        void    init(JSContext *cx, JSScriptable *scope, jsString_t source,
                        jsString_t global, jsBool_t flat);

    public:
        static  void    classInitialize();
        static  void    defineClass(JSContext *cx, JSScriptable *scope);
        static  jsBool_t
                        isHex(jsChar_t c);
        static  int     unHex(jsChar_t c);
        static  jsBool_t
                        isDigit(jsChar_t c);
        static  int     unDigit(jsChar_t c);

        JSNativeRegExp(JSContext *cx, JSScriptable *scope, jsString_t source,
                jsString_t global, jsBool_t flat);
        JSNativeRegExp(JSContext *cx);
        virtual ~JSNativeRegExp() {
        }

        jsString_t
                toString(JSContext *cx);
        int64_t getLastIndex();
        void    setLastIndex(int64_t i);
        jsString_t
                getSource();
        jsBool_t
                getGlobal();
        jsBool_t
                getIgnoreCase();
        jsBool_t
                getMultiline();
        int8_t  getFlags();

        /* JS methods */
        jsVal_t toStringVal(JSContext *cx);

        /* instance of methods */
        jsBool_t
                isInstanceOf(const jsString_t className);

        /* scriptable methods */
        jsString_t
                getClassName(JSContext *cx);

        /* function methods */
        jsVal_t call(JSContext *cx, JSScriptable *scope, JSScriptable *thisObj,
                        int numArgs, jsVal_t *args);
        JSScriptable *
                construct(JSContext *cx, JSScriptable *scope, int numArgs,
                        jsVal_t *args);

        /* gc methods */
        void    mark(JSContext *cx);
        void    finalize(JSContext *cx);
} ;

#endif /* _JS_NATIVE_REG_EXP_H_ */
#endif /* REGEXP */

