/*
 * 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.
 */
/* Header file */
#include <stdio.h>
#include "js-javascriptexception.h"
#include "js-exceptionhandler.h"
#include "js-utils.h"
#include "js-parser.h"

/* Procedures */
void JSParser::mustMatchToken(JSContext * cx, JSTokenStream * ts, int toMatch, jsString_t messageId)
{
    int tt;

    if ((tt = ts->getToken(cx)) != toMatch) {
        reportError(cx, ts, messageId);
        ts->ungetToken(cx, tt); // In case the parser decides to continue
    }
}
void JSParser::reportError(JSContext * cx, JSTokenStream * ts, jsString_t messageId)
{
    this->ok = FALSE;
    ts->reportSyntaxError(cx, messageId);

    /* Throw an exception to unwind the recursive descent parse. 
     * We use JavaScriptException here even though it is really 
     * a different use of the exception than it is usually used
     * for.
     */
    cx->exceptionHandler->throwEx(new(cx) JSJavaScriptException(cx, JS_STRING_TO_VAL(JS_NewZoneString(cx, messageId))));
}

/*
 * The C version of this function takes an argument list,
 * which doesn't seem to be needed for tree generation...
 * it'd only be useful for checking argument hiding, which
 * I'm not doing anyway...
 */
JSNode *JSParser::parseFunctionBody(JSContext * cx, JSTokenStream * ts, JSSource * source)
{
    int oldflags = ts->flags;

    ts->flags &= ~(JSTokenStream::TSF_RETURN_EXPR | JSTokenStream::TSF_RETURN_VOID);
    ts->flags |= JSTokenStream::TSF_FUNCTION;

    JSNode *pn = nf->createBlock(cx, ts->getLineno());
#ifdef VX   // setjmp is too slow
    int tt;

    while ((tt = ts->peekToken(cx)) > ts->_EOF && tt != ts->RC) {
        if (tt == JSTokenStream::FUNCTION) {
            ts->getToken(cx);
            nf->addChildToBack(pn, function(cx, ts, source, FALSE));
            /* function doesn't add its own final EOL,
             * because it gets SEMI + EOL from Statement when it's
             * a nested function; so we need to explicitly add an
             * EOL here.
             */
            source->append((jsChar_t) ts->EOL);
            wellTerminated(cx, ts, ts->FUNCTION);
        }
        else {
            nf->addChildToBack(pn, statement(cx, ts, source));
        }
    }
#else /* !VX */
    JSException *ex;

    if ((ex = enterTry(cx)) == NULL) {
        int tt;

        while ((tt = ts->peekToken(cx)) > ts->_EOF && tt != ts->RC) {
            if (tt == JSTokenStream::FUNCTION) {
                ts->getToken(cx);
                nf->addChildToBack(pn, function(cx, ts, source, FALSE));
                /* function doesn't add its own final EOL,
                 * because it gets SEMI + EOL from Statement when it's
                 * a nested function; so we need to explicitly add an
                 * EOL here.
                 */
                source->append((jsChar_t) ts->EOL);
                wellTerminated(cx, ts, ts->FUNCTION);
            }
            else {
                nf->addChildToBack(pn, statement(cx, ts, source));
            }
        }
        cx->exceptionHandler->exitTry();
    }
    else if (ex->isInstanceOf(JSJavaScriptException::className)) {
        this->ok = FALSE;
    }
    else {
        cx->exceptionHandler->throwEx(ex);
    }
#endif /* VX */
    // finally
    {
        // also in finally block:
        // flushNewLines, clearPushback.

        ts->flags = oldflags;
    }

    return pn;
}

JSNode *JSParser::function(JSContext * cx, JSTokenStream * ts, JSSource * source, jsBool_t isExpr)
{
    jsString_t name = NULL;
    JSNode *args = nf->createLeaf(cx, ts->LP);
    JSNode *body;
    int baseLineno = ts->getLineno();   // line number where source starts

    // save a reference to the function in the enclosing source.
    source->append((jsChar_t) ts->FUNCTION);
    source->append((jsChar_t) source->functionNumber);
    source->functionNumber++;

    // make a new Source for the enclosed function
    JSSource fnSource(cx);

    source = &fnSource;

    // FUNCTION as the first token in a Source means it's a function
    // definition, and not a reference.
    source->append((jsChar_t) ts->FUNCTION);

    if (ts->matchToken(cx, ts->NAME)) {
        name = JS_NewZoneString(cx, ts->getString());   // save
        source->addString(ts->NAME, name);
    }
    else {
        ;   // it's an anonymous function
    }

    mustMatchToken(cx, ts, ts->LP, "msg.no.paren.parms");
    source->append((jsChar_t) ts->LP);

    if (!ts->matchToken(cx, ts->RP)) {
        jsBool_t first = TRUE;

        do {
            if (!first) {
                source->append((jsChar_t) ts->COMMA);
            }
            first = FALSE;
            mustMatchToken(cx, ts, ts->NAME, "msg.no.parm");

            jsString_t s = ts->getString();

            nf->addChildToBack(args, nf->createName(cx, s));

            source->addString(ts->NAME, s);
        } while (ts->matchToken(cx, ts->COMMA));

        mustMatchToken(cx, ts, ts->RP, "msg.no.paren.after.parms");
    }
    source->append((jsChar_t) ts->RP);

    mustMatchToken(cx, ts, ts->LC, "msg.no.brace.body");
    source->append((jsChar_t) ts->LC);
    source->append((jsChar_t) ts->EOL);
    body = parseFunctionBody(cx, ts, source);
    mustMatchToken(cx, ts, ts->RC, "msg.no.brace.after.body");
    source->append((jsChar_t) ts->RC);
    // skip the last EOL so nested functions work...

    // name might be null;
    int sourceLength = source->buf.length();
    JSNode *result = nf->createFunction(cx, name, args, body, ts->getSourceName(),
            baseLineno, ts->getLineno(), source->buf.offString(),
            sourceLength, isExpr);

    if (name != NULL) {
        JS_DeleteZoneObj(cx, name);
    }
    return result;
}

JSNode *JSParser::statements(JSContext * cx, JSTokenStream * ts, JSSource * source)
{
    JSNode *pn = nf->createBlock(cx, ts->getLineno());

    int tt;

    while ((tt = ts->peekToken(cx)) > ts->_EOF && tt != ts->RC) {
        nf->addChildToBack(pn, statement(cx, ts, source));
    }

    return pn;
}

JSNode *JSParser::condition(JSContext * cx, JSTokenStream * ts, JSSource * source)
{
    JSNode *pn;

    mustMatchToken(cx, ts, ts->LP, "msg.no.paren.cond");
    source->append((jsChar_t) ts->LP);
    pn = expr(cx, ts, source, FALSE);
    mustMatchToken(cx, ts, ts->RP, "msg.no.paren.after.cond");
    source->append((jsChar_t) ts->RP);

    // there's a check here in jsparse.c that corrects = to ==

    return pn;
}

jsBool_t JSParser::wellTerminated(JSContext * cx, JSTokenStream * ts, int lastExprType)
{
    int tt = ts->peekTokenSameLine(cx);

    if (tt == ts->_ERROR) {
        return FALSE;
    }

    if (tt != ts->_EOF && tt != ts->EOL && tt != ts->SEMI && tt != ts->RC) {
        int version = cx->getLanguageVersion();

        if ((tt == ts->FUNCTION || lastExprType == ts->FUNCTION) && (version < JSContext::VERSION_1_2)) {
            /*
             * Checking against version < 1.2 and version >= 1.0
             * in the above line breaks old javascript, so we keep it
             * this way for now... XXX warning needed?
             */
            return TRUE;
        }
        else {
            reportError(cx, ts, "msg.no.semi.stmt");
        }
    }
    return TRUE;
}

// match a NAME; return null if no match.
jsString_t JSParser::matchLabel(JSContext * cx, JSTokenStream * ts)
{
    int lineno = ts->getLineno();

    jsString_t label = NULL;
    int tt;

    tt = ts->peekTokenSameLine(cx);
    if (tt == ts->NAME) {
        ts->getToken(cx);
        label = JS_NewZoneString(cx, ts->getString());
    }

    if (lineno == ts->getLineno()) {
        wellTerminated(cx, ts, ts->_ERROR);
    }

    return label;
}

JSNode *JSParser::statement(JSContext * cx, JSTokenStream * ts, JSSource * source)
{
#ifdef VX   // eneterTry(setjmp) is too slow
    return statementHelper(cx, ts, source);
#else /* !VX */
    JSException *ex;

    if ((ex = enterTry(cx)) == NULL) {
        JSNode *result = statementHelper(cx, ts, source);

        cx->exceptionHandler->exitTry();
        return result;
    }
    else if (ex->isInstanceOf(JSJavaScriptException::className)) {
        // skip to end of statement
        int lineno = ts->getLineno();
        int t;

        do {
            t = ts->getToken(cx);
        } while (t != JSTokenStream::SEMI && t != JSTokenStream::EOL && t != JSTokenStream::_EOF && t != JSTokenStream::_ERROR);
        return nf->createExprStatement(cx, nf->createName(cx, "error"), lineno);
    }
    else {
        cx->exceptionHandler->throwEx(ex);
    }
    return NULL;
#endif /* !VX */
}

/**
 * Whether the "catch (e: e instanceof Exception) { ... }" syntax
 * is implemented.
 */
JSNode *JSParser::statementHelper(JSContext * cx, JSTokenStream * ts, JSSource * source)
{
    JSNode *pn = NULL;

    // If skipsemi == TRUE, don't add SEMI + EOL to source at the
    // end of this statment.  For compound statements, IF/FOR etc.
    jsBool_t skipsemi = FALSE;

    int tt;

    int lastExprType = 0;   // For wellTerminated.  0 to avoid warning.

    tt = ts->getToken(cx);

    switch (tt) {
    case JSTokenStream::IF:{
            skipsemi = TRUE;

            source->append((jsChar_t) ts->IF);

            int lineno = ts->getLineno();
            JSNode *cond = condition(cx, ts, source);

            source->append((jsChar_t) ts->LC);
            source->append((jsChar_t) ts->EOL);

            JSNode *ifTrue = statement(cx, ts, source);
            JSNode *ifFalse = NULL;

            if (ts->matchToken(cx, ts->ELSE)) {
                source->append((jsChar_t) ts->RC);
                source->append((jsChar_t) ts->ELSE);
                source->append((jsChar_t) ts->LC);
                source->append((jsChar_t) ts->EOL);
                ifFalse = statement(cx, ts, source);
            }
            source->append((jsChar_t) ts->RC);
            source->append((jsChar_t) ts->EOL);
            pn = nf->createIf(cx, cond, ifTrue, ifFalse, lineno);
            break;
        }

    case JSTokenStream::SWITCH:{
            skipsemi = TRUE;

            source->append((jsChar_t) ts->SWITCH);
            pn = nf->createSwitch(cx, ts->getLineno());

            JSNode *cur_case = NULL;    // to kill warning
            JSNode *case_statements;

            mustMatchToken(cx, ts, ts->LP, "msg.no.paren.switch");
            source->append((jsChar_t) ts->LP);
            nf->addChildToBack(pn, expr(cx, ts, source, FALSE));
            mustMatchToken(cx, ts, ts->RP, "msg.no.paren.after.switch");
            source->append((jsChar_t) ts->RP);
            mustMatchToken(cx, ts, ts->LC, "msg.no.brace.switch");
            source->append((jsChar_t) ts->LC);
            source->append((jsChar_t) ts->EOL);

            while ((tt = ts->getToken(cx)) != ts->RC && tt != ts->_EOF) {
                switch (tt) {
                case JSTokenStream::CASE:
                    source->append((jsChar_t) ts->CASE);
                    cur_case = nf->createUnary(cx, ts->CASE, expr(cx, ts, source, FALSE));
                    source->append((jsChar_t) ts->COLON);
                    source->append((jsChar_t) ts->EOL);
                    break;

                case JSTokenStream::DEFAULT:
                    cur_case = nf->createLeaf(cx, ts->DEFAULT);
                    source->append((jsChar_t) ts->DEFAULT);
                    source->append((jsChar_t) ts->COLON);
                    source->append((jsChar_t) ts->EOL);
                    // XXX check that there isn't more than one default
                    break;

                default:
                    reportError(cx, ts, "msg.bad.switch");
                    break;
                }
                mustMatchToken(cx, ts, ts->COLON, "msg.no.colon.case");

                case_statements = nf->createLeaf(cx, JSTokenStream::BLOCK);

                while ((tt = ts->peekToken(cx)) != ts->RC && tt != ts->CASE && tt != ts->DEFAULT && tt != ts->_EOF) {
                    nf->addChildToBack(case_statements, statement(cx, ts, source));
                }
                // assert cur_case
                nf->addChildToBack(cur_case, case_statements);

                nf->addChildToBack(pn, cur_case);
            }
            source->append((jsChar_t) ts->RC);
            source->append((jsChar_t) ts->EOL);
            break;
        }

    case JSTokenStream::WHILE:{
            skipsemi = TRUE;

            source->append((jsChar_t) ts->WHILE);

            int lineno = ts->getLineno();
            JSNode *cond = condition(cx, ts, source);

            source->append((jsChar_t) ts->LC);
            source->append((jsChar_t) ts->EOL);

            JSNode *body = statement(cx, ts, source);

            source->append((jsChar_t) ts->RC);
            source->append((jsChar_t) ts->EOL);

            pn = nf->createWhile(cx, cond, body, lineno);
            break;

        }

    case JSTokenStream::DO:{
            source->append((jsChar_t) ts->DO);
            source->append((jsChar_t) ts->LC);
            source->append((jsChar_t) ts->EOL);

            int lineno = ts->getLineno();

            JSNode *body = statement(cx, ts, source);

            source->append((jsChar_t) ts->RC);
            mustMatchToken(cx, ts, ts->WHILE, "msg.no.while.do");
            source->append((jsChar_t) ts->WHILE);
            JSNode *cond = condition(cx, ts, source);

            pn = nf->createDoWhile(cx, body, cond, lineno);
            break;
        }

    case JSTokenStream::FOR:{
            skipsemi = TRUE;

            source->append((jsChar_t) ts->FOR);
            int lineno = ts->getLineno();

            JSNode *init;   // Node init is also foo in 'foo in Object'
            JSNode *cond;   // Node cond is also object in 'foo in Object'
            JSNode *incr = NULL;    // to kill warning
            JSNode *body;

            mustMatchToken(cx, ts, ts->LP, "msg.no.paren.for");
            source->append((jsChar_t) ts->LP);
            tt = ts->peekToken(cx);
            if (tt == ts->SEMI) {
#if defined(TARGET_OS_LINUX)
                init = nf->createLeaf(cx, ts->VOIDX);
#else
                init = nf->createLeaf(cx, ts->VOID);
#endif
            }
            else {
                if (tt == ts->VAR) {
                    // set init to a var list or initial
                    ts->getToken(cx);   // throw away the 'var' token
                    init = variables(cx, ts, source, TRUE);
                }
                else {
                    init = expr(cx, ts, source, TRUE);
                }
            }

            tt = ts->peekToken(cx);
            if (tt == ts->RELOP && ts->getOp() == ts->IN) {
                ts->matchToken(cx, ts->RELOP);
                source->append((jsChar_t) ts->IN);
                // 'cond' is the object over which we're iterating
                cond = expr(cx, ts, source, FALSE);
            }
            else {  // ordinary for loop
                mustMatchToken(cx, ts, ts->SEMI, "msg.no.semi.for");
                source->append((jsChar_t) ts->SEMI);
                if (ts->peekToken(cx) == ts->SEMI) {
                    // no loop condition
#if defined(TARGET_OS_LINUX)
                    cond = nf->createLeaf(cx, ts->VOIDX);
#else
                    cond = nf->createLeaf(cx, ts->VOID);
#endif
                }
                else {
                    cond = expr(cx, ts, source, FALSE);
                }

                mustMatchToken(cx, ts, ts->SEMI, "msg.no.semi.for.cond");
                source->append((jsChar_t) ts->SEMI);
                if (ts->peekToken(cx) == ts->RP) {
#if defined(TARGET_OS_LINUX)
                    incr = nf->createLeaf(cx, ts->VOIDX);
#else
                    incr = nf->createLeaf(cx, ts->VOID);
#endif
                }
                else {
                    incr = expr(cx, ts, source, FALSE);
                }
            }

            mustMatchToken(cx, ts, ts->RP, "msg.no.paren.for.ctrl");
            source->append((jsChar_t) ts->RP);
            source->append((jsChar_t) ts->LC);
            source->append((jsChar_t) ts->EOL);
            body = statement(cx, ts, source);
            source->append((jsChar_t) ts->RC);
            source->append((jsChar_t) ts->EOL);

            if (incr == NULL) {
                // cond could be null if 'in obj' got eaten by the init node.
                pn = nf->createForIn(cx, init, cond, body, lineno);
            }
            else {
                pn = nf->createFor(cx, init, cond, incr, body, lineno);
            }
            break;
        }

    case JSTokenStream::TRY:{
            int lineno = ts->getLineno();

            JSNode *tryblock;
            JSNode *catchblocks = NULL;
            JSNode *finallyblock = NULL;

            skipsemi = TRUE;
            source->append((jsChar_t) ts->TRY);
            source->append((jsChar_t) ts->LC);
            source->append((jsChar_t) ts->EOL);
            tryblock = statement(cx, ts, source);
            source->append((jsChar_t) ts->RC);
            source->append((jsChar_t) ts->EOL);

            catchblocks = nf->createLeaf(cx, JSTokenStream::BLOCK);

            jsBool_t sawDefaultCatch = FALSE;
            int peek = ts->peekToken(cx);
            if (peek == ts->CATCH) {
                while (ts->matchToken(cx, ts->CATCH)) {
                    if (sawDefaultCatch) {
                        reportError(cx, ts, "msg.catch.unreachable");
                    }
                    source->append((jsChar_t) ts->CATCH);
                    mustMatchToken(cx, ts, ts->LP, "msg.no.paren.catch");
                    source->append((jsChar_t) ts->LP);

                    mustMatchToken(cx, ts, ts->NAME, "msg.bad.catchcond");
                    jsString_t varName = JS_NewZoneString(cx, ts->getString());
                    source->addString(ts->NAME, varName);

                    JSNode *catchCond = NULL;
                    if (ts->matchToken(cx, ts->IF)) {
                        source->append((jsChar_t) ts->IF);
                        catchCond = expr(cx, ts, source, FALSE);
                    }
                    else {
                        sawDefaultCatch = TRUE;
                    }

                    mustMatchToken(cx, ts, ts->RP, "msg.bad.catchcond");
                    source->append((jsChar_t) ts->RP);
                    mustMatchToken(cx, ts, ts->LC, "msg.no.brace.catchblock");
                    source->append((jsChar_t) ts->LC);
                    source->append((jsChar_t) ts->EOL);

                    nf->addChildToBack(catchblocks, nf->createCatch(cx, varName, catchCond, statements(cx, ts, source), ts->getLineno()));
                    JS_DeleteZoneObj(cx, varName);

                    mustMatchToken(cx, ts, ts->RC, "msg.no.brace.after.body");
                    source->append((jsChar_t) ts->RC);
                    source->append((jsChar_t) ts->EOL);
                }
            }
            else if (peek != ts->FINALLY) {
                mustMatchToken(cx, ts, ts->FINALLY, "msg.try.no.catchfinally");
            }

            if (ts->matchToken(cx, ts->FINALLY)) {
                source->append((jsChar_t) ts->FINALLY);

                source->append((jsChar_t) ts->LC);
                source->append((jsChar_t) ts->EOL);
                finallyblock = statement(cx, ts, source);
                source->append((jsChar_t) ts->RC);
                source->append((jsChar_t) ts->EOL);
            }

            pn = nf->createTryCatchFinally(cx, tryblock, catchblocks, finallyblock, lineno);

            catchblocks->release(cx);
            break;
        }
    case JSTokenStream::THROW:{
            int lineno = ts->getLineno();
            source->append((jsChar_t) ts->THROW);
            pn = nf->createThrow(cx, expr(cx, ts, source, FALSE), lineno);
            if (lineno == ts->getLineno()) {
                wellTerminated(cx, ts, ts->_ERROR);
            }
            break;
        }
    case JSTokenStream::BREAK:{
            int lineno = ts->getLineno();

            source->append((jsChar_t) ts->BREAK);

            // matchLabel only matches if there is one
            jsString_t label = matchLabel(cx, ts);

            if (label != NULL) {
                source->addString(ts->NAME, label);
            }
            pn = nf->createBreak(cx, label, lineno);
            if (label != NULL) {
                JS_DeleteZoneObj(cx, label);
            }
            break;
        }
    case JSTokenStream::CONTINUE:{
            int lineno = ts->getLineno();

            source->append((jsChar_t) ts->CONTINUE);

            // matchLabel only matches if there is one
            jsString_t label = matchLabel(cx, ts);

            if (label != NULL) {
                source->addString(ts->NAME, label);
            }
            pn = nf->createContinue(cx, label, lineno);
            if (label != NULL) {
                JS_DeleteZoneObj(cx, label);
            }
            break;
        }
    case JSTokenStream::WITH:{
            skipsemi = TRUE;

            source->append((jsChar_t) ts->WITH);
            int lineno = ts->getLineno();
            mustMatchToken(cx, ts, ts->LP, "msg.no.paren.with");
            source->append((jsChar_t) ts->LP);

            JSNode *obj = expr(cx, ts, source, FALSE);

            mustMatchToken(cx, ts, ts->RP, "msg.no.paren.after.with");
            source->append((jsChar_t) ts->RP);
            source->append((jsChar_t) ts->LC);
            source->append((jsChar_t) ts->EOL);

            JSNode *body = statement(cx, ts, source);

            source->append((jsChar_t) ts->RC);
            source->append((jsChar_t) ts->EOL);

            pn = nf->createWith(cx, obj, body, lineno);
            break;
        }
    case JSTokenStream::VAR:{
            int lineno = ts->getLineno();
            pn = variables(cx, ts, source, FALSE);
            if (ts->getLineno() == lineno) {
                wellTerminated(cx, ts, ts->_ERROR);
            }
            break;
        }
    case JSTokenStream::RETURN:{
            JSNode *retExpr = NULL;
            int lineno = 0;

            source->append((jsChar_t) ts->RETURN);

            // bail if we're not in a (toplevel) function
            if ((ts->flags & ts->TSF_FUNCTION) == 0) {
                reportError(cx, ts, "msg.bad.return");
            }

            /* This is ugly, but we don't want to require a semicolon. */
            ts->flags |= ts->TSF_REGEXP;
            tt = ts->peekTokenSameLine(cx);
            ts->flags &= ~ts->TSF_REGEXP;

            if (tt != ts->_EOF && tt != ts->EOL && tt != ts->SEMI && tt != ts->RC) {
                lineno = ts->getLineno();
                retExpr = expr(cx, ts, source, FALSE);
                if (ts->getLineno() == lineno)
                    wellTerminated(cx, ts, ts->_ERROR);
                ts->flags |= ts->TSF_RETURN_EXPR;
            }
            else {
                ts->flags |= ts->TSF_RETURN_VOID;
            }

            // XXX ASSERT pn
            pn = nf->createReturn(cx, retExpr, lineno);
            break;
        }
    case JSTokenStream::LC:
        skipsemi = TRUE;

        pn = statements(cx, ts, source);
        mustMatchToken(cx, ts, ts->RC, "msg.no.brace.block");
        break;

    case JSTokenStream::_ERROR:
        // Fall thru, to have a node for error recovery to work on
    case JSTokenStream::EOL:
    case JSTokenStream::SEMI:
#if defined(TARGET_OS_LINUX)
        pn = nf->createLeaf(cx, ts->VOIDX);
#else
        pn = nf->createLeaf(cx, ts->VOID);
#endif
        skipsemi = TRUE;
        break;

    default:{
            lastExprType = tt;
            int tokenno = ts->getTokenno();
            ts->ungetToken(cx, tt);
            int lineno = ts->getLineno();

            pn = expr(cx, ts, source, FALSE);

            if (ts->peekToken(cx) == ts->COLON) {
                /* check that the last thing the tokenizer returned was a
                 * NAME and that only one token was consumed.
                 */
                if (lastExprType != ts->NAME || (ts->getTokenno() != tokenno)) {
                    reportError(cx, ts, "msg.bad.label");
                }

                ts->getToken(cx);   // eat the COLON

                /* in the C source, the label is associated with the
                 * statement that follows:
                 *                nf->addChildToBack(pn, statement(ts));
                 */
                jsString_t name = ts->getString();
                pn = nf->createLabel(cx, name, lineno);

                // depend on decompiling lookahead to guess that that
                // last name was a label.
                source->append((jsChar_t) ts->COLON);
                source->append((jsChar_t) ts->EOL);
                return pn;
            }

            if (lastExprType == ts->FUNCTION) {
                nf->setFunctionExpressionStatement(cx, pn);
            }

            pn = nf->createExprStatement(cx, pn, lineno);

            /*
             * Check explicitly against (multi-line) function
             * statement.

             * lastExprEndLine is a hack to fix an
             * automatic semicolon insertion problem with function
             * expressions; the ts->getLineno() == lineno check was
             * firing after a function definition even though the
             * next statement was on a new line, because
             * speculative getToken calls advanced the line number
             * even when they didn't succeed.
             */
            if (ts->getLineno() == lineno || (lastExprType == ts->FUNCTION && ts->getLineno() == lastExprEndLine)) {
                wellTerminated(cx, ts, lastExprType);
            }
            break;
        }
    }
    ts->matchToken(cx, ts->SEMI);
    if (!skipsemi) {
        source->append((jsChar_t) ts->SEMI);
        source->append((jsChar_t) ts->EOL);
    }

    return pn;
}

JSNode *JSParser::variables(JSContext * cx, JSTokenStream * ts, JSSource * source, jsBool_t inForInit)
{
    JSNode *pn = nf->createVariables(cx, ts->getLineno());
    jsBool_t first = TRUE;

    source->append((jsChar_t) ts->VAR);

    for (;;) {
        JSNode *name;
        JSNode *init;

        mustMatchToken(cx, ts, ts->NAME, "msg.bad.var");
        jsString_t s = ts->getString();

        if (!first) {
            source->append((jsChar_t) ts->COMMA);
        }
        first = FALSE;

        source->addString(ts->NAME, s);
        name = nf->createName(cx, s);

        // omitted check for argument hiding

        if (ts->matchToken(cx, ts->ASSIGN)) {
            if (ts->getOp() != ts->NOP) {
                reportError(cx, ts, "msg.bad.var.init");
            }

            source->append((jsChar_t) ts->ASSIGN);
            source->append((jsChar_t) ts->NOP);

            init = assignExpr(cx, ts, source, inForInit);
            nf->addChildToBack(name, init);
        }
        nf->addChildToBack(pn, name);
        if (!ts->matchToken(cx, ts->COMMA)) {
            break;
        }
    }
    return pn;
}

JSNode *JSParser::expr(JSContext * cx, JSTokenStream * ts, JSSource * source, jsBool_t inForInit)
{
    JSNode *pn = assignExpr(cx, ts, source, inForInit);

    while (ts->matchToken(cx, ts->COMMA)) {
        source->append((jsChar_t) ts->COMMA);
        pn = nf->createBinary(cx, ts->COMMA, pn, assignExpr(cx, ts, source, inForInit));
    }
    return pn;
}

JSNode *JSParser::assignExpr(JSContext * cx, JSTokenStream * ts, JSSource * source, jsBool_t inForInit)
{
    JSNode *pn = condExpr(cx, ts, source, inForInit);

    if (ts->matchToken(cx, ts->ASSIGN)) {
        // omitted: "invalid assignment left-hand side" check.
        int op = ts->getOp();

        source->append((jsChar_t) ts->ASSIGN);
        source->append((jsChar_t) op);
        pn = nf->createBinary(cx, ts->ASSIGN, op, pn, assignExpr(cx, ts, source, inForInit));
    }

    return pn;
}

JSNode *JSParser::condExpr(JSContext * cx, JSTokenStream * ts, JSSource * source, jsBool_t inForInit)
{
    JSNode *ifTrue;
    JSNode *ifFalse;

    JSNode *pn = orExpr(cx, ts, source, inForInit);

    if (ts->matchToken(cx, ts->HOOK)) {
        source->append((jsChar_t) ts->HOOK);
        ifTrue = assignExpr(cx, ts, source, FALSE);
        mustMatchToken(cx, ts, ts->COLON, "msg.no.colon.cond");
        source->append((jsChar_t) ts->COLON);
        ifFalse = assignExpr(cx, ts, source, inForInit);
        return nf->createTernary(cx, pn, ifTrue, ifFalse);
    }

    return pn;
}

JSNode *JSParser::orExpr(JSContext * cx, JSTokenStream * ts, JSSource * source, jsBool_t inForInit)
{
    JSNode *pn = andExpr(cx, ts, source, inForInit);

    if (ts->matchToken(cx, ts->OR)) {
        source->append((jsChar_t) ts->OR);
        pn = nf->createBinary(cx, ts->OR, pn, orExpr(cx, ts, source, inForInit));
    }

    return pn;
}

JSNode *JSParser::andExpr(JSContext * cx, JSTokenStream * ts, JSSource * source, jsBool_t inForInit)
{
    JSNode *pn = bitOrExpr(cx, ts, source, inForInit);

    if (ts->matchToken(cx, ts->AND)) {
        source->append((jsChar_t) ts->AND);
        pn = nf->createBinary(cx, ts->AND, pn, andExpr(cx, ts, source, inForInit));
    }

    return pn;
}

JSNode *JSParser::bitOrExpr(JSContext * cx, JSTokenStream * ts, JSSource * source, jsBool_t inForInit)
{
    JSNode *pn = bitXorExpr(cx, ts, source, inForInit);

    while (ts->matchToken(cx, ts->BITOR)) {
        source->append((jsChar_t) ts->BITOR);
        pn = nf->createBinary(cx, ts->BITOR, pn, bitXorExpr(cx, ts, source, inForInit));
    }
    return pn;
}

JSNode *JSParser::bitXorExpr(JSContext * cx, JSTokenStream * ts, JSSource * source, jsBool_t inForInit)
{
    JSNode *pn = bitAndExpr(cx, ts, source, inForInit);

    while (ts->matchToken(cx, ts->BITXOR)) {
        source->append((jsChar_t) ts->BITXOR);
        pn = nf->createBinary(cx, ts->BITXOR, pn, bitAndExpr(cx, ts, source, inForInit));
    }
    return pn;
}

JSNode *JSParser::bitAndExpr(JSContext * cx, JSTokenStream * ts, JSSource * source, jsBool_t inForInit)
{
    JSNode *pn = eqExpr(cx, ts, source, inForInit);

    while (ts->matchToken(cx, ts->BITAND)) {
        source->append((jsChar_t) ts->BITAND);
        pn = nf->createBinary(cx, ts->BITAND, pn, eqExpr(cx, ts, source, inForInit));
    }
    return pn;
}

JSNode *JSParser::eqExpr(JSContext * cx, JSTokenStream * ts, JSSource * source, jsBool_t inForInit)
{
    JSNode *pn = relExpr(cx, ts, source, inForInit);

    while (ts->matchToken(cx, ts->EQOP)) {
        int op = ts->getOp();

        source->append((jsChar_t) ts->EQOP);
        source->append((jsChar_t) op);
        pn = nf->createBinary(cx, ts->EQOP, op, pn, relExpr(cx, ts, source, inForInit));
    }
    return pn;
}

JSNode *JSParser::relExpr(JSContext * cx, JSTokenStream * ts, JSSource * source, jsBool_t inForInit)
{
    JSNode *pn = shiftExpr(cx, ts, source);

    while (ts->matchToken(cx, ts->RELOP)) {
        int op = ts->getOp();
        if (inForInit && op == ts->IN) {
            ts->ungetToken(cx, ts->RELOP);
            break;
        }
        source->append((jsChar_t) ts->RELOP);
        source->append((jsChar_t) op);
        pn = nf->createBinary(cx, ts->RELOP, op, pn, shiftExpr(cx, ts, source));
    }
    return pn;
}

JSNode *JSParser::shiftExpr(JSContext * cx, JSTokenStream * ts, JSSource * source)
{
    JSNode *pn = addExpr(cx, ts, source);

    while (ts->matchToken(cx, ts->SHOP)) {
        int op = ts->getOp();

        source->append((jsChar_t) ts->SHOP);
        source->append((jsChar_t) op);
        pn = nf->createBinary(cx, op, pn, addExpr(cx, ts, source));
    }
    return pn;
}

JSNode *JSParser::addExpr(JSContext * cx, JSTokenStream * ts, JSSource * source)
{
    int tt;
    JSNode *pn = mulExpr(cx, ts, source);

    while ((tt = ts->getToken(cx)) == ts->ADD || tt == ts->SUB) {
        source->append((jsChar_t) tt);
        // flushNewLines
        pn = nf->createBinary(cx, tt, pn, mulExpr(cx, ts, source));
    }
    ts->ungetToken(cx, tt);

    return pn;
}

JSNode *JSParser::mulExpr(JSContext * cx, JSTokenStream * ts, JSSource * source)
{
    int tt;

    JSNode *pn = unaryExpr(cx, ts, source);

    while ((tt = ts->peekToken(cx)) == ts->MUL || tt == ts->DIV || tt == ts->MOD) {
        tt = ts->getToken(cx);
        source->append((jsChar_t) tt);
        pn = nf->createBinary(cx, tt, pn, unaryExpr(cx, ts, source));
    }

    return pn;
}

JSNode *JSParser::unaryExpr(JSContext * cx, JSTokenStream * ts, JSSource * source)
{
    int tt;

    ts->flags |= ts->TSF_REGEXP;
    tt = ts->getToken(cx);
    ts->flags &= ~ts->TSF_REGEXP;

    switch (tt) {
    case JSTokenStream::UNARYOP:{
            int op = ts->getOp();

            source->append((jsChar_t) ts->UNARYOP);
            source->append((jsChar_t) op);
            return nf->createUnary(cx, ts->UNARYOP, op, unaryExpr(cx, ts, source));
        }
    case JSTokenStream::ADD:
    case JSTokenStream::SUB:
        source->append((jsChar_t) ts->UNARYOP);
        source->append((jsChar_t) tt);
        return nf->createUnary(cx, ts->UNARYOP, tt, unaryExpr(cx, ts, source));

    case JSTokenStream::INC:
    case JSTokenStream::DEC:
        source->append((jsChar_t) tt);
        return nf->createUnary(cx, tt, ts->PRE, memberExpr(cx, ts, source, TRUE));

    case JSTokenStream::DELPROP:
        source->append((jsChar_t) ts->DELPROP);
        return nf->createUnary(cx, ts->DELPROP, unaryExpr(cx, ts, source));

    case JSTokenStream::_ERROR:
        break;

    default:
        ts->ungetToken(cx, tt);

        int lineno = ts->getLineno();

        JSNode *pn = memberExpr(cx, ts, source, TRUE);

        /* don't look across a newline boundary for a postfix incop.

         * the rhino scanner seems to work differently than the js
         * scanner here; in js, it works to have the line number check
         * precede the peekToken calls.  It'd be better if they had
         * similar behavior...
         */
        int peeked;

        if (((peeked = ts->peekToken(cx)) == ts->INC || peeked == ts->DEC) && ts->getLineno() == lineno) {
            int pf = ts->getToken(cx);
            source->append((jsChar_t) pf);
            return nf->createUnary(cx, pf, ts->POST, pn);
        }
        return pn;
    }
    return nf->createName(cx, "err");   // Only reached on error.
    // Try to continue.
}

JSNode *JSParser::argumentList(JSContext * cx, JSTokenStream * ts, JSSource * source, JSNode * listNode)
{
    jsBool_t matched;

    ts->flags |= ts->TSF_REGEXP;
    matched = ts->matchToken(cx, ts->RP);
    ts->flags &= ~ts->TSF_REGEXP;
    if (!matched) {
        jsBool_t first = TRUE;

        do {
            if (!first) {
                source->append((jsChar_t) ts->COMMA);
            }
            first = FALSE;
            nf->addChildToBack(listNode, assignExpr(cx, ts, source, FALSE));
        } while (ts->matchToken(cx, ts->COMMA));

        mustMatchToken(cx, ts, ts->RP, "msg.no.paren.arg");
    }
    source->append((jsChar_t) ts->RP);
    return listNode;
}

JSNode *JSParser::memberExpr(JSContext * cx, JSTokenStream * ts, JSSource * source, jsBool_t allowCallSyntax)
{
    int tt;

    JSNode *pn;

    /* Check for new expressions. */
    ts->flags |= ts->TSF_REGEXP;
    tt = ts->peekToken(cx);
    ts->flags &= ~ts->TSF_REGEXP;
    if (tt == ts->NEW) {
        /* Eat the NEW token. */
        ts->getToken(cx);
        source->append((jsChar_t) ts->NEW);

        /* Make a NEW node to append to. */
        pn = nf->createLeaf(cx, ts->NEW);
        nf->addChildToBack(pn, memberExpr(cx, ts, source, FALSE));

        if (ts->matchToken(cx, ts->LP)) {
            source->append((jsChar_t) ts->LP);
            /* Add the arguments to pn, if any are supplied. */
            pn = argumentList(cx, ts, source, pn);
        }

        /* XXX there's a check in the C source against
         * "too many constructor arguments" - how many
         * do we claim to support?
         */

        /* Experimental syntax:  allow an object literal to follow a new
         * expression, which will mean a kind of anonymous class built with
         * the JavaAdapter.
         * the object literal will be passed as an additional argument to the
         * constructor.
         */
        tt = ts->peekToken(cx);
        if (tt == ts->LC) {
            nf->addChildToBack(pn, primaryExpr(cx, ts, source));
        }
    }
    else {
        pn = primaryExpr(cx, ts, source);
    }

    lastExprEndLine = ts->getLineno();
    while ((tt = ts->getToken(cx)) > ts->_EOF) {
        if (tt == ts->DOT) {
            source->append((jsChar_t) ts->DOT);
            mustMatchToken(cx, ts, ts->NAME, "msg.no.name.after.dot");
            jsString_t s = ts->getString();
            source->addString(ts->NAME, s);
            pn = nf->createBinary(cx, ts->DOT, pn, nf->createName(cx, ts->getString()));
            /* pn = nf->createBinary(ts->DOT, pn, memberExpr(ts))
             * is the version in Brendan's IR C version.  Not in ECMA...
             * does it reflect the 'new' operator syntax he mentioned?
             */
            lastExprEndLine = ts->getLineno();
        }
        else if (tt == ts->LB) {
            source->append((jsChar_t) ts->LB);
            pn = nf->createBinary(cx, ts->LB, pn, expr(cx, ts, source, FALSE));

            mustMatchToken(cx, ts, ts->RB, "msg.no.bracket.index");
            source->append((jsChar_t) ts->RB);
            lastExprEndLine = ts->getLineno();
        }
        else if (allowCallSyntax && tt == ts->LP) {
            /* make a call node */

            pn = nf->createUnary(cx, ts->CALL, pn);
            source->append((jsChar_t) ts->LP);

            /* Add the arguments to pn, if any are supplied. */
            pn = argumentList(cx, ts, source, pn);
            lastExprEndLine = ts->getLineno();
        }
        else {
            ts->ungetToken(cx, tt);

            break;
        }
    }

    return pn;
}

JSNode *JSParser::primaryExpr(JSContext * cx, JSTokenStream * ts, JSSource * source)
{
    int tt;

    JSNode *pn;

    ts->flags |= ts->TSF_REGEXP;
    tt = ts->getToken(cx);
    ts->flags &= ~ts->TSF_REGEXP;

    switch (tt) {

    case JSTokenStream::FUNCTION:
        return function(cx, ts, source, TRUE);

    case JSTokenStream::LB:
        {
            source->append((jsChar_t) ts->LB);
            pn = nf->createLeaf(cx, ts->ARRAYLIT);

            ts->flags |= ts->TSF_REGEXP;

            jsBool_t matched = ts->matchToken(cx, ts->RB);

            ts->flags &= ~ts->TSF_REGEXP;

            if (!matched) {
                jsBool_t first = TRUE;
                do {
                    ts->flags |= ts->TSF_REGEXP;
                    tt = ts->peekToken(cx);
                    ts->flags &= ~ts->TSF_REGEXP;

                    if (!first) {
                        source->append((jsChar_t) ts->COMMA);
                    }
                    else {
                        first = FALSE;
                    }

                    if (tt == ts->RB) { // to fix [,,,].length behavior...
                        break;
                    }

                    if (tt == ts->COMMA) {
                        nf->addChildToBack(pn, nf->createLeaf(cx, ts->PRIMARY, ts->UNDEFINED));
                    }
                    else {
                        nf->addChildToBack(pn, assignExpr(cx, ts, source, FALSE));
                    }

                } while (ts->matchToken(cx, ts->COMMA));
                mustMatchToken(cx, ts, ts->RB, "msg.no.bracket.arg");
            }
            source->append((jsChar_t) ts->RB);

            JSNode *result = nf->createArrayLiteral(cx, pn);

            pn->removeChildren();
            pn->release(cx);
            return result;
        }

    case JSTokenStream::LC:{
            pn = nf->createLeaf(cx, ts->OBJLIT);

            source->append((jsChar_t) ts->LC);
            if (!ts->matchToken(cx, ts->RC)) {

                jsBool_t first = TRUE;
              commaloop:
                do {
                    JSNode *property;

                    if (!first) {
                        source->append((jsChar_t) ts->COMMA);
                    }
                    else {
                        first = FALSE;
                    }

                    tt = ts->getToken(cx);
                    switch (tt) {
                        // map NAMEs to STRINGs in object literal context.
                    case JSTokenStream::NAME:
                    case JSTokenStream::STRING:{
                            jsString_t s = ts->getString();

                            source->addString(ts->NAME, s);
                            property = nf->createString(cx, ts->getString());
                            break;
                        }
                    case JSTokenStream::NUMBER:{
                            double n = ts->getNumber();

                            source->addNumber(n);
                            property = nf->createNumber(cx, n);
                            break;
                        }
                    case JSTokenStream::RC:
                        // trailing comma is OK.
                        ts->ungetToken(cx, tt);
                        goto commaloop;
                    default:
                        reportError(cx, ts, "msg.bad.prop");
                        goto commaloop;
                    }
                    mustMatchToken(cx, ts, ts->COLON, "msg.no.colon.prop");

                    // OBJLIT is used as ':' in object literal for
                    // decompilation to solve spacing ambiguity.
                    source->append((jsChar_t) ts->OBJLIT);
                    nf->addChildToBack(pn, property);
                    nf->addChildToBack(pn, assignExpr(cx, ts, source, FALSE));

                } while (ts->matchToken(cx, ts->COMMA));

                mustMatchToken(cx, ts, ts->RC, "msg.no.brace.prop");
            }
            source->append((jsChar_t) ts->RC);

            JSNode *result = nf->createObjectLiteral(cx, pn);

            pn->removeChildren();
            pn->release(cx);
            return result;
        }

    case JSTokenStream::LP:

        /* Brendan's IR-jsparse.c makes a new node tagged with
         * TOK_LP here... I'm not sure I understand why.  Isn't
         * the grouping already implicit in the structure of the
         * parse tree?  also TOK_LP is already overloaded (I
         * think) in the C IR as 'function call.'  */
        source->append((jsChar_t) ts->LP);
        pn = expr(cx, ts, source, FALSE);
        source->append((jsChar_t) ts->RP);
        mustMatchToken(cx, ts, ts->RP, "msg.no.paren");
        return pn;

    case JSTokenStream::NAME:{
            jsString_t name = ts->getString();

            source->addString(ts->NAME, name);
            return nf->createName(cx, name);
        }

    case JSTokenStream::NUMBER:{
            double n = ts->getNumber();

            source->addNumber(n);
            return nf->createNumber(cx, n);
        }

    case JSTokenStream::STRING:{
            jsString_t s = ts->getString();

            source->addString(ts->STRING, s);
            return nf->createString(cx, s);
        }

    case JSTokenStream::OBJECT:{
            jsString_t flags = ts->regExpFlags;

            ts->regExpFlags = NULL;

            jsString_t re = ts->getString();

	    if (re == NULL) re = "";

            int length = strlen(re) + strlen(flags) + TableSize("//") - 1 + 1;
            jsString_t s = (jsString_t) JS_NewZoneObj(cx, sizeof(*s) * length);

            sprintf(s, "/%s/%s", re, flags);
            source->addString(ts->OBJECT, s);
            JS_DeleteZoneObj(cx, s);
            return nf->createRegExp(cx, re, flags);
        }

    case JSTokenStream::PRIMARY:
        source->append((jsChar_t) ts->PRIMARY);
        source->append((jsChar_t) ts->getOp());
        return nf->createLeaf(cx, ts->PRIMARY, ts->getOp());

    case JSTokenStream::RESERVED:
        reportError(cx, ts, "msg.reserved.id");
        break;

    case JSTokenStream::_ERROR:
        /* the scanner or one of its subroutines reported the error. */
        break;

    default:
        reportError(cx, ts, "msg.syntax");
        break;

    }
    return NULL;    // should never reach here
}

