/*
 * 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
/**
 * This class implements the RegExp native object.
 *
 */
/* Header files */
#include <stdio.h>
#include <ctype.h>
#include <limits.h>
#include "js-nativefunction.h"
#include "js-nativeglobal.h"
#include "js-scriptruntime.h"
#include "js-runtimeexception.h"
#include "js-exceptionhandler.h"
#include "js-objectadapter.h"
#include "js-tokenstream.h"
#include "js-stringbuffer.h"
#include "js-utils.h"

#include "js-evaluatorexception.h"

#if defined(USE_UTF8) || defined(USE_SJIS)
# include "js-stringruntime.h"
#endif
#include "js-nativeregexpctor.h"
#include "js-nativeregexp.h"

/* Class data */
const jsString_t	JSNativeRegExp::className = "RegExp";
const jsString_t	JSNativeRegExp::metachars = "|^${*+?().[\\";
const jsString_t	JSNativeRegExp::closurechars = "{*+?";
int			JSNativeRegExp::level = 0;
jsString_t		*JSNativeRegExp::reopname;		// assume NULL
int			JSNativeRegExp::reopnameLength;		// assume 0

/* Procedures */
JSNativeRegExp::CompilerState::CompilerState(jsString_t source, int flags,
					JSContext *cx, JSScriptable *scope)
{
    this->source = source;
    this->scope = scope;
    this->flags = flags;
    this->cx = cx;
    indexBegin = 0;
    index = 0;
    flags = 0;
    parenCount = 0;
    progLength = 0;
    prog = NULL;
}

void
JSNativeRegExp::RENode::calcBMSize(JSContext *cx, JSScriptable *scope,
				jsString_t s, int index, int cp2, jsBool_t fold)
{
    int maxc = 0;

    while (index < cp2) {
#if defined(USE_UTF8) || defined(USE_SJIS)
	wchar_t c;

	index += JSStringRuntime::mbtowc(&c, s + index,
					JSStringRuntime::_MB_CUR_MAX);
#else
	uint16_t /* jsChar_t */ c = s[index++];
#endif

	if (c == '\\') {
	    if (index + 5 <= cp2 && s[index] == 'u'
		&& JSNativeRegExp::isHex(s[index+1]) 
		&& JSNativeRegExp::isHex(s[index+2])
		&& JSNativeRegExp::isHex(s[index+3]) 
		&& JSNativeRegExp::isHex(s[index+4]))
	    {
		int x = (((((JSNativeRegExp::unHex(s[index+0]) << 4) +
				JSNativeRegExp::unHex(s[index+1])) << 4) +
				JSNativeRegExp::unHex(s[index+2])) << 4) +
				JSNativeRegExp::unHex(s[index+3]);

#if defined(USE_UTF8) || defined(USE_SJIS)
		c = (wchar_t /* jsChar_t */) x;
#else
		c = (uint16_t /* jsChar_t */) x;
#endif
		index += 5;
	    } else {
		/*
		 * Octal and hex escapes can't be > 255.  Skip this
		 * backslash and let the loop pass over the remaining
		 * escape sequence as if it were text to match.
		 */
		if (maxc < 255) maxc = 255;
		continue;
	    }
	}
	if (fold) {
	    /*
	     * Don't assume that lowercase are above uppercase, or
	     * that c is either even when c has upper and lowercase
	     * versions.
	     */
#if defined(USE_UTF8) || defined(USE_SJIS)
	    wchar_t c2;
#else
	    uint16_t /* jsChar_t */ c2;
#endif

	    if ((c2 = toupper(c)) > maxc) {
		maxc = c2;
	    }
	    if ((c2 = tolower(c2)) > maxc) {
		maxc = c2;
	    }
	}
	if (c > maxc)
	    maxc = c;
    }
#if defined(USE_UTF8) || defined(USE_SJIS)
    if (maxc > SHRT_MAX) {
	cx->exceptionHandler->throwEx(
                cx,
                JSNativeGlobal::constructError(cx, "SizeError",
			     JSScriptRuntime::getMessage(cx, "msg.bad.size"),
			     scope));
    }
#endif
    bmsize = (short)((maxc + JSNativeRegExp::JS_BITS_PER_BYTE) 
				    / JSNativeRegExp::JS_BITS_PER_BYTE);
}
void
JSNativeRegExp::RENode::matchBit(int c, int fill)
{
    int i = (c) >> 3;
    int8_t b = (int8_t) (c & 7);

    b = (int8_t) (1 << b);
    if (fill != 0) {
	bitmap[i] &= ~b;
    } else {
	bitmap[i] |= b;
    }
}
void
JSNativeRegExp::RENode::checkRange(int lastc, int fill)
{
    matchBit(lastc, fill);
    matchBit('-', fill);
}
void
JSNativeRegExp::RENode::buildBitmap(JSContext *cx, JSNativeRegExp *regExp,
				MatchState *state, jsString_t s, jsBool_t fold)
{
    int index = (int) kid;
    int end = kid2;
    int8_t fill = 0;
    int i,n,ocp;
    
    jsBool_t _not = FALSE;

    kid2 = 0;
    if (s[index] == '^') {
	_not = TRUE;
	kid2 = -1;
	index++;
    }
    
    calcBMSize(cx, state->scope, s, index, end, fold);
    bitmap = (int8_t *) JS_NewREObj(regExp, sizeof (int8_t) * bmsize);
    memset(bitmap, 0, sizeof (int8_t) * bmsize);
    if (_not) {
	fill = (int8_t) 0xff;
	for (i = 0; i < bmsize; i++)
	    bitmap[i] = (int8_t) 0xff;
	bitmap[0] = (int8_t) 0xfe;
    }
    int nchars = bmsize * JSNativeRegExp::JS_BITS_PER_BYTE;
    int lastc = nchars;
    jsBool_t inrange = FALSE;
    
    while (index < end) {
#if defined(USE_UTF8) || defined(USE_SJIS)
	wchar_t c;

	index += JSStringRuntime::mbtowc(&c, s + index,
					JSStringRuntime::_MB_CUR_MAX);
#else
	uint16_t /* jsChar_t */ c = s[index++];
#endif
	if (c == '\\') {
#if defined(USE_UTF8) || defined(USE_SJIS)
	    index += JSStringRuntime::mbtowc(&c, s + index,
					JSStringRuntime::_MB_CUR_MAX);
#else
	    c = s[index++];
#endif
	    switch (c) {
	    case 'b':
	    case 'f':
	    case 'n':
	    case 'r':
	    case 't':
	    case 'v':
		c = JSNativeRegExp::getEscape(cx, c);
		break;

	    case 'd':
		if (inrange) {
		    checkRange(lastc, fill);
		}
		lastc = nchars;
		for (c = '0'; c <= '9'; c++) {
		    matchBit(c, fill);
		}
		continue;

	    case 'D':
		if (inrange) {
		    checkRange(lastc, fill);
		}
		lastc = nchars;
		for (c = 0; c < '0'; c++) {
		    matchBit(c, fill);
		}
		for (c = '9' + 1; c < nchars; c++) {
		    matchBit(c, fill);
		}
		continue;

	    case 'w':
		if (inrange) {
		    checkRange(lastc, fill);
		}
		lastc = nchars;
		for (c = 0; c < nchars; c++) {
		    if (JSNativeRegExp::isWord(c)) {
			matchBit(c, fill);
		    }
		}
		continue;

	    case 'W':
		if (inrange) {
		    checkRange(lastc, fill);
		}
		lastc = nchars;
		for (c = 0; c < nchars; c++) {
		    if (!JSNativeRegExp::isWord(c)) {
			matchBit(c, fill);
		    }
		}
		continue;

	    case 's':
		if (inrange) {
		    checkRange(lastc, fill);
		}
		lastc = nchars;
		for (c = 0; c < nchars; c++) {
		    if (isspace(c)) {
			matchBit(c, fill);
		    }
		}
		continue;

	    case 'S':
		if (inrange) {
		    checkRange(lastc, fill);
		}
		lastc = nchars;
		for (c = 0; c < nchars; c++) {
		    if (!isspace(c)) {
			matchBit(c, fill);
		    }
		}
		continue;

	    case '0':
	    case '1':
	    case '2':
	    case '3':
	    case '4':
	    case '5':
	    case '6':
	    case '7':
		n = JSNativeRegExp::unDigit(c);
		ocp = index - 2;
		c = s[index];
		if ('0' <= c && c <= '7') {
		    index++;
		    n = 8 * n + JSNativeRegExp::unDigit(c);

		    c = s[index];
		    if ('0' <= c && c <= '7') {
			index++;
			i = 8 * n + JSNativeRegExp::unDigit(c);
			if (i <= 0377) {
			    n = i;
			} else {
			    index--;
			}
		    }
		}
#if defined(USE_UTF8) || defined(USE_SJIS)
		c = (wchar_t /* jsChar_t */) n;
#else
		c = (uint16_t /* jsChar_t */) n;
#endif
		break;

	    case 'x': {
		int length = strlen(s);

		ocp = index;
#if defined(USE_UTF8) || defined(USE_SJIS)
		if (index < length
		   && (index += JSStringRuntime::mbtowc(&c, s + index,
				    		JSStringRuntime::_MB_CUR_MAX),
			JSNativeRegExp::isHex(c))) {
		    n = JSNativeRegExp::unHex(c);
		    if (index < length) {
		    	index += JSStringRuntime::mbtowc(&c, s + index,
						JSStringRuntime::_MB_CUR_MAX);
			if (JSNativeRegExp::isHex(c)) {
			    n <<= 4;
			    n += JSNativeRegExp::unHex(c);
			}
		    }
		} else {
		    index = ocp;	/* \xZZ is xZZ (Perl does \0ZZ!) */
		    n = 'x';
		}
		c = (wchar_t /* jsChar_t */) n;
#else /* !(defined(USE_UTF8) || defined(USE_SJIS)) */
		if (index < length
		    && JSNativeRegExp::isHex(c = s[index++])) {
		    n = JSNativeRegExp::unHex(c);
		    if (index < length &&
			JSNativeRegExp::isHex(c = s[index++])) {
			n <<= 4;
			n += JSNativeRegExp::unHex(c);
		    }
		} else {
		    index = ocp;	/* \xZZ is xZZ (Perl does \0ZZ!) */
		    n = 'x';
		}
		c = (uint16_t /* jsChar_t */) n;
#endif /* defined(USE_UTF8) || defined(USE_SJIS) */
		break;
	    }

	    case 'u': {
		int length = strlen(s);

		if (length > index+3
			&& JSNativeRegExp::isHex(s[index+0])
			&& JSNativeRegExp::isHex(s[index+1]) 
			&& JSNativeRegExp::isHex(s[index+2])
			&& JSNativeRegExp::isHex(s[index+3])) {
		    n = (((((JSNativeRegExp::unHex(s[index+0]) << 4) +
			    JSNativeRegExp::unHex(s[index+1])) << 4) +
			    JSNativeRegExp::unHex(s[index+2])) << 4) +
			    JSNativeRegExp::unHex(s[index+3]);
#if defined(USE_UTF8) || defined(USE_SJIS)
		    c = (wchar_t /* jsChar_t */) n;
#else
		    c = (uint16_t /* jsChar_t */) n;
#endif
		    index += 4;
		}
		break;
 	    }

	    case 'c':
#if defined(USE_UTF8) || defined(USE_SJIS)
		index += JSStringRuntime::mbtowc(&c, s + index,
						JSStringRuntime::_MB_CUR_MAX);
		c = toupper(c);
		c = (wchar_t /* jsChar_t */) (c ^ 64); // JS_TOCTRL
#else
		c = s[index++];
		c = toupper(c);
		c = (uint16_t /* jsChar_t */) (c ^ 64); // JS_TOCTRL
#endif
		break;
	    }
	}

	if (inrange) {
	    if (lastc > c) {
		cx->exceptionHandler->throwEx(
                        cx,
			JSNativeGlobal::constructError(cx, "RangeError",
			     JSScriptRuntime::getMessage(cx, "msg.bad.range"),
			     state->scope));
	    }
	    inrange = FALSE;
	} else {
	    // Set lastc so we match just c's bit in the for loop.
	    lastc = c;

	    // [balance:
	    if (index + 1 < end && s[index] == '-' &&
		s[index+1] != ']') {
		index++;
		inrange = TRUE;
		continue;
	    }
	}

	// Match characters in the range [lastc, c].
	for (; lastc <= c; lastc++) {
	    matchBit(lastc, fill);
	    if (fold) {
		/*
		 * Must do both upper and lower for Turkish dotless i,
		 * Georgian, etc.
		 */
		jsChar_t foldc = toupper(lastc);
		matchBit(foldc, fill);
		foldc = tolower(foldc);
		matchBit(foldc, fill);
	    }
	}
	lastc = c;
    }
}

