JS_ReportError(state->context, "unterminated quantifier %s",
JS_ReportErrorNumber(state->context, NULL, JSMSG_UNTERM_QUANTIFIER,
static REParser ParseQuantAtom;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 == '|') {	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 == '|');    }    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 ((c = *cp) != 0 && c != '|' && 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;    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}). */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:    switch (*cp) {      case '{':	c = *++cp;	if (!JS7_ISDEC(c)) {	    JS_ReportError(state->context, "invalid quantifier %s", 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_ReportError(state->context, "overlarge minimum %s",			       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_ReportError(state->context, "overlarge maximum %s",				       up);			return NULL;		    }		}		if (max == 0)		    goto zero_quant;		if (min > max) {		    JS_ReportError(state->context,				   "maximum %s less than minimum", up);		    return NULL;		}	    } else {		/* 0 means no upper bound. */		max = 0;	    }	} else {	    /* Exactly n times. */	    if (min == 0) {      zero_quant:		JS_ReportError(state->context, "zero quantifier %s", state->cp);		return NULL;	    }	    max = min;	}	if (*cp != '}') {	    JS_ReportError(state->context, "unterminated quantifier %s",			   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 loop;      case '*':	if (!(ren->flags & RENODE_NONEMPTY)) {	    JS_ReportError(state->context,			   "regular expression before * could be empty");	    return NULL;	}	cp++;	ren = NewRENode(state, REOP_STAR, ren);	goto loop;      case '+':	if (!(ren->flags & RENODE_NONEMPTY)) {	    JS_ReportError(state->context,			   "regular expression before + could be empty");	    return NULL;	}	cp++;	ren2 = NewRENode(state, REOP_PLUS, ren);	if (!ren2)	    return NULL;	if (ren->flags & RENODE_NONEMPTY)	    ren2->flags |= RENODE_NONEMPTY;	ren = ren2;	goto loop;      case '?':	cp++;	ren = NewRENode(state, REOP_OPT, ren);	goto loop;    }    state->cp = cp;    return ren;}