/*
 * Build a parse tree from the given JSTokenStream.  Creates a tree
 * by making calls to the NodeFactory object that was used to
 * construct the Parser.
 *
 * @param ts the JSTokenStream to parse
 *
 * @return an Object representing the parsed
 * program.  If the parse fails, null will be returned.  (The
 * parse failure will result in a call to the current Context's
 * ErrorReporter.)
 */
JSNode *JSParser::parse(JSContext * cx, JSTokenStream * ts)
{
#ifdef VX
    this->ok = TRUE;
    JSSource source(cx);

    int tt; // last token from getToken();
    int baseLineno = ts->getLineno();   // line number where source starts

    /* so we have something to add nodes to until
     * we've collected all the source */
    JSNode *tempBlock = nf->createLeaf(cx, JSTokenStream::BLOCK);

    JSException *ex;

    if ((ex = enterTry(cx)) == NULL) {
        while (TRUE) {
            ts->flags |= ts->TSF_REGEXP;
            tt = ts->getToken(cx);
            ts->flags &= ~ts->TSF_REGEXP;

            if (tt <= ts->_EOF) {
                break;
            }

            if (tt == ts->FUNCTION) {
                nf->addChildToBack(tempBlock, function(cx, ts, &source, FALSE));
                /* function doesn't add its own final EOL,
                 * because it gets SEMI + EOL from Statement when it's
                 * a nested function; so we need to explicitly add an
                 * EOL here.
                 */
                source.append((jsChar_t) ts->EOL);
                wellTerminated(cx, ts, ts->FUNCTION);
            }
            else {
                ts->ungetToken(cx, tt);
                nf->addChildToBack(tempBlock, statement(cx, ts, &source));
            }
        }
        cx->exceptionHandler->exitTry();

        int sourceLength = source.buf.length();
        JSNode *pn = nf->createScript(cx, tempBlock, ts->getSourceName(),
                baseLineno, ts->getLineno(),
                source.buf.offString(), sourceLength);
        // add next line to delete tempBlock
        tempBlock->removeChildren();
        tempBlock->release(cx);

        return pn;
    }
    else {
        return NULL;
    }
#else /* !VX */
    this->ok = TRUE;
    JSSource source(cx);

    int tt; // last token from getToken();
    int baseLineno = ts->getLineno();   // line number where source starts

    /* so we have something to add nodes to until
     * we've collected all the source */
    JSNode *tempBlock = nf->createLeaf(cx, JSTokenStream::BLOCK);

    while (TRUE) {
        ts->flags |= ts->TSF_REGEXP;
        tt = ts->getToken(cx);
        ts->flags &= ~ts->TSF_REGEXP;

        if (tt <= ts->_EOF) {
            break;
        }

        if (tt == ts->FUNCTION) {
            JSException *ex;

            if ((ex = enterTry(cx)) == NULL) {
                nf->addChildToBack(tempBlock, function(cx, ts, &source, FALSE));
                /* function doesn't add its own final EOL,
                 * because it gets SEMI + EOL from Statement when it's
                 * a nested function; so we need to explicitly add an
                 * EOL here.
                 */
                source.append((jsChar_t) ts->EOL);
                wellTerminated(cx, ts, ts->FUNCTION);
                cx->exceptionHandler->exitTry();
            }
            else if (ex->isInstanceOf(JSJavaScriptException::className)) {
                this->ok = FALSE;
                break;
            }
            else {
                cx->exceptionHandler->throwEx(ex);
            }
        }
        else {
            ts->ungetToken(cx, tt);
            nf->addChildToBack(tempBlock, statement(cx, ts, &source));
        }
    }

    if (!this->ok) {
        // XXX ts->clearPushback() call here?
        return NULL;
    }

    int sourceLength = source.buf.length();
    JSNode *pn = nf->createScript(cx, tempBlock, ts->getSourceName(),
            baseLineno, ts->getLineno(),
            source.buf.offString(), sourceLength);
    // add next lines to delete tempBlock
    tempBlock->removeChildren();
    tempBlock->release(cx);

    return pn;
#endif /* !VX */
}

JSParser::JSParser(JSIRFactory * nf)
{
    this->nf = nf;
}