JSNativeRegExp::RENode::RENode(CompilerState *state, int8_t op, void *kid)
{
    this->op = op;
    this->kid = kid;

    flags = 0;
    offset = 0;
    next = NULL;
    kid2 = 0;
    num = 0;
    chr = '\0';
    min = max = 0;
    kidlen = 0;
    bmsize = 0;
    s = NULL;
    bitmap = NULL;
}

jsVal_t
JSNativeRegExp::compile(JSContext *cx, JSScriptable *thisVal, int numArgs,
			jsVal_t *args, JSFunction *funObj)
{
    JSNativeRegExp *thisObj = (JSNativeRegExp *) thisVal; // XXX check cast

    jsString_t s = numArgs == 0 ? (jsString_t) "" :
				JSScriptRuntime::toString(cx, args[0]);
    jsString_t global
		= numArgs > 1 ? JSScriptRuntime::toString(cx, args[1]) :
				(jsString_t) NULL;

    thisObj->init(cx, (JSNativeFunction *) funObj, s, global, FALSE);
    if (numArgs != 0) JS_DeleteZoneObj(cx, s);
    if (numArgs > 1) JS_DeleteZoneObj(cx, global);
    return JS_OBJECT_TO_VAL(thisObj);
}
JSRegExpImpl *
JSNativeRegExp::getImpl(JSContext *cx)
{
    return (JSRegExpImpl *) JSScriptRuntime::getRegExpProxy(cx);
}
jsVal_t
JSNativeRegExp::execSub(JSContext *cx, JSScriptable *thisObj,
			int numArgs, jsVal_t *args, JSScriptable *scopeObj,
			jsBool_t test, JSFunction *funObj)
{
    if (!thisObj->isInstanceOf(JSNativeRegExp::className)) {
	jsVal_t name;

	((JSNativeFunction *) funObj)->jsGet_name(cx, &name);
	cx->exceptionHandler->throwEx(
                cx,
		JSNativeGlobal::constructError(cx, "TypeError",
			JSScriptRuntime::getMessage(cx, "msg.incompat.call %s",
				JS_VAL_TO_STRING(name)),
			scopeObj));
    }
    JSNativeRegExp *re = (JSNativeRegExp *) thisObj;
    jsString_t str;

    if (numArgs == 0) {
	str = getImpl(cx)->input;
	if (str == NULL) {
	    cx->exceptionHandler->throwEx(
                    cx,
                    JSNativeGlobal::constructError(cx, "SyntaxError",
				JSScriptRuntime::getMessage(cx, 
					"msg.no.re.input.for %s",
					re->toString(cx)),
			thisObj));
	}
    } else {
	str = JSScriptRuntime::toString(cx, args[0]);
    }
    int i;

    if ((re->flags & GLOB) != 0) {
	i = re->lastIndex;
    } else {
	i = 0;
    }
    int indexp = i;
    jsVal_t rval = re->executeRegExp(cx, scopeObj, str, &indexp, test);

    if ((re->flags & GLOB) != 0) {
	re->lastIndex = (rval == JS_VAL_NULL) ? 0 : indexp;
    }
    if (numArgs != 0) {
	JS_DeleteZoneObj(cx, str);
    }

    return rval;
}
jsVal_t
JSNativeRegExp::exec(JSContext *cx, JSScriptable *thisObj,
			  int numArgs, jsVal_t *args, JSFunction *funObj)
{
    return execSub(cx, thisObj, numArgs, args, (JSNativeFunction *) funObj,
		FALSE, funObj);
}
jsVal_t
JSNativeRegExp::test(JSContext *cx, JSScriptable *thisObj,
		    int numArgs, jsVal_t *args, JSFunction *funObj)
{
    jsVal_t rval = execSub(cx, thisObj, numArgs, args,
			(JSNativeFunction *) funObj, TRUE, funObj);

    if (rval != JS_VAL_TRUE) {
	rval = JS_VAL_FALSE;
    }
    return rval;
}
jsChar_t
JSNativeRegExp::getEscape(JSContext *cx, jsChar_t c)
{
    switch (c) {
    case 'b':
	return '\b';
    case 'f':
	return '\f';
    case 'n':
	return '\n';
    case 'r':
	return '\r';
    case 't':
	return '\t';
    case 'v':
	return (jsChar_t) 11; // '\v' is not vtab in Java
    }
    cx->exceptionHandler->throwEx(cx, new (cx) JSRuntimeException());
    /* NOT REACHED */
    return '\0';
}
jsBool_t
JSNativeRegExp::isWord(jsChar_t c)
{
    return isalpha(c) || isDigit(c) || c == '_';
}
#if defined(USE_UTF8) || defined(USE_SJIS)
jsBool_t
JSNativeRegExp::matchChar(int flags, wchar_t c, wchar_t c2)
#else
jsBool_t
JSNativeRegExp::matchChar(int flags, jsChar_t c, jsChar_t c2)
#endif
{
    if (c == c2) {
	return TRUE;
    } else {
	if ((flags & FOLD) != 0) {
	    c = toupper(c);
	    c2 = toupper(c2);
	    return c == c2 || tolower(c) == tolower(c2);
	} else {
	    return FALSE;
	}
    }
}
jsString_t
JSNativeRegExp::getPrintableString(JSContext *cx, jsString_t str)
{
    if (debug) {
	int length = strlen(str);
	JSStringBuffer buf(cx, length);

#if defined(USE_UTF8) || defined(USE_SJIS)
	for (int i = 0; i < length; ) {
	    wchar_t c;

	    i += JSStringRuntime::mbtowc(&c, str + i,
					JSStringRuntime::_MB_CUR_MAX);
# ifdef USE_UTF8
	    if ((c < 0x20) || (c > 0x7F)) {
		if (c == '\n') {
		    buf.append("\\n");
		} else {
		    jsChar_t s[16];

		    sprintf(s, "%x", c);
		    buf.append("\\u");
		    buf.append(s);
		}
	    } else {
		buf.append((jsChar_t) c);
	    }
# else
    -> on other environment <-
# endif
	}
#else /* !(defined(USE_UTF8) || defined(USE_SJIS)) */
	for (int i = 0; i < length; i++) {
	    int c = str[i];

	    if ((c < 0x20) || (c > 0x7F)) {
		if (c == '\n') {
		    buf.append("\\n");
		} else {
		    jsChar_t s[16];

		    sprintf(s, "%x", c);
		    buf.append("\\u");
		    buf.append(s);
		}
	    } else {
		buf.append((jsChar_t) c);
	    }
	}
#endif /* defined(USE_UTF8) || defined(USE_SJIS) */
	return buf.offString();
    } else {
	return JS_NewZoneString(cx, "");
    }
}
void
JSNativeRegExp::dumpRegExp(JSContext *cx, CompilerState *state, RENode *ren)
{
    if (debug) {
	if (level == 0) {
	    printf("level offset  flags  description\n");
	}
	level++;
	do {
	    jsString_t source = ren->s != NULL ? ren->s : state->source;

	    printf("%d", level);
	    printf(" ");
	    printf("%d", ren->offset);
	    printf(" %s%s%s%s%s%s%s  %s",
		((ren->flags & RENode::ANCHORED) != 0 ? "A" : "-"),
		((ren->flags & RENode::SINGLE)   != 0 ? "S" : "-"),
		// F for full
		((ren->flags & RENode::NONEMPTY) != 0 ? "F" : "-"),
		// N for next
		((ren->flags & RENode::ISNEXT)   != 0 ? "N" : "-"),
		((ren->flags & RENode::GOODNEXT) != 0 ? "G" : "-"),
		((ren->flags & RENode::ISJOIN)   != 0 ? "J" : "-"),
		((ren->flags & RENode::MINIMAL)  != 0 ? "M" : "-"),
		reopname[ren->op]);

	    switch (ren->op) {
	    case REOP_ALT:
		printf(" ");
		printf("%d\n", ren->next->offset);
		dumpRegExp(cx, state, (RENode *) ren->kid);
		break;

	    case REOP_STAR:
	    case REOP_PLUS:
	    case REOP_OPT:
	    case REOP_ANCHOR1:
	    case REOP_ASSERT:
	    case REOP_ASSERT_NOT:
		printf("\n");
		dumpRegExp(cx, state, (RENode *) ren->kid);
		break;

	    case REOP_QUANT:
		printf(" next ");
		printf("%d", ren->next->offset);
		printf(" min ");
		printf("%d", ren->min);
		printf(" max ");
		printf("%d\n", ren->max);
		dumpRegExp(cx, state, (RENode *) ren->kid);
		break;

	    case REOP_LPAREN:
		printf(" num ");
		printf("%d\n", ren->num);
		dumpRegExp(cx, state, (RENode *) ren->kid);
		break;

 	    case REOP_LPARENNON:
		printf("\n");
		dumpRegExp(cx, state, (RENode *) ren->kid);
		break;

	    case REOP_BACKREF:
	    case REOP_RPAREN:
		printf(" num ");
		printf("%d\n", ren->num);
		break;

	    case REOP_CCLASS: {
		int index = (int) ren->kid;
		int index2 = ren->kid2;
		int len = index2 - index;

		printf(" [");

		jsString_t cclass
				= JS_Substring(cx, source, index, index + len);
		jsString_t s = getPrintableString(cx, cclass);
	
		printf(s);
		printf("]\n");
		JS_DeleteZoneObj(cx, s);
		JS_DeleteZoneObj(cx, cclass);
		break;
	    }

	    case REOP_FLAT: {
		int index = (int) ren->kid;
		int index2 = ren->kid2;
		int len = index2 - index;

		printf(" ");

		jsString_t flat = JS_Substring(cx, source, index, index + len);
		jsString_t s = getPrintableString(cx, flat);

		printf(" (");
		printf("%d", len);
		printf(")\n");
		JS_DeleteZoneObj(cx, s);
		JS_DeleteZoneObj(cx, flat);
		break;
	    }

	    case REOP_FLAT1:
		printf(" ");
		printf("%d", ren->chr);
		printf(" ('\\");
		printf("%o", ren->chr);
		printf("')\n");
		break;

	    case REOP_JUMP:
		printf(" ");
		printf("%d\n", ren->next->offset);
		break;

	    case REOP_UCFLAT: {
		int index = (int) ren->kid;
		int len = ren->kid2 - index;
		for (int i = 0; i < len; i++) {
		    printf("\\u%04x", source[index+i]);
		}
		printf("\n");
		break;
	    }

	    case REOP_UCFLAT1:
		printf("\\u%04x", ren->chr);
		printf("\n");
		break;

	    case REOP_UCCLASS: {
		int index = (int) ren->kid;
		int len = ren->kid2 - index;
		printf(" [");
		for (int i = 0; i < len; i++) {
		    printf("\\u%04x", source[index+i]);
		}
		printf("]\n");
		break;
	      }

	    default:
		printf("\n");
		break;
	    }

	    if ((ren->flags & RENode::GOODNEXT) == 0) {
		break;
	    }
	} while ((ren = ren->next) != NULL);
	level--;
    }
}
void
JSNativeRegExp::fixNext(JSContext *cx, CompilerState *state, RENode *ren1,
			RENode *ren2, RENode *oldnext)
{
    jsBool_t goodnext;
    RENode *next, *kid, *ren;

    goodnext = ren2 != NULL && (ren2->flags & RENode::ISNEXT) == 0;

    /*
     * Find the final node in a list of alternatives, or concatenations, or
     * even a concatenation of alternatives followed by non-alternatives (e.g.
     * ((x|y)z)w where ((x|y)z) is ren1 and w is ren2).
     */
    for (; (next = ren1->next) != NULL && next != oldnext; ren1 = next) {
	if (ren1->op == REOP_ALT) {
	    /* Find the end of this alternative's operand list. */
	    kid = (RENode *) ren1->kid;
	    if (kid->op == REOP_JUMP) {
		continue;
	    }
	    for (ren = kid; ren->next != NULL; ren = ren->next) {
		if (ren->op == REOP_ALT) {
		    cx->exceptionHandler->throwEx(
                            cx,
                            new (cx) JSRuntimeException("REOP_ALT not expected"));
		}
	    }

	    /* Append a jump node to all but the last alternative. */
	    ren->next = new (this) RENode(state, REOP_JUMP, NULL);
	    ren->next->flags |= RENode::ISNEXT;
	    ren->flags |= RENode::GOODNEXT;

	    /* Recur to fix all descendent nested alternatives. */
	    fixNext(cx, state, kid, ren2, oldnext);
	}
    }

    /*
     * Now ren1 points to the last alternative, or to the final node on a
     * concatenation list.  Set its next link to ren2, flagging a join point
     * if appropriate.
     */
    if (ren2 != NULL) {
	if ((ren2->flags & RENode::ISNEXT) == 0) {
	    ren2->flags |= RENode::ISNEXT;
	} else {
	    ren2->flags |= RENode::ISJOIN;
	}
    }
    ren1->next = ren2;
    if (goodnext) {
	ren1->flags |= RENode::GOODNEXT;
    }

    /*
     * The following ops have a kid subtree through which to recur.  Here is
     * where we fix the next links under the final ALT node's kid.
     */
    switch (ren1->op) {
    case REOP_ALT:
    case REOP_QUANT:
    case REOP_STAR:
    case REOP_PLUS:
    case REOP_OPT:
    case REOP_LPAREN:
    case REOP_LPARENNON:
    case REOP_ASSERT:
    case REOP_ASSERT_NOT:
	fixNext(cx, state, (RENode *) ren1->kid, ren2, oldnext);
	break;
    default:;
    }
}
void
JSNativeRegExp::setNext(JSContext *cx, CompilerState *state, RENode *ren1,
			RENode *ren2)
{
    fixNext(cx, state, ren1, ren2, NULL);
}
/*
 * Top-down regular expression grammar, based closely on Perl 4.
 *
 *  regexp:     altern		  A regular expression is one or more
 *	      altern '|' regexp       alternatives separated by vertical bar.
 */
JSNativeRegExp::RENode *
JSNativeRegExp::parseRegExp(JSContext *cx, CompilerState *state)
{
    RENode *ren = parseAltern(cx, state);

    if (ren == NULL) {
	return NULL;
    }
    jsString_t source = state->source;
    int index = state->index;
    int length = strlen(source);

    if (index < length && source[index] == '|') {
	RENode *kid = ren;

	ren = new (this) RENode(state, REOP_ALT, kid);
	if (ren == NULL) {
	    return NULL;
	}
	ren->flags = (int8_t) (kid->flags
				& (RENode::ANCHORED | RENode::NONEMPTY));

	RENode *ren1 = ren;

	do {
	    /* (balance: */
	    state->index = ++index;
	    if (index < length && (source[index] == '|'
		|| source[index] == ')')) {
		kid = new (this) RENode(state, REOP_EMPTY, NULL);
	    } else {
		kid = parseAltern(cx, state);
		index = state->index;
	    }
	    if (kid == NULL) {
		return NULL;
	    }
	    RENode *ren2 = new (this) RENode(state, REOP_ALT, kid);

	    if (ren2 == NULL) {
		return NULL;
	    }
	    ren1->next = ren2;
	    ren1->flags |= RENode::GOODNEXT;
	    ren2->flags = (int8_t) ((kid->flags
				    & (RENode::ANCHORED | RENode::NONEMPTY))
				    | RENode::ISNEXT);
	    ren1 = ren2;
	} while (index < length && source[index] == '|');
    }
    return ren;
}
/*
 *  altern:     item		    An alternative is one or more items,
 *	      item altern	     concatenated together.
 */
JSNativeRegExp::RENode *
JSNativeRegExp::parseAltern(JSContext *cx, CompilerState *state)
{
    RENode *ren = parseItem(cx, state);
    if (ren == NULL) {
	return NULL;
    }
    RENode *ren1 = ren;
    int flags = 0;
    jsString_t source = state->source;
    int index = state->index;
    jsChar_t c;
    /* (balance: */
    int length = strlen(source);

    while (index != length && (c = source[index]) != '|' && c != ')') {
	RENode *ren2 = parseItem(cx, state);

	if (ren2 == NULL) {
	    return NULL;
	}
	setNext(cx, state, ren1, ren2);
	flags |= ren2->flags;
	ren1 = ren2;
	index = state->index;
    }

    /*
    * Propagate NONEMPTY to the front of a concatenation list, so that the
    * first alternative in (^a|b) is considered non-empty.  The first node
    * in a list may match the empty string (as ^ does), but if the list is
    * non-empty, then the first node's NONEMPTY flag must be set.
    */
    ren->flags |= flags & RENode::NONEMPTY;
    return ren;
}
/*
 *  item:       assertion	       An item is either an assertion or
 *	      quantatom	       a quantified atom.
 *
 *  assertion:  '^'		     Assertions match beginning of string
 *				      (or line if the class static property
 *				      RegExp.multiline is true).
 *	      '$'		     End of string (or line if the class
 *				      static property RegExp.multiline is
 *				      true).
 *	      '\b'		    Word boundary (between \w and \W).
 *	      '\B'		    Word non-boundary.
 */
JSNativeRegExp::RENode *
JSNativeRegExp::parseItem(JSContext *cx, CompilerState *state)
{
    RENode *ren;
    int8_t op;

    jsString_t source = state->source;
    int index = state->index;
    int length = strlen(source);

    switch (index < length ? source[index] : '\0') {
    case '^':
	state->index = index + 1;
	ren = new (this) RENode(state, REOP_BOL, NULL);
	ren->flags |= RENode::ANCHORED;
	return ren;

    case '$':
	state->index = index + 1;
	if (index == state->indexBegin || 
            ((source[index-1] == '(' || source[index-1] == '|') && /*balance)*/
            (index - 1 == state->indexBegin || source[index-2] != '\\')))
	{
	    return new (this) RENode(state, REOP_EOLONLY, NULL);
	} else {
	    return new (this) RENode(state, REOP_EOL, NULL);
	}

    case '\\':
	switch (++index < length ? source[index] : '\0') {
	case 'b':
	    op = REOP_WBDRY;
	    break;
	case 'B':
	    op = REOP_WNONBDRY;
	    break;
	default:
	    return parseQuantAtom(cx, state);
	}

	/*
	 * Word boundaries and non-boundaries are flagged as non-empty
	 * so they will be prefixed by an anchoring node.
	 */
	state->index = index + 1;
	ren = new (this) RENode(state, op, NULL);
	ren->flags |= RENode::NONEMPTY;
	return ren;

    default:;
    }
    return parseQuantAtom(cx, state);
}
/*
 *  quantatom:  atom		    An unquantified atom.
 *	      quantatom '{' n ',' m '}'
 *				      Atom must occur between n and m times.
 *	      quantatom '{' n ',' '}' Atom must occur at least n times.
 *	      quantatom '{' n '}'     Atom must occur exactly n times.
 *	      quantatom '*'	   Zero or more times (same as {0,}).
 *	      quantatom '+'	   One or more times (same as {1,}).
 *	      quantatom '?'	   Zero or one time (same as {0,1}).
 *
 *	      any of which can be optionally followed by '?' for ungreedy
 */
JSNativeRegExp::RENode *
JSNativeRegExp::parseQuantAtom(JSContext *cx, CompilerState *state)
{
    RENode *ren = parseAtom(cx, state);

    if (ren == NULL) {
	return NULL;
    }

    int up;
    jsChar_t c;
    RENode *ren2;
    int min, max;
    jsString_t source = state->source;
    int index = state->index;
    int length = strlen(source);
    while (index < length) {
	switch (source[index]) {
	case '{': {
	    int length = strlen(source);

	    c = '\0';	// avoid compile error
	    if (++index == length || !isDigit(c = source[index])) {
		jsChar_t s[2];

		sprintf(s, "%c", source[state->index]);
		reportError(cx, "msg.bad.quant", s, state);
		return NULL;
	    }
	    min = unDigit(c);
	    while (++index < length && isDigit(c = source[index])) {
		min = 10 * min + unDigit(c);
		if ((min >> 16) != 0) {
		    reportError(cx, "msg.overlarge.max", tail(source, index),
			    state);
		    return NULL;
		}
	    }
	    if (source[index] == ',') {
		up = ++index;
		if (isDigit(source[index])) {
		    max = unDigit(source[index]);
		    while (isDigit(c = source[++index])) {
			max = 10 * max + unDigit(c);
			if ((max >> 16) != 0) {
			    jsChar_t s[2];

			    sprintf(s, "%c", source[up]);
			    reportError(cx, "msg.overlarge.max", s, state);
			    return NULL;
			}
		    }
		    if (max == 0) {
			reportError(cx, "msg.zero.quant",
				    tail(source, state->index), state);
			return NULL;
		    }
		    if (min > max) {
			reportError(cx, "msg.max.lt.min", tail(source, up),
				state);
			return NULL;
		    }
		} else {
		    /* 0 means no upper bound. */
		    max = 0;
		}
	    } else {
		/* Exactly n times. */
		if (min == 0) {
		    reportError(cx, "msg.zero.quant",
				tail(source, state->index), state);
		    return NULL;
		}
		max = min;
	    }
	    if (source[index] != '}') {
		jsChar_t s[2];

		sprintf(s, "%c", source[state->index]);
		reportError(cx, "msg.unterm.quant", s, state);
		return NULL;
	    }
	    index++;

	    ren2 = new (this) RENode(state, REOP_QUANT, ren);
	    if (min > 0 && (ren->flags & RENode::NONEMPTY) != 0) {
		ren2->flags |= RENode::NONEMPTY;
	    }
	    ren2->min = (short) min;
	    ren2->max = (short) max;
	    ren = ren2;
	    break;
	}

	case '*':
	    index++;
	    ren = new (this) RENode(state, REOP_STAR, ren);
	    break;

	case '+':
	    index++;
	    ren2 = new (this) RENode(state, REOP_PLUS, ren);
	    if ((ren->flags & RENode::NONEMPTY) != 0) {
		ren2->flags |= RENode::NONEMPTY;
	    }
	    ren = ren2;
	    break;

	case '?':
	    index++;
	    ren = new (this) RENode(state, REOP_OPT, ren);
	    break;

	default:
	    goto exitloop;
	}
	if ((index < length) && (source[index] == '?')) {
	    ren->flags |= RENode::MINIMAL;
	    index++;
	}
    }
exitloop:

    state->index = index;
    return ren;
}
/*
 *  atom:       '(' regexp ')'	  A parenthesized regexp (what matched
 *				      can be addressed using a backreference,
 *				      see '\' n below).
 *	      '.'		     Matches any char except '\n'.
 *	      '[' classlist ']'       A character class.
 *	      '[' '^' classlist ']'   A negated character class.
 *	      '\f'		    Form Feed.
 *	      '\n'		    Newline (Line Feed).
 *	      '\r'		    Carriage Return.
 *	      '\t'		    Horizontal Tab.
 *	      '\v'		    Vertical Tab.
 *	      '\d'		    A digit (same as [0-9]).
 *	      '\D'		    A non-digit.
 *	      '\w'		    A word character, [0-9a-z_A-Z].
 *	      '\W'		    A non-word character.
 *	      '\s'		    A whitespace character, [ \b\f\n\r\t\v].
 *	      '\S'		    A non-whitespace character.
 *	      '\' n		   A backreference to the nth (n decimal
 *				      and positive) parenthesized expression.
 *	      '\' octal	       An octal escape sequence (octal must be
 *				      two or three digits long, unless it is
 *				      0 for the null character).
 *	      '\x' hex		A hex escape (hex must be two digits).
 *	      '\c' ctrl	       A control character, ctrl is a letter.
 *	      '\' literalatomchar     Any character except one of the above
 *				      that follow '\' in an atom.
 *	      otheratomchar	   Any character not first among the other
 *				      atom right-hand sides.
 */
JSNativeRegExp::RENode *
JSNativeRegExp::parseAtom(JSContext *cx, CompilerState *state)
{
    int num = 0, len;
    RENode *ren = NULL;
    RENode *ren2;
#if defined(USE_UTF8) || defined(USE_SJIS)
    wchar_t /* jsChar_t */ c;
    int n;
#else
    uint16_t /* jsChar_t */ c;
#endif
    int8_t op;

    jsBool_t skipCommon = FALSE;
    jsBool_t doFlat = FALSE;

    jsString_t source = state->source;
    int index = state->index;
    int ocp = index;
    int length = strlen(source);

    if (index == length) {
	state->index = index;
	return new (this) RENode(state, REOP_EMPTY, NULL);
    }
    switch (source[index]) {
	/* handle /|a/ by returning an empty node for the leftside */
    case '|':
	return new (this) RENode(state, REOP_EMPTY, NULL);

    case '(':
	op = REOP_END;
	if (source[index + 1] == '?') {
	    switch (source[index + 2]) {
	    case ':' :
		op = REOP_LPARENNON;
		break;
	    case '=' :
		op = REOP_ASSERT;
		break;
	    case '!' :
		op = REOP_ASSERT_NOT;
		break;
	    }
	}
	if (op == REOP_END) {
	    op = REOP_LPAREN;
	    num = state->parenCount++;      /* \1 is numbered 0, etc. */
	    state->index = index + 1;
	} else {
	    state->index = index + 3;
	}
	ren2 = parseRegExp(cx, state);
	if (ren2 == NULL) {
	    return NULL;
	}
	index = state->index;
	if (index >= length || source[index] != ')') {
	    reportError(cx, "msg.unterm.paren", tail(source, ocp), state);
	    return NULL;
	}
	index++;
	ren = new (this) RENode(state, op, ren2);
	ren->flags = (int8_t) (ren2->flags
				& (RENode::ANCHORED | RENode::NONEMPTY));
	ren->num = num;
	if ((op == REOP_LPAREN) || (op == REOP_LPARENNON)) {
	    /* Assume RPAREN ops immediately succeed LPAREN ops */
	    ren2 = new (this) RENode(state, (int8_t)(op + 1), NULL);
	    setNext(cx, state, ren, ren2);
	    ren2->num = num;
	}
	break;

    case '.':
	++index;
	op = REOP_DOT;
	if ((index < length) && (source[index] == '*')) {
	    index++;
	    op = REOP_DOTSTAR;
	    if ((index < length) && (source[index] == '?')) {
		index++;
		op = REOP_DOTSTARMIN;
	    }
	}
	ren = new (this) RENode(state, op, NULL);
	if (ren->op == REOP_DOT) {
	    ren->flags = RENode::SINGLE | RENode::NONEMPTY;
	}
	break;

    case '[':
	/* A char class must have at least one char in it. */
	if (++index == length) {
	    reportError(cx, "msg.unterm.class", tail(source, ocp), state);
	    return NULL;
	}
	c = source[index];
	ren = new (this) RENode(state, REOP_CCLASS, (void *) index);

	/* A negated class must have at least one char in it after the ^. */
	if (c == '^' && ++index == length) {
	    reportError(cx, "msg.unterm.class", tail(source, ocp), state);
	    return NULL;
	}

#if defined(USE_UTF8) || defined(USE_SJIS)
	n = 1;
	for (;;) {
	    if ((index += n) >= length) {
		reportError(cx, "msg.unterm.paren", tail(source, ocp), state);
		return NULL;
	    }
	    n = JSStringRuntime::mbtowc(&c, source + index,
					JSStringRuntime::_MB_CUR_MAX);
	    if (c == ']') {
		break;
	    }
	    if (c == '\\' && index+1 != length) {
	        index += JSStringRuntime::mbtowc(&c, source + index + n,
						JSStringRuntime::_MB_CUR_MAX);
	    }
	}
#else /* !(defined(USE_UTF8) || defined(USE_SJIS)) */
	for (;;) {
	    if (++index == length) {
		reportError(cx, "msg.unterm.paren", tail(source, ocp), state);
		return NULL;
	    }
	    c = source[index];
	    if (c == ']') {
		break;
	    }
	    if (c == '\\' && index+1 != length) {
		index++;
	    }
	}
#endif /* defined(USE_UTF8) || defined(USE_SJIS) */
	ren->kid2 = index++;

	/* Since we rule out [] and [^], we can set the non-empty flag. */
	ren->flags = RENode::SINGLE | RENode::NONEMPTY;
	break;

    case '\\':
	if (++index == length) {
	    JSContext::reportError(cx, 
		JSScriptRuntime::getMessage(cx, "msg.trail.backslash"));
	    return NULL;
	}
	c = source[index];
	switch (c) {
	case 'f':
	case 'n':
	case 'r':
	case 't':
	case 'v':
	    c = getEscape(cx, c);
	    ren = new (this) RENode(state, REOP_FLAT1, NULL);
	    break;
	case 'd':
	    ren = new (this) RENode(state, REOP_DIGIT, NULL);
	    break;
	case 'D':
	    ren = new (this) RENode(state, REOP_NONDIGIT, NULL);
	    break;
	case 'w':
	    ren = new (this) RENode(state, REOP_ALNUM, NULL);
	    break;
	case 'W':
	    ren = new (this) RENode(state, REOP_NONALNUM, NULL);
	    break;
	case 's':
	    ren = new (this) RENode(state, REOP_SPACE, NULL);
	    break;
	case 'S':
	    ren = new (this) RENode(state, REOP_NONSPACE, NULL);
	    break;

	case '0':
	case '1':
    	case '2':
    	case '3':
    	case '4':
    	case '5':
    	case '6':
    	case '7':
    	case '8':
    	case '9':
	    /*
		Yuk. Keeping the old style \n interpretation for 1.2
		compatibility.
	    */
	    if ((state->cx->getLanguageVersion() != JSContext::VERSION_DEFAULT)
		&& (state->cx->getLanguageVersion()
			<= JSContext::VERSION_1_4)) {
		switch (c) {		    
		case '0':
		    state->index = index;
		    num = doOctal(state);
		    index = state->index;
		    ren = new (this) RENode(state, REOP_FLAT1, NULL);
#if defined(USE_UTF8) || defined(USE_SJIS)
		    c = (wchar_t /* jsChar_t */) num;
#else
		    c = (uint16_t /* jsChar_t */) num;
#endif
		    break;

		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		    num = unDigit(c);
		    len = 1;
		    while (++index < length && isDigit(c = source[index])) {
			num = 10 * num + unDigit(c);
			len++;
		    }
		    /* n in [8-9] and > count of parenetheses, then revert to
		    '8' or '9', ignoring the '\' */
		    if (((num == 8) || (num == 9))
			&& (num > state->parenCount)) {
			ocp = --index;  /* skip beyond the '\' */
			doFlat = TRUE;
			skipCommon = TRUE;
			break;			
		    }
		    /* more than 1 digit, or a number greater than
			the count of parentheses => it's an octal */
		    if ((len > 1) || (num > state->parenCount)) {
			state->index = ocp;
			num = doOctal(state);
			index = state->index;
			ren = new (this) RENode(state, REOP_FLAT1, NULL);
#if defined(USE_UTF8) || defined(USE_SJIS)
			c = (wchar_t /* jsChar_t */) num;
#else
			c = (uint16_t /* jsChar_t */) num;
#endif
			break;
		    }
		    index--;
		    ren = new (this) RENode(state, REOP_BACKREF, NULL);
		    ren->num = num - 1;       /* \1 is numbered 0, etc. */

		    /* Avoid common chr- and flags-setting 
						    code after switch. */
		    ren->flags = RENode::NONEMPTY;
		    skipCommon = TRUE;
		    break;
		}
	    } else {
		if (c == '0') {
		    ren = new (this) RENode(state, REOP_FLAT1, NULL);
		    c = 0;
		} else {
		    num = unDigit(c);
		    len = 1;
		    while (++index < length && isDigit(c = source[index])) {
			num = 10 * num + unDigit(c);
			len++;
		    }
		    index--;
		    ren = new (this) RENode(state, REOP_BACKREF, NULL);
		    ren->num = num - 1;       /* \1 is numbered 0, etc. */
		    /* Avoid common chr- and flags-setting 
						    code after switch. */
		    ren->flags = RENode::NONEMPTY;
		    skipCommon = TRUE;
		}
	    }
	    break;
	    
	case 'x':
	    ocp = index;
	    if (++index < length && isHex(c = source[index])) {
		num = unHex(c);
		if (++index < length && isHex(c = source[index])) {
		    num <<= 4;
		    num += unHex(c);
		} else {
		    if ((state->cx->getLanguageVersion()
				!= JSContext::VERSION_DEFAULT)
			&& (state->cx->getLanguageVersion()
				<= JSContext::VERSION_1_4)) {
			index--; /* back up so index points to last hex char */
		    } else { /* ecma 2 requires pairs of hex digits. */
			index = ocp;
			num = 'x';
		    }
		}
	    } else {
		index = ocp;	/* \xZZ is xZZ (Perl does \0ZZ!) */
		num = 'x';
	    }
	    ren = new (this) RENode(state, REOP_FLAT1, NULL);
#if defined(USE_UTF8) || defined(USE_SJIS)
	    c = (wchar_t /* jsChar_t */) num;
#else
	    c = (uint16_t /* jsChar_t */) num;
#endif
	    break;

	case 'c':
	    c = source[++index];
	    if (!('A' <= c && c <= 'Z') && !('a' <= c && c <= 'z')) {
		index -= 2;
		ocp = index;
		doFlat = TRUE;
		skipCommon = TRUE;
		break;
	    }
	    c = toupper(c);
#if defined(USE_UTF8) || defined(USE_SJIS)
	    c = (wchar_t /* jsChar_t */) (c ^ 64); // JS_TOCTRL
#else
	    c = (uint16_t /* jsChar_t */) (c ^ 64); // JS_TOCTRL
#endif
	    ren = new (this) RENode(state, REOP_FLAT1, NULL);
	    break;

	case 'u':
	    if (index + 4 < length &&
		isHex(source[index+1]) && isHex(source[index+2]) &&
		isHex(source[index+3]) && isHex(source[index+4])) {
		num = (((((unHex(source[index+1]) << 4) +
			    unHex(source[index+2])) << 4) +
			   unHex(source[index+3])) << 4) +
			  unHex(source[index+4]);
#if defined(USE_UTF8) || defined(USE_SJIS)
		c = (wchar_t /* jsChar_t */) num;
#else
		c = (uint16_t /* jsChar_t */) num;
#endif
		index += 4;
		ren = new (this) RENode(state, REOP_FLAT1, NULL);
		break;
	    }

	    /* Unlike Perl \\xZZ, we take \\uZZZ to be literal-u then ZZZ. */
	    ocp = index;
	    doFlat = TRUE;
	    skipCommon = TRUE;
	    break;

	default:
	    ocp = index;
	    doFlat = TRUE;
	    skipCommon = TRUE;
	    break;
	}

	/* Common chr- and flags-setting code for escape opcodes. */
	if (ren != NULL && !skipCommon) {
	    ren->chr = c;
	    ren->flags = RENode::SINGLE | RENode::NONEMPTY;
	}
	skipCommon = FALSE;

	if (!doFlat) {
	    /* Skip to next unparsed char. */
	    index++;
	    break;
	}

	/* fall through since doFlat was true */
	doFlat = FALSE;

    default:
#if defined(USE_UTF8) || defined(USE_SJIS)
	n = JSStringRuntime::mblen(source + index,
				JSStringRuntime::_MB_CUR_MAX);
	while ((index += n) < length
		&& strchr(metachars, source[index]) == NULL) {
	    n = JSStringRuntime::mblen(source + index,
					JSStringRuntime::_MB_CUR_MAX);
	}
#else
	while (++index != length && strchr(metachars, source[index]) == NULL)
	    ;
#endif
	len = (int)(index - ocp);
#if defined(USE_UTF8) || defined(USE_SJIS)
	if (index < length && len > 1
	    && strchr(closurechars, source[index]) != NULL) {
	    index -= n;
	    len -= n;
	}
#else /* !(defined(USE_UTF8) || defined(USE_SJIS)) */
	if (index != length && len > 1
	    && strchr(closurechars, source[index]) != NULL) {
	    index--;
	    len--;
	}
#endif
	if (len > REOP_FLATLEN_MAX) {
	    len = REOP_FLATLEN_MAX;
	    index = ocp + len;
	}

	if (len == 1) {
	    ren = new (this) RENode(state, REOP_FLAT1, (void *) ocp);
	} else {
	    ren = new (this) RENode(state, REOP_FLAT, (void *) ocp);
	}
	ren->flags = RENode::NONEMPTY;
	if (len > 1) {
	    ren->kid2 = index;
	} else {
	    ren->flags |= RENode::SINGLE;
	    ren->chr = source[ocp];
	}
	break;
    }

    state->index = index;
    return ren;
}
int
JSNativeRegExp::doOctal(CompilerState *state)
{
    jsString_t source = state->source;
    int index = state->index;
    int tmp, num = 0;
    jsChar_t c = '\0';	// avoid compile error
    int length = strlen(source);

    while (++index < length && '0' <= (c = source[index]) && c <= '7') {
	tmp = 8 * num + (int)(c - '0');
	if (tmp > 0377) {
	    break;
	}
	num = tmp;
    }
    index--;
    state->index = index;
    return num;
}
jsString_t
JSNativeRegExp::tail(jsString_t a, int i)
{
    return (a +  i);
}
int
JSNativeRegExp::greedyRecurse(JSContext *cx, GreedyState *grState, int index,
			int previousKid)
{
    int kidMatch;
    int match;
    int num;

    /*
     * when the kid match fails, we reset the parencount and run any 
     * previously succesful kid in order to restablish it's paren
     * contents.
     */

    num = grState->state->parenCount;
    kidMatch = matchRENodes(cx, grState->state, grState->kid, grState->next,
			index);
    if (kidMatch == -1) {
	grState->state->parenCount = num;
	if (previousKid != -1) {
	    matchRENodes(cx, grState->state, grState->kid, grState->next,
			previousKid);
	}
	return matchRENodes(cx, grState->state, grState->next, grState->stop,
			index);
    } else {
	if (kidMatch == index) { /* no point pursuing an empty match forever */
	    return kidMatch;
	}
	if ((grState->maxKid == 0) || (++grState->kidCount < grState->maxKid)) {
	    match = greedyRecurse(cx, grState, kidMatch, index);
	    if (match != -1) return match;
	    --grState->kidCount;
	    grState->state->parenCount = num;
	    matchRENodes(cx, grState->state, grState->kid, grState->next,
			index);
	}
	match = matchRENodes(cx, grState->state, grState->next, grState->stop,
			kidMatch);
	if (match != -1) return match;
	/* No subsequent kid could complete; final backtrack attempt with
	   zero kids */    
	grState->state->parenCount = num;
	if (previousKid != -1) {
	    matchRENodes(cx, grState->state, grState->kid, grState->next,
			previousKid);
	}
	return matchRENodes(cx, grState->state, grState->next, grState->stop,
			index);
    }
}
int
JSNativeRegExp::matchGreedyKid(JSContext *cx, MatchState *state, RENode *ren,
			RENode *stop, int kidCount, int index, int previousKid)
{
    GreedyState grState;

    grState.state = state;
    grState.kid = (RENode *) ren->kid;
    grState.next = ren->next;
    grState.stop = stop;
    grState.kidCount = kidCount;
    grState.maxKid = (ren->op == REOP_QUANT) ? ren->max : 0;
    return greedyRecurse(cx, &grState, index, previousKid);
}
int
JSNativeRegExp::matchNonGreedyKid(JSContext *cx, MatchState *state,
				RENode *ren, int kidCount, int maxKid,
				int index)
{
    int kidMatch;
    int match;

    match = matchRENodes(cx, state, ren->next, NULL, index);
    if (match != -1) return index;
    kidMatch = matchRENodes(cx, state, (RENode *) ren->kid, ren->next, index);
    if (kidMatch == -1) {
	return -1;
    } else {
	if (kidMatch == index) { /* no point pursuing an empty match forever */
	    return kidMatch;
	}
	return matchNonGreedyKid(cx, state, ren, kidCount, maxKid, kidMatch);
    }
}
int
JSNativeRegExp::matchRENodes(JSContext *cx, MatchState *state, RENode *ren,
			RENode *stop, int index)
{
    int num;
    jsString_t input = state->input;
    int inputLength = strlen(input);
#if defined(USE_UTF8) || defined(USE_SJIS)
    wchar_t c;
    int n;
#endif

    while ((ren != stop) && (ren != NULL)) {
	switch (ren->op) {
	case REOP_EMPTY:
	    break;
	case REOP_ALT: {
	    if (ren->next->op != REOP_ALT) {
		ren = (RENode *) ren->kid;
		continue;
	    } else {
		num = state->parenCount;

		int kidMatch = matchRENodes(cx, state, (RENode *) ren->kid,
					stop, index);

		if (kidMatch != -1) return kidMatch;
		for (int i = num; i < state->parenCount; i++) {
		    state->parens[i]->length = 0;
		}
		state->parenCount = num;
	    }
	    break;
	}
	case REOP_QUANT: {
	    int lastKid = -1;

	    for (num = 0; num < ren->min; num++) {
		int kidMatch = matchRENodes(cx, state, (RENode *) ren->kid,
					ren->next, index);

		if (kidMatch == -1) {
		    return -1;
		} else {
		    lastKid = index;
		    index = kidMatch;
		}
	    }
	    if (num == ren->max) {
		// Have matched the exact count required, 
		// need to match the rest of the regexp.
		break;
	    }
	    if ((ren->flags & RENode::MINIMAL) == 0) {
		return matchGreedyKid(cx, state, ren, stop, num, index,
				lastKid);
	    } else {
		index = matchNonGreedyKid(cx, state, ren, num, ren->max, index);
		if (index == -1) return -1;
	    }						
	    break;
	}
	case REOP_PLUS: {
	    int kidMatch = matchRENodes(cx, state, (RENode *) ren->kid,
				ren->next, index);

	    if (kidMatch == -1) {
		return -1;
	    }
	    if ((ren->flags & RENode::MINIMAL) == 0) {
		return matchGreedyKid(cx, state, ren, stop, 1, kidMatch, index);
	    }
	    index = matchNonGreedyKid(cx, state, ren, 1, 0, kidMatch);
	    if (index == -1) return -1;
	    break;
	}
	case REOP_STAR:					
	    if ((ren->flags & RENode::MINIMAL) == 0) {
		return matchGreedyKid(cx, state, ren, stop, 0, index, -1);
	    }
	    index = matchNonGreedyKid(cx, state, ren, 0, 0, index);
	    if (index == -1) return -1;
	    break;
	case REOP_OPT: {
	    int saveNum = state->parenCount;

	    if (((ren->flags & RENode::MINIMAL) != 0)) {
		int restMatch = matchRENodes(cx, state, ren->next, stop, index);

		if (restMatch != -1) return restMatch;
	    }
	    int kidMatch = matchRENodes(cx, state, (RENode *) ren->kid,
				ren->next, index);

	    if (kidMatch == -1) {
		state->parenCount = saveNum;
		break;
	    } else {
		int restMatch = matchRENodes(cx, state, ren->next, stop,
					kidMatch);

		if (restMatch == -1) {
		    // need to undo the result of running the kid
		    state->parenCount = saveNum;
		    break;
		} else {
		    return restMatch;
		}
	    }
	}
	case REOP_LPARENNON:
	    ren = (RENode *) ren->kid;
	    continue;
	case REOP_RPARENNON:
	    break;
	case REOP_LPAREN: {
	    num = ren->num;
	    ren = (RENode *) ren->kid;

	    JSSubString *parsub = state->parens[num];

	    if (parsub == NULL) {
		parsub = state->parens[num] = new (cx) JSSubString();
		parsub->charArray = input;
	    }
	    parsub->index = index;
	    parsub->length = 0;
	    if (num >= state->parenCount) {
		state->parenCount = num + 1;
	    }
	    continue;
	}
	case REOP_RPAREN: {
	    num = ren->num;

	    JSSubString *parsub = state->parens[num];

	    if (parsub == NULL) {
		cx->exceptionHandler->throwEx(
                        cx,
                        new (cx) JSRuntimeException("Paren problem"));
	    }
	    parsub->length = index - parsub->index;
	    break;
	}
	case REOP_ASSERT: {
	    int kidMatch = matchRENodes(cx, state, (RENode *) ren->kid,
				ren->next, index);

	    if (kidMatch == -1) return -1;
	    break;
	}
	case REOP_ASSERT_NOT: {
	    int kidMatch = matchRENodes(cx, state, (RENode *) ren->kid,
				ren->next, index);

	    if (kidMatch != -1) return -1;
	    break;
	}
	case REOP_BACKREF: {
	    num = ren->num;
	    if (num >= state->parensLength) {
		JSContext::reportError(cx, 
			JSScriptRuntime::getMessage(cx, "msg.bad.backref"));
		return -1;
	    }
	    JSSubString *parsub = state->parens[num];

	    if (parsub == NULL) {
		parsub = state->parens[num] = new (cx) JSSubString();
	    }

	    int length = parsub->length;

	    if ((inputLength - index) < length) {
		return -1;
	    } else {
		for (int i = 0; i < length; i++, index++) {
		    if (!matchChar(state->flags, input[index],
				parsub->charArray[parsub->index + i])) {
			return -1;
		    }
		}
	    }
	    break;
	}
	case REOP_CCLASS:
	    if (index < inputLength) {
		if (ren->bitmap == NULL) {
		    jsString_t source = (ren->s != NULL) 
						? ren->s 
						: this->source;
		    ren->buildBitmap(cx, this, state, source,
				((state->flags & FOLD) != 0));
		}
#if defined(USE_UTF8) || defined(USE_SJIS)
                wchar_t c;
                int n = JSStringRuntime::mbtowc(&c, input + index,
						JSStringRuntime::_MB_CUR_MAX);

                int b = ((c & 0x7ffffff) >> 3);

                if (b >= ren->bmsize) {
                    if (ren->kid2 == -1) { // a ^ class
                        index += n;     // index++;
                    } else {
                        return -1;
                    }   
                } else {
                    int bit = c & 7;

                    bit = 1 << bit;
                    if ((ren->bitmap[b] & bit) != 0) {
                        index += n;     // index++;
                    } else {
                        return -1;
                    }
                }
#else /* !(defined(USE_UTF8) || defined(USE_SJIS)) */
		uint16_t /* jsChar_t */ c = input[index];

		int b = ((c & 0xff) >> 3);

		if (b >= ren->bmsize) {
		    if (ren->kid2 == -1) { // a ^ class
			index++;
		    } else {
			return -1;
		    }
		} else {
		    int bit = c & 7;

		    bit = 1 << bit;
		    if ((ren->bitmap[b] & bit) != 0) {
			index++;
		    } else {
			return -1;
		    }
		}
#endif /* USE_UTF8 */
	    } else {
		return -1;
	    }
	    break;
	case REOP_DOT:
	    if ((index < inputLength) && (input[index] != '\n')) {
#if defined(USE_UTF8) || defined(USE_SJIS)
                index += JSStringRuntime::mblen(input + index,
						JSStringRuntime::_MB_CUR_MAX);
#else
		index++;
#endif
	    } else {
		return -1;
	    }
	    break;
	case REOP_DOTSTARMIN: {
	    int cp2;

	    for (cp2 = index; cp2 < inputLength; cp2++) {
		int cp3 = matchRENodes(cx, state, ren->next, stop, cp2);

		if (cp3 != -1) return cp3;
		if (input[cp2] == '\n') {
		    return -1;
		}
	    }
	    return -1;
	}
	case REOP_DOTSTAR: {
	    int cp2;

	    for (cp2 = index; cp2 < inputLength; cp2++) {
		if (input[cp2] == '\n') {
		    break;
		}
	    }
	    while (cp2 >= index) {
		int cp3 = matchRENodes(cx, state, ren->next, stop, cp2);

		if (cp3 != -1) {
		    return cp3;
		}
		cp2--;
	    }
	    return -1;
	}
	case REOP_WBDRY:
	    if (((index == 0) || !isWord(input[index-1]))
		 ^ ((index >= inputLength)
		|| !isWord(input[index]))) {
		; // leave index 
	    } else {
		return -1;
	    }
	    break;
	case REOP_WNONBDRY:
	    if (((index == 0) || !isWord(input[index-1]))
		^ ((index < inputLength) && isWord(input[index]))) {
		; // leave index 
	    } else {
		return -1;
	    }
	    break;
	case REOP_EOLONLY:
	case REOP_EOL: {
	    if (index == inputLength) {
		; // leave index;
	    } else {
		JSRegExpImpl *reImpl = getImpl(cx);

		if ((reImpl->multiline) || ((state->flags & MULTILINE) != 0)) {
		    if (input[index] == '\n') {
			;// leave index
		    } else {
			return -1;
		    }
		} else {
		    return -1;
		}
	    }
	    break;
	}
	case REOP_BOL: {
	    JSRegExpImpl *reImpl = getImpl(cx);

	    if (index != 0) {
		if ((index < inputLength)
		    && (reImpl->multiline
			|| ((state->flags & MULTILINE) != 0))) {
		    if (input[index - 1] == '\n') {
			break;
		    }
		}
		return -1;
	    }
	    // leave index
	    break;
	}
	case REOP_DIGIT:
	    if ((index < inputLength) && isDigit(input[index])) {
		index++;
	    } else {
		return -1;
	    }
	    break;
	case REOP_NONDIGIT:
	    if ((index < inputLength) && !isDigit(input[index])) {
#if defined(USE_UTF8) || defined(USE_SJIS)
                index += JSStringRuntime::mblen(input + index,
						JSStringRuntime::_MB_CUR_MAX);
#else
		index++;
#endif
	    } else {
		return -1;
	    }
	    break;
	case REOP_ALNUM:
	    if ((index < inputLength) && isWord(input[index])) {
		index++;
	    } else {
		return -1;
	    }
	    break;
	case REOP_NONALNUM:
	    if ((index < inputLength) && !isWord(input[index])) {
#if defined(USE_UTF8) || defined(USE_SJIS)
                index += JSStringRuntime::mblen(input + index,
						JSStringRuntime::_MB_CUR_MAX);
#else
		index++;
#endif
	    } else {
		return -1;
	    }
	    break;
	case REOP_SPACE:
	    if ((index < inputLength)
		&& (JSTokenStream::isJSSpace(input[index])
			|| JSTokenStream::isJSLineTerminator(input[index]))) {
		index++;
	    } else {
		return -1;
	    }
	    break;
	case REOP_NONSPACE:
	    if ((index < inputLength)
		&& !(JSTokenStream::isJSSpace(input[index])
			|| JSTokenStream::isJSLineTerminator(input[index]))) {
#if defined(USE_UTF8) || defined(USE_SJIS)
                index += JSStringRuntime::mblen(input + index,
						JSStringRuntime::_MB_CUR_MAX);
#else
		index++;
#endif
	    } else {
		return -1;
	    }
	    break;
	case REOP_FLAT1:
#if defined(USE_UTF8) || defined(USE_SJIS)
            n = JSStringRuntime::mbtowc(&c, input + index,
					JSStringRuntime::_MB_CUR_MAX);
            if ((index < inputLength)
		&& matchChar(state->flags, ren->chr, c)) {
		index += n;
            } else {
		return -1;
            }
            break;
#else
	    if ((index < inputLength)
		&& matchChar(state->flags, ren->chr, input[index])) {
		index++;
	    } else {
		return -1;
	    }
	    break;
#endif
	case REOP_FLAT: {
	    jsString_t source = (ren->s != NULL)
					? ren->s
					: this->source;
	    int start = (int) ren->kid;
	    int length = ren->kid2 - start;

	    if ((inputLength - index) < length) {
		return -1;
	    } else {
		for (int i = 0; i < length; i++, index++) {
		    if (!matchChar(state->flags, input[index],
				source[start + i])) {
			return -1;
		    }
		}
	    }
	    break;
	}
	case REOP_JUMP:
	    break;
	case REOP_END:
	    break;
	default :
	    cx->exceptionHandler->throwEx(
                    cx,
                    new (cx) JSRuntimeException("Unsupported by node matcher"));
	}
	ren = ren->next;
    }
    return index;
}
int
JSNativeRegExp::matchRegExp(JSContext *cx, MatchState *state, RENode *ren,
			int index)
{
    // have to include the position beyond the last character
    // in order to detect end-of-input/line condition
    int length = strlen(state->input);

#if defined(USE_UTF8) || defined(USE_SJIS)
   int mblen = 0;
   for (int i = index;
	i <= length;) {
#else
    for (int i = index; i <= length; i++) {	    
#endif
	state->skipped = i - index;
	state->parenCount = 0;

	int result = matchRENodes(cx, state, ren, NULL, i);

	if (result != -1) {
	    return result;
	}
      	mblen = JSStringRuntime::mblen(state->input + i,
					   JSStringRuntime::_MB_CUR_MAX);
	if(mblen == -1){ // 2003.11.28 ichino
	 JSContext::reportWarning(cx,
			        JSContext::getMessage(cx,
				"msg.illegal.character Shift-JIS"));

	 /*cx->exceptionHandler->throwEx(
                cx,
                JSContext::reportRuntimeError(cx,
                JSContext::getMessage(cx, "msg.illegal.character Shift-JIS")));*/ 
	    return -1;
	}
	i += mblen;
    }
    return -1;
}
/*
 * indexp is assumed to be an array of length 1
 */
jsVal_t
JSNativeRegExp::executeRegExp(JSContext *cx, JSScriptable *scopeObj, jsString_t str,
			int *indexp, jsBool_t test)
{
    JSNativeRegExp *re = this;
    JSRegExpImpl *res = getImpl(cx);

    /*
     * Initialize a CompilerState to minimize recursive argument traffic.
     */
    MatchState state;

    memset(&state, 0, sizeof (state));
    this->state = &state; // for mark
    state.anchoring = FALSE;
    state.flags = re->flags;
    state.scope = scopeObj;

    jsString_t charArray = JS_NewGCString(cx, str);
    int length = strlen(charArray);

    cx->pushLocalVal(JS_STRING_TO_VAL(charArray));
    int start = *indexp;

    if (start > length) {
	start = length;
    }
    int index = start;

    state.cpbegin = 0;
    state.cpend = length;
    state.start = start;
    state.skipped = 0;
    state.input = charArray;

    state.parenCount = 0;
    state.maybeParens = (JSSubString **)
		JS_NewZoneObj(cx, sizeof (*state.maybeParens) * re->parenCount);
    memset(state.maybeParens, 0, sizeof (*state.maybeParens) * re->parenCount);
    state.maybeParensLength = re->parenCount;
    state.parens = (JSSubString **)
		JS_NewZoneObj(cx, sizeof (*state.parens) * re->parenCount);
    memset(state.parens, 0, sizeof (*state.parens) * re->parenCount);
    state.parensLength = re->parenCount;
    // We allocate the elements of "parens" and "maybeParens" lazily in
    // the Java port since we don't have arenas.

    /*
     * Call the recursive matcher to do the real work.  Return null on mismatch
     * whether testing or not.  On match, return an extended Array object.
     */
    index = matchRegExp(cx, &state, ren, index);
    if (index == -1) {
	cx->popLocalVals(1);
    	this->state = NULL; // for mark
	return JS_VAL_NULL;
    }
    int i = index - state.cpbegin;
    *indexp = i;
    int matchlen = i - (start + state.skipped);
    int ep = index; 
    index -= matchlen;
    jsVal_t result;
    JSScriptable *obj;

    if (test) {
	/*
	 * Testing for a match and updating cx.regExpImpl: don't allocate
	 * an array object, do return true.
	 */
	result = JS_VAL_TRUE;
	cx->pushLocalVal(result);
	obj = NULL;
    } else {
	/*
	 * The array returned on match has element 0 bound to the matched
	 * string, elements 1 through state.parenCount bound to the paren
	 * matches, an index property telling the length of the left context,
	 * and an input property referring to the input string.
	 */
	JSScriptable *scope = getTopLevelScope(cx, scopeObj);
	result = JS_OBJECT_TO_VAL(
			JSScriptRuntime::newObject(cx, scope, "Array",
						0, NULL));
	cx->pushLocalVal(result);
	obj = JS_VAL_TO_OBJECT(result);

	jsString_t matchstr
			= JS_Substring(cx, charArray, index, index + matchlen);

	obj->put(cx, 0, obj, JS_STRING_TO_VAL(JS_NewGCString(cx, matchstr)));
	JS_DeleteZoneObj(cx, matchstr);
    }

    if (state.parenCount > re->parenCount) {
	cx->exceptionHandler->throwEx(cx, new (cx) JSRuntimeException());
    }
    if (state.parenCount == 0) {
	res->parens.setSize(0);
	res->lastParen = res->emptySubString;
    } else {
	JSSubString *parsub = NULL;
	int num;

	res->parens.setSize(state.parenCount);
	for (num = 0; num < state.parenCount; num++) {
	    parsub = state.parens[num];
	    res->parens.setElementAt(parsub, num);
	    if (test) {
		continue;
	    }
	    jsString_t parstr = parsub == NULL ? (jsString_t) "":
						parsub->toString(cx);

	    obj->put(cx, num+1, obj,
		JS_STRING_TO_VAL(JS_NewGCString(cx, parstr)));
	    if (parsub != NULL) {
		JS_DeleteZoneObj(cx, parstr);
	    }
	}
	res->lastParen = parsub;
    }

    if (!test) {
	/*
	 * Define the index and input properties last for better for/in loop
	 * order (so they come after the elements).
	 */
	obj->put(cx, "index", obj, JS_INT_TO_VAL(start + state.skipped));
	obj->put(cx, "input", obj, JS_STRING_TO_VAL(JS_NewGCString(cx, str)));
    }

    if (res->lastMatch == NULL) {
	res->lastMatch = new (cx) JSSubString();
	res->leftContext = new (cx) JSSubString();
	res->rightContext = new (cx) JSSubString();
    }
    res->lastMatch->charArray = charArray;
    res->lastMatch->index = index;
    res->lastMatch->length = matchlen;

    res->leftContext->charArray = charArray;
    if (cx->getLanguageVersion() == JSContext::VERSION_1_2) {
	/*
	 * JS1.2 emulated Perl4.0.1.8 (patch level 36) for global regexps used
	 * in scalar contexts, and unintentionally for the string.match "list"
	 * psuedo-context.  On "hi there bye", the following would result:
	 *
	 * Language     while(/ /g){print("$`");}   s/ /$`/g
	 * perl4.036    "hi", "there"	       "hihitherehi therebye"
	 * perl5	"hi", "hi there"	    "hihitherehi therebye"
	 * js1.2	"hi", "there"	       "hihitheretherebye"
	 *
	 * Insofar as JS1.2 always defined $` as "left context from the last
	 * match" for global regexps, it was more consistent than perl4.
	 */
	res->leftContext->index = start;
	res->leftContext->length = state.skipped;
    } else {
	/*
	 * For JS1.3 and ECMAv2, emulate Perl5 exactly:
	 *
	 * js1.3	"hi", "hi there"	    "hihitherehi therebye"
	 */
	res->leftContext->index = 0;
	res->leftContext->length = start + state.skipped;
    }

    res->rightContext->charArray = charArray;
    res->rightContext->index = ep;
    res->rightContext->length = state.cpend - ep;

    JS_DeleteZoneObj(cx, state.maybeParens);
    JS_DeleteZoneObj(cx, state.parens);

    cx->popLocalVals(2);
    this->state = NULL; // for mark
    return result;
}
void
JSNativeRegExp::reportError(JSContext *cx, jsString_t msg, jsString_t arg,
			CompilerState *state)
{
    int length = strlen(msg) + TableSize(" %s");
    jsString_t message
		= (jsString_t) JS_NewZoneObj(cx, sizeof (*message) * length);

    sprintf(message, "%s %%s", msg);

    cx->exceptionHandler->throwEx(
            cx,
            JSNativeGlobal::constructError(cx, "SyntaxError",
			JSScriptRuntime::getMessage(cx, message, arg),
			state->scope));
}
void
JSNativeRegExp::init(JSContext *cx, JSScriptable *scope, jsString_t source, 
			jsString_t global, jsBool_t flat)
{
    this->source = JS_NewGCString(cx, source);
    flags = 0;
    if (global != NULL) {
	int length = strlen(global);

	for (int i = 0; i < length; i++) {
	    jsChar_t c = global[i];

	    if (c == 'g') {
		flags |= GLOB;
	    } else if (c == 'i') {
		flags |= FOLD;
	    } else if (c == 'm') {
		flags |= MULTILINE;
	    } else {
		cx->exceptionHandler->throwEx(
                        cx,
			JSNativeGlobal::constructError(cx, "SyntaxError",
				JSScriptRuntime::getMessage(cx,
						"msg.invalid.re.flag %c", c),
				scope));
	    }
	}
    }

    CompilerState state(source, flags, cx, scope);

    this->compilerState = &state;

    if (flat) {
	ren = NULL;
	int sourceLen = strlen(source);
	int index = 0;

	while (sourceLen > 0) {
	    int len = sourceLen;
	    RENode *ren2;

	    if (len > REOP_FLATLEN_MAX) {
		len = REOP_FLATLEN_MAX;
	    }
	    if (len == 1) {
		ren2 = new (this) RENode(&state, REOP_FLAT1, (void *) index);
	    } else {
		ren2 = new (this) RENode(&state, REOP_FLAT, (void *) index);
	    }

	    ren2->flags = RENode::NONEMPTY;
	    if (len > 1) {
		ren2->kid2 = index + len;
	    } else {
		ren2->flags |= RENode::SINGLE;
		ren2->chr = state.source[index];		    
	    }
	    index += len;
	    sourceLen -= len;
	    if (ren == NULL) {
		ren = ren2;
	    } else {
		setNext(cx, &state, ren, ren2);
	    }
	}
    } else {
	this->ren = parseRegExp(cx, &state);
    }
    if (ren == NULL) return;

    RENode *end = new (this) RENode(&state, REOP_END, NULL);

    setNext(cx, &state, ren, end);
    if (debug) {
	dumpRegExp(cx, &state, ren);
    }
    this->lastIndex = 0;
    this->parenCount = state.parenCount;
    this->flags = flags;
#ifdef PERSIST
    this->flat = flat;
#endif

    scope = getTopLevelScope(cx, scope);
    setPrototype(cx, getClassPrototype(cx, scope, "RegExp"));
    setParentScope(cx, scope);
}

void
JSNativeRegExp::defineClass(JSContext *cx, JSScriptable *scope)
{
    class RegExpAdapter: public JSObjectAdapter {
#define defineFunction(name, clazz)					\
		static jsVal_t						\
			name(JSContext *cx, JSScriptable *thisObj,	\
				int numArgs, jsVal_t *args,		\
				JSFunction *funObj) {			\
		    JSScriptable *scriptable				\
				= getScriptable(cx, thisObj,		\
					clazz::className, funObj);	\
									\
		    clazz *_thisObj = (clazz *) scriptable;		\
									\
		    return _thisObj->name(cx);			\
		}
#define	defineNumberGetter(name, clazz, uname)				\
		static jsBool_t						\
			name##_getter(jsContext_t context,		\
				jsObject_t object, jsString_t propName,	\
				jsObject_t start, jsVal_t *value) {	\
		    JSContext *_cx = (JSContext *) context;		\
		    JSScriptable *obj = (JSScriptable *) object;	\
		    JSScriptable *startObj = (JSScriptable *) start;	\
		    JSScriptable *scriptable 				\
				= getScriptable(_cx, obj, startObj,  	\
						clazz::className);	\
		    clazz *thisObj = (clazz *) scriptable;		\
									\
		    *value = JSScriptRuntime::toValue(_cx,		\
					(double) thisObj->get##uname()); \
		    return TRUE;					\
		}
#define	defineNumberSetter(name, clazz, uname)				\
		static void						\
			name##_setter(jsContext_t context,		\
				jsObject_t object, jsString_t propName,	\
				jsObject_t start, jsVal_t value) {	\
		    JSContext *_cx = (JSContext *) context;		\
		    JSScriptable *obj = (JSScriptable *) object;	\
		    JSScriptable *startObj = (JSScriptable *) start;	\
		    JSScriptable *scriptable 				\
				= getScriptable(_cx, obj, startObj,  	\
						clazz::className);	\
		    clazz *thisObj = (clazz *) scriptable;		\
									\
		    thisObj->set##uname(				\
				(int64_t)				\
				JSScriptRuntime::toNumber(_cx, value));	\
		}
#define	defineStringGetter(name, clazz, uname)				\
		static jsBool_t						\
			name##_getter(jsContext_t context,		\
				jsObject_t object, jsString_t propName,	\
				jsObject_t start, jsVal_t *value) {	\
		    JSContext *_cx = (JSContext *) context;		\
		    JSScriptable *obj = (JSScriptable *) object;	\
		    JSScriptable *startObj = (JSScriptable *) start;	\
		    JSScriptable *scriptable 				\
				= getScriptable(_cx, obj, startObj,  	\
						clazz::className);	\
		    clazz *thisObj = (clazz *) scriptable;		\
									\
		    *value = JS_STRING_TO_VAL(thisObj->get##uname());	\
		    return TRUE;					\
		}
#define	defineBoolGetter(name, clazz, uname)				\
		static jsBool_t						\
			name##_getter(jsContext_t context,		\
				jsObject_t object, jsString_t propName,	\
				jsObject_t start, jsVal_t *value) {	\
		    JSContext *_cx = (JSContext *) context;		\
		    JSScriptable *obj = (JSScriptable *) object;	\
		    JSScriptable *startObj = (JSScriptable *) start;	\
		    JSScriptable *scriptable 				\
				= getScriptable(_cx, obj, startObj,  	\
						clazz::className);	\
		    clazz *thisObj = (clazz *) scriptable;		\
									\
		    *value = JS_BOOLEAN_TO_VAL(thisObj->get##uname());	\
		    return TRUE;					\
		}

 	public:
	    defineFunction(toStringVal, JSNativeRegExp)

	    defineNumberGetter(lastIndex, JSNativeRegExp, LastIndex)
	    defineStringGetter(source, JSNativeRegExp, Source)
	    defineBoolGetter(global, JSNativeRegExp, Global)
	    defineBoolGetter(ignoreCase, JSNativeRegExp, IgnoreCase)
	    defineBoolGetter(multiline, JSNativeRegExp, Multiline)

	    defineNumberSetter(lastIndex, JSNativeRegExp, LastIndex)
    } ;
    static jsFunction_t	functions[] = {
#define	Item(name, length)	{ #name,  name, length }
	Item(compile, 0),	
	{ "toString", RegExpAdapter::toStringVal, 0 },
	Item(exec, 0),	
	Item(test, 0),	
#undef	Item
    } ;
    JSNativeRegExp *proto = new (cx) JSNativeRegExp(cx);

    cx->pushLocalVal(JS_OBJECT_TO_VAL(proto));
    proto->setParentScope(cx, scope);
    proto->setPrototype(cx, getObjectPrototype(cx, scope));

    proto->defineFunctionProperties(cx,  className, TableSize(functions),
			functions, JS_PA_DontEnum);

    static jsProperty_t properties[] = {
#define	Item(name, attributes, clazz)	{				\
	    #name,  attributes, &clazz::name##_getter, &clazz::name##_setter \
	}
