/*
 * 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(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(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(JSNativeGlobal::constructError(cx, "TypeError", JSScriptRuntime::getMessage(cx, "msg.incompat.call %s", JS_VAL_TO_STRING(name)), scopeObj));
        return JS_VAL_NULL;
    }
    JSNativeRegExp *re = (JSNativeRegExp *) thisObj;
    jsString_t str;

    if (numArgs == 0) {
        str = getImpl(cx)->input;
        if (str == NULL) {
            cx->exceptionHandler->throwEx(JSNativeGlobal::constructError(cx, "SyntaxError", JSScriptRuntime::getMessage(cx, "msg.no.re.input.for %s", re->toString(cx)), thisObj));
            return JS_VAL_NULL; 
        }
    }
    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(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(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(new(cx) JSRuntimeException("Paren problem"));
                    return -1;
                }
                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(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(
               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(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(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(JSNativeGlobal::constructError(cx, "SyntaxError", JSScriptRuntime::getMessage(cx, "msg.invalid.re.flag %c", c), scope));
            }
        }
    }

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

    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;

    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)
{
    zone. ~ JSZone();
}

JSNativeRegExp::JSNativeRegExp(JSContext * cx, JSScriptable * scope, jsString_t source, jsString_t global, jsBool_t flat)
:zone(cx->runtime)
{
    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 */
