NULL, JSREPORT_ERROR,
JSREPORT_ERROR,
static REParser ParseAtom;/* * Top-down regular expression grammar, based closely on Perl4. * *  regexp:     altern                  A regular expression is one or more *              altern '|' regexp       alternatives separated by vertical bar. */static RENode *ParseRegExp(CompilerState *state){    RENode *ren, *kid, *ren1, *ren2;    const jschar *cp;    ren = ParseAltern(state);    if (!ren)	return NULL;    cp = state->cp;    if ((cp < state->cpend) && (*cp == '|')) {	kid = ren;	ren = NewRENode(state, REOP_ALT, kid);	if (!ren)	    return NULL;	ren->flags = kid->flags & (RENODE_ANCHORED | RENODE_NONEMPTY);	ren1 = ren;	do {	    /* (balance: */	    state->cp = ++cp;	    if (*cp == '|' || *cp == ')') {		kid = NewRENode(state, REOP_EMPTY, NULL);	    } else {		kid = ParseAltern(state);		cp = state->cp;	    }	    if (!kid)		return NULL;	    ren2 = NewRENode(state, REOP_ALT, kid);	    if (!ren2)		return NULL;	    ren1->next = ren2;	    ren1->flags |= RENODE_GOODNEXT;	    ren2->flags = (kid->flags & (RENODE_ANCHORED | RENODE_NONEMPTY))			  | RENODE_ISNEXT;	    ren1 = ren2;	} while ((cp < state->cpend) && (*cp == '|'));    }    return ren;}/* *  altern:     item                    An alternative is one or more items, *              item altern             concatenated together. */static RENode *ParseAltern(CompilerState *state){    RENode *ren, *ren1, *ren2;    uintN flags;    const jschar *cp;    jschar c;    ren = ren1 = ParseItem(state);    if (!ren)	return NULL;    flags = 0;    cp = state->cp;    /* (balance: */    while ((cp < state->cpend) && (c = *cp) != '|' && c != ')') {	ren2 = ParseItem(state);	if (!ren2)	    return NULL;	if (!SetNext(state, ren1, ren2))	    return NULL;	flags |= ren2->flags;	ren1 = ren2;	cp = state->cp;    }    /*     * 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. */static RENode *ParseItem(CompilerState *state){    const jschar *cp;    RENode *ren;    REOp op;    cp = state->cp;    if (cp < state->cpend)        switch (*cp) {          case '^':	    state->cp = cp + 1;	    ren = NewRENode(state, REOP_BOL, NULL);	    if (ren)	        ren->flags |= RENODE_ANCHORED;	    return ren;          case '$':	    state->cp = cp + 1;	    return NewRENode(state,			     (cp == state->cpbegin ||			      ((cp[-1] == '(' || cp[-1] == '|') && /*balance)*/			       (cp - 1 == state->cpbegin || cp[-2] != '\\')))			     ? REOP_EOLONLY			     : REOP_EOL,			     NULL);          case '\\':	    switch (*++cp) {	      case 'b':	        op = REOP_WBDRY;	        break;	      case 'B':	        op = REOP_WNONBDRY;	        break;	      default:	        return ParseQuantAtom(state);	    }	    /*	     * Word boundaries and non-boundaries are flagged as non-empty so they	     * will be prefixed by an anchoring node.	     */	    state->cp = cp + 1;	    ren = NewRENode(state, op, NULL);	    if (ren)	        ren->flags |= RENODE_NONEMPTY;	    return ren;          default:;        }    return ParseQuantAtom(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*/static RENode *ParseQuantAtom(CompilerState *state){    RENode *ren, *ren2;    const jschar *cp, *up;    jschar c;    uint32 min, max;    ren = ParseAtom(state);    if (!ren)	return NULL;    cp = state->cp;loop:    if (cp < state->cpend)        switch (*cp) {          case '{':	    c = *++cp;	    if (!JS7_ISDEC(c)) {                js_ReportCompileErrorNumber(state->context, state->tokenStream,                                            NULL, JSREPORT_ERROR,                                            JSMSG_BAD_QUANTIFIER, state->cp);	        return NULL;	    }	    min = (uint32)JS7_UNDEC(c);	    for (c = *++cp; JS7_ISDEC(c); c = *++cp) {	        min = 10 * min + (uint32)JS7_UNDEC(c);	        if (min >> 16) {                    js_ReportCompileErrorNumber(state->context,                                                state->tokenStream, NULL,                                                JSREPORT_ERROR,                                                JSMSG_MIN_TOO_BIG, state->cp);		    return NULL;	        }	    }	    if (*cp == ',') {	        up = ++cp;	        if (JS7_ISDEC(*cp)) {		    max = (uint32)JS7_UNDEC(*cp);		    for (c = *++cp; JS7_ISDEC(c); c = *++cp) {		        max = 10 * max + (uint32)JS7_UNDEC(c);		        if (max >> 16) {                            js_ReportCompileErrorNumber(state->context,                                                        state->tokenStream,                                                        NULL,                                                        JSREPORT_ERROR,                                                        JSMSG_MAX_TOO_BIG, up);			    return NULL;		        }		    }		    if (max == 0)		        goto zero_quant;		    if (min > max) {                        js_ReportCompileErrorNumber(state->context,                                                    state->tokenStream,                                                    NULL,                                                    JSREPORT_ERROR,                                                    JSMSG_OUT_OF_ORDER, up);		        return NULL;		    }	        } else {		    /* 0 means no upper bound. */		    max = 0;	        }	    } else {	        /* Exactly n times. */	        if (min == 0) {          zero_quant:                    js_ReportCompileErrorNumber(state->context,                                                state->tokenStream,                                                NULL,                                                JSREPORT_ERROR,                                                JSMSG_ZERO_QUANTIFIER,                                                state->cp);		    return NULL;	        }	        max = min;	    }	    if (*cp != '}') {                js_ReportCompileErrorNumber(state->context,                                            state->tokenStream,                                            NULL,                                            JSREPORT_ERROR,                                            JSMSG_UNTERM_QUANTIFIER, state->cp);	        return NULL;	    }	    cp++;	    ren2 = NewRENode(state, REOP_QUANT, ren);	    if (!ren2)	        return NULL;	    if (min > 0 && (ren->flags & RENODE_NONEMPTY))	        ren2->flags |= RENODE_NONEMPTY;	    ren2->u.range.min = (uint16)min;	    ren2->u.range.max = (uint16)max;	    ren = ren2;            goto parseMinimalFlag;          case '*':	    cp++; 	    ren = NewRENode(state, REOP_STAR, ren);    parseMinimalFlag :            if (*cp == '?') {                ren->flags |= RENODE_MINIMAL;                cp++;            }	    goto loop;          case '+':	    cp++;	    ren2 = NewRENode(state, REOP_PLUS, ren);	    if (!ren2)	        return NULL;	    if (ren->flags & RENODE_NONEMPTY)	        ren2->flags |= RENODE_NONEMPTY;	    ren = ren2;	    goto parseMinimalFlag;          case '?':	    cp++;	    ren = NewRENode(state, REOP_OPT, ren);	    goto parseMinimalFlag;        }    state->cp = cp;    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. */static jschar metachars[] = {    '|', '^', '$', '{', '*', '+', '?', '(', ')', '.', '[', '\\', '}', 0};static jschar closurechars[] = {    '{', '*', '+', '?', 0	/* balance} */};static RENode *ParseAtom(CompilerState *state){    const jschar *cp, *ocp;    uintN tmp, num, len;    RENode *ren, *ren2;    jschar c;    REOp op;    cp = ocp = state->cp;        /* handle /|a/ by returning an empty node for the leftside */    if ((cp == state->cpend) || (*cp == '|'))        return NewRENode(state, REOP_EMPTY, NULL);    ren = NULL; /* suppress warning */    switch (*cp) {      case '(':        num = -1;   /* suppress warning */        op = REOP_END;        if (cp[1] == '?') {            switch (cp[2]) {                case ':' :                    op = REOP_LPARENNON;                    break;                case '=' :                    op = REOP_ASSERT;                    break;                case '!' :                    op = REOP_ASSERT_NOT;                    break;            }        }        if (op == REOP_END) {	    num = state->parenCount++;	/* \1 is numbered 0, etc. */            op = REOP_LPAREN;	    state->cp = cp + 1;        }        else            state->cp = cp + 3;	ren2 = ParseRegExp(state);	if (!ren2)	    return NULL;	cp = state->cp;	if (*cp != ')') {            js_ReportCompileErrorNumber(state->context, state->tokenStream,                                        NULL,                                        JSREPORT_ERROR,                                        JSMSG_MISSING_PAREN, ocp);	    return NULL;	}	cp++;	ren = NewRENode(state, op, ren2);	if (!ren)	    return NULL;	ren->flags = ren2->flags & (RENODE_ANCHORED | RENODE_NONEMPTY);	ren->u.num = num;        if ((op == REOP_LPAREN) || (op == REOP_LPARENNON)) {                /* Assume RPAREN ops immediately succeed LPAREN ops */	    ren2 = NewRENode(state, (REOp)(op + 1), NULL);	    if (!ren2 || !SetNext(state, ren, ren2))	        return NULL;	    ren2->u.num = num;        }        break;      case '.':	cp++;        op = REOP_DOT;        if (*cp == '*') {	    cp++;            op = REOP_DOTSTAR;            if (*cp == '?') {                cp++;                op = REOP_DOTSTARMIN;            }        }	ren = NewRENode(state, op, NULL);	if (ren && op == REOP_DOT)	    ren->flags = RENODE_SINGLE | RENODE_NONEMPTY;	break;      case '[':        ++cp;        ren = NewRENode(state, REOP_CCLASS, (void *)cp);	if (!ren)	    return NULL;        while ((c = *++cp) != ']') {	    if (cp == state->cpend) {                js_ReportCompileErrorNumber(state->context, state->tokenStream,                                            NULL,                                            JSREPORT_ERROR,                                            JSMSG_UNTERM_CLASS, ocp);		return NULL;	    }	    if (c == '\\' && (cp+1 != state->cpend))		cp++;	}	ren->u.kid2 = (void *)cp++;        ren->u.ucclass.bitmap = NULL;	/* Since we rule out [] and [^], we can set the non-empty flag. */	ren->flags = RENODE_SINGLE | RENODE_NONEMPTY;	break;      case '\\':	c = *++cp;        if (cp == state->cpend) {            js_ReportCompileErrorNumber(state->context, state->tokenStream,                                        NULL,                                        JSREPORT_ERROR,                                        JSMSG_TRAILING_SLASH);	    return NULL;        }	switch (c) {	  case 'f':	  case 'n':	  case 'r':	  case 't':	  case 'v':	    ren = NewRENode(state, REOP_FLAT1, NULL);	    c = js_strchr(js_EscapeMap, c)[-1];	    break;	  case 'd':	    ren = NewRENode(state, REOP_DIGIT, NULL);	    break;	  case 'D':	    ren = NewRENode(state, REOP_NONDIGIT, NULL);	    break;	  case 'w':	    ren = NewRENode(state, REOP_ALNUM, NULL);	    break;	  case 'W':	    ren = NewRENode(state, REOP_NONALNUM, NULL);	    break;	  case 's':	    ren = NewRENode(state, REOP_SPACE, NULL);	    break;	  case 'S':	    ren = NewRENode(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->context->version != JSVERSION_DEFAULT &&			      state->context->version <= JSVERSION_1_4) {                switch (c) {	          case '0':do_octal:	            num = 0;	            while ('0' <= (c = *++cp) && c <= '7') {		        tmp = 8 * num + (uintN)JS7_UNDEC(c);		        if (tmp > 0377)		            break;		        num = tmp;	            }	            cp--;	            ren = NewRENode(state, REOP_FLAT1, NULL);	            c = (jschar)num;	            break;	          case '1':	          case '2':	          case '3':	          case '4':	          case '5':	          case '6':	          case '7':	          case '8':	          case '9':	            num = (uintN)JS7_UNDEC(c);	            tmp = 1;                    for (c = *++cp; JS7_ISDEC(c); c = *++cp, tmp++)		        num = 10 * num + (uintN)JS7_UNDEC(c);                    /* 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 = --cp; /* skip beyond the '\' */                        goto do_flat;                    }                    /* more than 1 digit, or a number greater than                        the count of parentheses => it's an octal */                    if ((tmp > 1) || (num > state->parenCount)) {		        cp = ocp;		        goto do_octal;	            }	            cp--;	            ren = NewRENode(state, REOP_BACKREF, NULL);	            if (!ren)		        return NULL;	            ren->u.num = num - 1;	/* \1 is numbered 0, etc. */	            /* Avoid common chr- and flags-setting code after switch. */	            ren->flags = RENODE_NONEMPTY;	            goto bump_cp;                }            }            else {                if (c == '0') {            	    ren = NewRENode(state, REOP_FLAT1, NULL);                    c = 0;                }                else {                    num = (uintN)JS7_UNDEC(c);                    for (c = *++cp; JS7_ISDEC(c); c = *++cp)		        num = 10 * num + (uintN)JS7_UNDEC(c);                    cp--;	            ren = NewRENode(state, REOP_BACKREF, NULL);	            if (!ren)		        return NULL;	            ren->u.num = num - 1;	/* \1 is numbered 0, etc. */	            /* Avoid common chr- and flags-setting code after switch. */	            ren->flags = RENODE_NONEMPTY;	            goto bump_cp;                }            }            break;	  case 'x':	    ocp = cp;	    c = *++cp;	    if (JS7_ISHEX(c)) {		num = JS7_UNHEX(c);		c = *++cp;		if (JS7_ISHEX(c)) {		    num <<= 4;		    num += JS7_UNHEX(c);		} else {                    if (state->context->version != JSVERSION_DEFAULT &&			      state->context->version <= JSVERSION_1_4)		        cp--;	/* back up so cp points to last hex char */                    else                        goto nothex; /* ECMA 2 insists on pairs */		}	    } else {nothex:		cp = ocp;	/* \xZZ is xZZ (Perl does \0ZZ!) */		num = 'x';	    }	    ren = NewRENode(state, REOP_FLAT1, NULL);	    c = (jschar)num;	    break;	  case 'c':	    c = *++cp;	    if (!JS7_ISLET(c)) {		cp -= 2;		ocp = cp;		goto do_flat;	    }	    c = JS_TOUPPER(c);	    c = JS_TOCTRL(c);	    ren = NewRENode(state, REOP_FLAT1, NULL);	    break;	  case 'u':	    if (JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) &&		JS7_ISHEX(cp[3]) && JS7_ISHEX(cp[4])) {		c = (((((JS7_UNHEX(cp[1]) << 4) + JS7_UNHEX(cp[2])) << 4)		      + JS7_UNHEX(cp[3])) << 4) + JS7_UNHEX(cp[4]);		cp += 4;		ren = NewRENode(state, REOP_FLAT1, NULL);		break;	    }	    /* Unlike Perl \xZZ, we take \uZZZ to be literal-u then ZZZ. */	    ocp = cp;	    goto do_flat;	  default:	    ocp = cp;	    goto do_flat;	}	/* Common chr- and flags-setting code for escape opcodes. */	if (ren) {	    ren->u.chr = c;	    ren->flags = RENODE_SINGLE | RENODE_NONEMPTY;	}      bump_cp:	/* Skip to next unparsed char. */	cp++;	break;      default:      do_flat:	while ((c = *++cp) && (cp != state->cpend) && !js_strchr(metachars, c))	    ;	len = (uintN)(cp - ocp);	if ((cp != state->cpend) && len > 1 && js_strchr(closurechars, c)) {	    cp--;	    len--;	}	if (len > REOP_FLATLEN_MAX) {	    len = REOP_FLATLEN_MAX;	    cp = ocp + len;	}	ren = NewRENode(state, (len == 1) ? REOP_FLAT1 : REOP_FLAT,			(void *)ocp);	if (!ren)	    return NULL;	ren->flags = RENODE_NONEMPTY;	if (len > 1) {	    ren->u.kid2 = (void *)cp;	} else {	    ren->flags |= RENODE_SINGLE;	    ren->u.chr = *ocp;	}	break;    }    state->cp = cp;    return ren;}