#define	ItemG(name, attributes, clazz)	{				\
	    #name,  attributes, &clazz::name##_getter, NULL		\
	}
	Item(lastIndex, JS_PA_Permanent, RegExpAdapter),
	ItemG(source, JS_PA_Permanent | JS_PA_ReadOnly, RegExpAdapter),
	ItemG(global, JS_PA_Permanent | JS_PA_ReadOnly, RegExpAdapter),
	ItemG(ignoreCase, JS_PA_Permanent | JS_PA_ReadOnly, RegExpAdapter),
	ItemG(multiline, JS_PA_Permanent | JS_PA_ReadOnly, RegExpAdapter),
#undef	ItemG
#undef	Item
    } ;

    proto->defineProperties(cx, TableSize(properties), properties);

    JSScriptable *ctor = JSNativeRegExpCtor::defineClass(cx, scope);

    cx->pushLocalVal(JS_OBJECT_TO_VAL(ctor));
    ctor->put(cx, "prototype", ctor, JS_OBJECT_TO_VAL(proto));

    cx->popLocalVals(2);
}
jsBool_t
JSNativeRegExp::isHex(jsChar_t c)
{
    return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') ||
	   ('A' <= c && c <= 'F');
}
int
JSNativeRegExp::unHex(jsChar_t c)
{
    if ('0' <= c && c <= '9') {
	return c - '0';
    }
    return 10 + tolower(c) - 'a';
}
jsBool_t
JSNativeRegExp::isDigit(jsChar_t c)
{
    return '0' <= c && c <= '9';
}
int
JSNativeRegExp::unDigit(jsChar_t c)
{
    return c - '0';
}

jsString_t
JSNativeRegExp::toString(JSContext *cx)
{
    JSStringBuffer buf(cx);

    buf.append('/');
    buf.append(source != NULL ? source : (jsString_t) "");
    buf.append('/');
    if ((flags & GLOB) != 0) {
	buf.append('g');
    }
    if ((flags & FOLD) != 0) {
	buf.append('i');
    }
    if ((flags & MULTILINE) != 0) {
	buf.append('m');
    }
    return buf.offString();
}
/**
 * "lastIndex" property of RegExp instances (defined in prototype)
 */
int64_t
JSNativeRegExp::getLastIndex()
{
    return (lastIndex & 0x0FFFFFFFFL);
}
/**
 * "lastIndex" property of RegExp instances (defined in prototype)
 */
void
JSNativeRegExp::setLastIndex(int64_t i)
{
    lastIndex = i;
}
/**
 * "source" property of RegExp instances (defined in prototype)
 */
jsString_t
JSNativeRegExp::getSource()
{
    return source;
}
/**
 * "global" property of RegExp instances (defined in prototype)
 */
jsBool_t
JSNativeRegExp::getGlobal()
{
    return (flags & GLOB) != 0;
}
/**
 * "ignoreCase" property of RegExp instances (defined in prototype)
 */
jsBool_t
JSNativeRegExp::getIgnoreCase()
{
    return (flags & FOLD) != 0;
}
/**
 * "multiline" property of RegExp instances (defined in prototype)
 */
jsBool_t
JSNativeRegExp::getMultiline()
{
    return (flags & MULTILINE) != 0;
}
int8_t
JSNativeRegExp::getFlags()
{
    return flags;
}

jsVal_t
JSNativeRegExp::toStringVal(JSContext *cx)
{
    jsString_t string = toString(cx);
    jsVal_t result =  JS_STRING_TO_VAL(JS_NewGCString(cx, string));

    JS_DeleteZoneObj(cx, string);
    return result;
}
jsBool_t
JSNativeRegExp::isInstanceOf(const jsString_t className)
{
    if (className == JSNativeRegExp::className) return TRUE;
    return JSScriptableObject::isInstanceOf(className);
}
jsString_t
JSNativeRegExp::getClassName(JSContext *cx)
{
    return "RegExp";
}
jsVal_t
JSNativeRegExp::call(JSContext *cx, JSScriptable *scope, JSScriptable *thisObj,
			int numArgs, jsVal_t *args)
{
    return execSub(cx, this, numArgs, args, scope, FALSE, this);
}

JSScriptable *
JSNativeRegExp::construct(JSContext *cx, JSScriptable *scope, int numArgs,
			jsVal_t *args)
{
    return JS_VAL_TO_OBJECT(call(cx, scope, NULL, numArgs, args));
}
void
JSNativeRegExp::mark(JSContext *cx)
{
    int i;

    JSGCHeap *gcHeap = &cx->runtime->gcHeap;

    if (gcHeap->isMarkObj(this)) return;

    JSScriptableObject::mark(cx);

    if (source != NULL) {
	gcHeap->markObj(source);
    }
    if (state != NULL) {
	for (i = 0; i < state->maybeParensLength; ++i) {
	    if (state->maybeParens[i] != NULL) {
	    	state->maybeParens[i]->mark(cx);
	    }
	}
	for (i = 0; i < state->parensLength; ++i) {
	    if (state->parens[i] != NULL) {
	    	state->parens[i]->mark(cx);
	    }
	}
    }
}
void
JSNativeRegExp::finalize(JSContext *cx)
{
    /*
     * Don't use 'zone.~JSZone', beacuse allocator is destroied before
     * calling ~JSGCMemory
     */
    zone.~JSGCMemory();
}
#ifdef PERSIST
void
JSNativeRegExp::presave( JSContext *cx, JSScriptable *scope,
			JSPersist::StringConstants& stringConstants, 
			JSPersist::NumberConstants& numberConstants )
{
    stringConstants.addString( source );
}
void
JSNativeRegExp::save( JSContext *cx, JSScriptable *scope, JSRandomWriter* out,
			JSPersist::StringConstants& stringConstants, 
			JSPersist::NumberConstants& numberConstants )
{
    jsBFRegexp_t regexp;

    regexp.r_source = stringConstants.getString( source );
    regexp.r_flags = flags;
    regexp.r_flat = flat;
    regexp.r_source = stringConstants.getString( source );

    regexp.r_source = JSPersist::normalize( regexp.r_source );
    int n = out->write( &regexp, 0, sizeof ( regexp ) );

    if ( n < ( int ) sizeof ( regexp ) ) {
	printf( "can't write regexp\n" );
	return;
    }
}
void
JSNativeRegExp::load( JSContext *cx, JSScriptable *scope, JSRandomReader* in,
			jsString_t* strings, double* numbers )
{
    jsBFRegexp_t regexp;
    int n = in->read( &regexp, 0, sizeof ( regexp ) );

    if ( n < ( int ) sizeof ( regexp ) ) {
	printf( "can't write regexp\n" );
	return;
    }
    regexp.r_source = JSPersist::localize( regexp.r_source );
    source = strings[ regexp.r_source - 1 ];

    jsChar_t global[ 4 ];
    int i = 0;

    if ( ( regexp.r_flags & JBF_REFLAGS_GLOB ) != 0 ) {
	global[ i ] = 'g';
	i += 1;
    }
    if ( ( regexp.r_flags & JBF_REFLAGS_FOLD ) != 0 ) {
	global[ i ] = 'i';
	i += 1;
    }
    if ( ( regexp.r_flags & JBF_REFLAGS_MULTILINE ) != 0 ) {
	global[ i ] = 'm';
	i += 1;
    }
    global[ i ] = '\0';

    init( cx, scope, strings[ regexp.r_source - 1 ], global, regexp.r_flat );
}
#endif /* PERSIST */

JSNativeRegExp::JSNativeRegExp(JSContext *cx, JSScriptable *scope,
			jsString_t source, jsString_t global, jsBool_t flat)
		: zone(cx->runtime)
{
    this->source = NULL;
    state = NULL;
    zone.setMinHIncr(1);
    init(cx, scope, source, global, flat);
}
JSNativeRegExp::JSNativeRegExp(JSContext *cx)
		: zone(cx->runtime)
{
    source = NULL;
    lastIndex = 0;
    parenCount = 0;
    flags = 0;
    program = NULL;
    ren = NULL;
    state = NULL;
}
void
JSNativeRegExp::classInitialize()
{
    if (debug) {
	static jsString_t a[] = {
	    "empty",
	    "alt",
	    "bol",
	    "eol",
	    "wbdry",
	    "wnonbdry",
	    "quant",
	    "star",
	    "plus",
	    "opt",
	    "lparen",
	    "rparen",
	    "dot",
	    "cclass",
	    "digit",
	    "nondigit",
	    "alnum",
	    "nonalnum",
	    "space",
	    "nonspace",
	    "backref",
	    "flat",
	    "flat1",
	    "jump",
	    "dotstar",
	    "anchor",
	    "eolonly",
	    "ucflat",
	    "ucflat1",
	    "ucclass",
	    "nucclass",
	    "backrefi",
	    "flati",
	    "flat1i",
	    "ucflati",
	    "ucflat1i",
	    "anchor1",
	    "ncclass",
	    "dotstar_min",
	    "lparen_non",
	    "rparen_non",
	    "assert",
	    "assertnot",
	    "end"
	};
	reopname = a;
	reopnameLength = TableSize(a);
    }
}
#endif /* REGEXP */

