📄 jsregexp.c
字号:
JUMP, <end> ... ENDALT */ state->progLength += 13; } else if (((RENode *) result->kid)->op == REOP_CLASS && ((RENode *) result->kid)->u.ucclass.index < 256 && ((RENode *) result->u.kid2)->op == REOP_FLAT && (state->flags & JSREG_FOLD) == 0) { result->op = REOP_ALTPREREQ2; result->u.altprereq.ch1 = ((RENode *) result->u.kid2)->u.flat.chr; result->u.altprereq.ch2 = ((RENode *) result->kid)->u.ucclass.index; /* ALTPREREQ2, <end>, uch1, uch2, <next>, ..., JUMP, <end> ... ENDALT */ state->progLength += 13; } else if (((RENode *) result->kid)->op == REOP_FLAT && ((RENode *) result->u.kid2)->op == REOP_CLASS && ((RENode *) result->u.kid2)->u.ucclass.index < 256 && (state->flags & JSREG_FOLD) == 0) { result->op = REOP_ALTPREREQ2; result->u.altprereq.ch1 = ((RENode *) result->kid)->u.flat.chr; result->u.altprereq.ch2 = ((RENode *) result->u.kid2)->u.ucclass.index; /* ALTPREREQ2, <end>, uch1, uch2, <next>, ..., JUMP, <end> ... ENDALT */ state->progLength += 13; } else /* ALT, <next>, ..., JUMP, <end> ... ENDALT */ state->progLength += 7; break; case REOP_CONCAT: result = operandStack[operandSP - 2]; while (result->next) result = result->next; result->next = operandStack[operandSP - 1]; break; case REOP_ASSERT: case REOP_ASSERT_NOT: case REOP_LPARENNON: case REOP_LPAREN: /* These should have been processed by a close paren. */ js_ReportCompileErrorNumber(state->context, state->tokenStream, NULL, JSREPORT_ERROR, JSMSG_MISSING_PAREN, opData->errPos); return JS_FALSE; default:; } return JS_TRUE;}/* * Parser forward declarations. */static JSBool ParseTerm(CompilerState *state);static JSBool ParseQuantifier(CompilerState *state);/* * 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. */#define INITIAL_STACK_SIZE 128static JSBoolParseRegExp(CompilerState *state){ uint16 parenIndex; RENode *operand; REOpData *operatorStack; RENode **operandStack; REOp op; intN i; JSBool result = JS_FALSE; intN operatorSP = 0, operatorStackSize = INITIAL_STACK_SIZE; intN operandSP = 0, operandStackSize = INITIAL_STACK_SIZE; /* Watch out for empty regexp */ if (state->cp == state->cpend) { state->result = NewRENode(state, REOP_EMPTY); return (state->result != NULL); } operatorStack = (REOpData *) JS_malloc(state->context, sizeof(REOpData) * operatorStackSize); if (!operatorStack) return JS_FALSE; operandStack = (RENode **) JS_malloc(state->context, sizeof(RENode *) * operandStackSize); if (!operandStack) goto out; while (JS_TRUE) { parenIndex = state->parenCount; if (state->cp == state->cpend) { /* * If we are at the end of the regexp and we're short one or more * operands, the regexp must have the form /x|/ or some such, with * left parentheses making us short more than one operand. */ if (operatorSP >= operandSP) { operand = NewRENode(state, REOP_EMPTY); if (!operand) goto out; goto pushOperand; } } else { switch (*state->cp) { /* balance '(' */ case '(': /* balance ')' */ ++state->cp; if (state->cp + 1 < state->cpend && *state->cp == '?' && (state->cp[1] == '=' || state->cp[1] == '!' || state->cp[1] == ':')) { switch (state->cp[1]) { case '=': op = REOP_ASSERT; /* ASSERT, <next>, ... ASSERTTEST */ state->progLength += 4; break; case '!': op = REOP_ASSERT_NOT; /* ASSERTNOT, <next>, ... ASSERTNOTTEST */ state->progLength += 4; break; default: op = REOP_LPARENNON; break; } state->cp += 2; } else { op = REOP_LPAREN; /* LPAREN, <index>, ... RPAREN, <index> */ state->progLength += 6; state->parenCount++; if (state->parenCount == 65535) { js_ReportCompileErrorNumber(state->context, state->tokenStream, NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_PARENS); goto out; } } goto pushOperator; case ')': /* If there's not a stacked open parenthesis, throw * a syntax error. */ for (i = operatorSP - 1; ; i--) { if (i < 0) { js_ReportCompileErrorNumber(state->context, state->tokenStream, NULL, JSREPORT_ERROR, JSMSG_UNMATCHED_RIGHT_PAREN); goto out; } if (operatorStack[i].op == REOP_ASSERT || operatorStack[i].op == REOP_ASSERT_NOT || operatorStack[i].op == REOP_LPARENNON || operatorStack[i].op == REOP_LPAREN) { break; } } /* fall thru... */ case '|': /* Expected an operand before these, so make an empty one */ operand = NewRENode(state, REOP_EMPTY); if (!operand) goto out; goto pushOperand; default: if (!ParseTerm(state)) goto out; operand = state->result;pushOperand: if (operandSP == operandStackSize) { operandStackSize += operandStackSize; operandStack = (RENode **)JS_realloc(state->context, operandStack, sizeof(RENode *) * operandStackSize); if (!operandStack) goto out; } operandStack[operandSP++] = operand; break; } } /* At the end; process remaining operators */restartOperator: if (state->cp == state->cpend) { while (operatorSP) { --operatorSP; if (!ProcessOp(state, &operatorStack[operatorSP], operandStack, operandSP)) goto out; --operandSP; } JS_ASSERT(operandSP == 1); state->result = operandStack[0]; result = JS_TRUE; goto out; } switch (*state->cp) { case '|': /* Process any stacked 'concat' operators */ ++state->cp; while (operatorSP && operatorStack[operatorSP - 1].op == REOP_CONCAT) { --operatorSP; if (!ProcessOp(state, &operatorStack[operatorSP], operandStack, operandSP)) goto out; --operandSP; } op = REOP_ALT; goto pushOperator; case ')': /* If there's not a stacked open parenthesis,we * accept the close as a flat. */ for (i = operatorSP - 1; ; i--) { if (i < 0) { js_ReportCompileErrorNumber(state->context, state->tokenStream, NULL, JSREPORT_ERROR, JSMSG_UNMATCHED_RIGHT_PAREN); goto out; } if (operatorStack[i].op == REOP_ASSERT || operatorStack[i].op == REOP_ASSERT_NOT || operatorStack[i].op == REOP_LPARENNON || operatorStack[i].op == REOP_LPAREN) { break; } } ++state->cp; /* process everything on the stack until the open */ while (JS_TRUE) { JS_ASSERT(operatorSP); --operatorSP; switch (operatorStack[operatorSP].op) { case REOP_ASSERT: case REOP_ASSERT_NOT: case REOP_LPAREN: operand = NewRENode(state, operatorStack[operatorSP].op); if (!operand) goto out; operand->u.parenIndex = operatorStack[operatorSP].parenIndex; JS_ASSERT(operandSP); operand->kid = operandStack[operandSP - 1]; operandStack[operandSP - 1] = operand; ++state->treeDepth; /* fall thru... */ case REOP_LPARENNON: state->result = operandStack[operandSP - 1]; if (!ParseQuantifier(state)) goto out; operandStack[operandSP - 1] = state->result; goto restartOperator; default: if (!ProcessOp(state, &operatorStack[operatorSP], operandStack, operandSP)) goto out; --operandSP; break; } } break; case '+': case '*': case '?': case '{': js_ReportCompileErrorNumber(state->context, state->tokenStream, NULL, JSREPORT_ERROR, JSMSG_BAD_QUANTIFIER, state->cp); result = JS_FALSE; goto out; default: /* Anything else is the start of the next term */ op = REOP_CONCAT;pushOperator: if (operatorSP == operatorStackSize) { operatorStackSize += operatorStackSize; operatorStack = (REOpData *)JS_realloc(state->context, operatorStack, sizeof(REOpData) * operatorStackSize); if (!operatorStack) goto out; } operatorStack[operatorSP].op = op; operatorStack[operatorSP].errPos = state->cp; operatorStack[operatorSP++].parenIndex = parenIndex; break; } }out: if (operatorStack) JS_free(state->context, operatorStack); if (operandStack) JS_free(state->context, operandStack); return result;}/* * Hack two bits in CompilerState.flags, for use within FindParenCount to flag * its being on the stack, and to propagate errors to its callers. */#define JSREG_FIND_PAREN_COUNT 0x8000#define JSREG_FIND_PAREN_ERROR 0x4000/* * Magic return value from FindParenCount and GetDecimalValue, to indicate * overflow beyond GetDecimalValue's max parameter, or a computed maximum if * its findMax parameter is non-null. */#define OVERFLOW_VALUE ((uintN)-1)static uintNFindParenCount(CompilerState *state){ CompilerState temp; int i; if (state->flags & JSREG_FIND_PAREN_COUNT) return OVERFLOW_VALUE; /* * Copy state into temp, flag it so we never report an invalid backref, * and reset its members to parse the entire regexp. This is obviously * suboptimal, but GetDecimalValue calls us only if a backref appears to * refer to a forward parenthetical, which is rare. */ temp = *state; temp.flags |= JSREG_FIND_PAREN_COUNT; temp.cp = temp.cpbegin; temp.parenCount = 0; temp.classCount = 0; temp.progLength = 0; temp.treeDepth = 0; for (i = 0; i < CLASS_CACHE_SIZE; i++) temp.classCache[i].start = NULL; if (!ParseRegExp(&temp)) { state->flags |= JSREG_FIND_PAREN_ERROR; return OVERFLOW_VALUE; } return temp.parenCount;}/* * Extract and return a decimal value at state->cp. The initial character c * has already been read. Return OVERFLOW_VALUE if the result exceeds max. * Callers who pass a non-null findMax should test JSREG_FIND_PAREN_ERROR in * state->flags to discover whether an error occurred under findMax. */static uintNGetDecimalValue(jschar c, uintN max, uintN (*findMax)(CompilerState *state), CompilerState *state){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -