/*
 * 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"
#ifdef PERSIST
# include "js-persist.h"
#endif

/* 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 */
#ifdef PERSIST
        int8_t          flat;           /* flat  */
#endif

	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

        CompilerState   *compilerState;
	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);
#ifdef PERSIST
        /* persist methods */
        void    presave( JSContext *cx, JSScriptable *scope,
                        JSPersist::StringConstants& stringConstants,
                        JSPersist::NumberConstants& numberConstants );
        void    save( JSContext *cx, JSScriptable *scope, JSRandomWriter* out,
                        JSPersist::StringConstants& stringConstants,
                        JSPersist::NumberConstants& numberConstants );
        void    load( JSContext *cx, JSScriptable *scope, JSRandomReader* in,
                        jsString_t* strings, double* numbers );
#endif
} ;

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

