📄 ejsparser.c
字号:
/* * Parse a function declaration */static int parseFunction(Ejs *ep, int state, int flags){ EjsInput *endScript, *bodyScript; EjsProperty *pp; EjsVar *func, *funcProp, *currentObj, *vp, *baseClass; char *procName; int varFlags, len, tid, bodyFlags, innerState; mprAssert(ep); func = 0; varFlags = 0; /* * method <name>(arg, arg, arg) { body }; * method name(arg, arg, arg) { body }; */ tid = ejsLexGetToken(ep, state); if (tid == EJS_TOK_GET) { varFlags |= EJS_GET_ACCESSOR; tid = ejsLexGetToken(ep, state); } else if (tid == EJS_TOK_SET) { varFlags |= EJS_SET_ACCESSOR; tid = ejsLexGetToken(ep, state); } if (tid == EJS_TOK_ID) { if (varFlags & EJS_SET_ACCESSOR) { if (mprAllocStrcat(MPR_LOC_ARGS(ep), &procName, EJS_MAX_ID + 5, 0, "-set-", ep->token, 0) < 0) { ejsError(ep, EJS_SYNTAX_ERROR, "Name %s is too long", ep->token); return EJS_STATE_ERR; } } else { procName = mprStrdup(ep, ep->token); } tid = ejsLexGetToken(ep, state); } else { procName = 0; } if (tid != EJS_TOK_LPAREN) { mprFree(procName); ejsSyntaxError(ep, 0); return EJS_STATE_ERR; } /* * Hand craft the method value structure. */ if (flags & EJS_FLAGS_EXE) { func = ejsCreateMethodVar(ep, 0, 0, 0); if (func == 0) { mprFree(procName); ejsMemoryError(ep); return EJS_STATE_ERR; } func->flags = varFlags; } tid = ejsLexGetToken(ep, state); while (tid == EJS_TOK_ID) { if (flags & EJS_FLAGS_EXE) { mprAddItem(func->method.args, mprStrdup(func->method.args, ep->token)); } tid = ejsLexGetToken(ep, state); if (tid == EJS_TOK_RPAREN || tid != EJS_TOK_COMMA) { break; } tid = ejsLexGetToken(ep, state); } if (tid != EJS_TOK_RPAREN) { mprFree(procName); ejsFreeVar(ep, func); ejsSyntaxError(ep, 0); return EJS_STATE_ERR; } /* Allow new lines before opening brace */ do { tid = ejsLexGetToken(ep, state); } while (tid == EJS_TOK_NEWLINE); if (tid != EJS_TOK_LBRACE) { mprFree(procName); ejsFreeVar(ep, func); ejsSyntaxError(ep, 0); return EJS_STATE_ERR; } /* * Register the method name early to allow for recursive * method calls (see note in ECMA standard, page 71) */ funcProp = 0; if (flags & EJS_FLAGS_EXE && procName) { currentObj = pickSpace(ep, 0, procName, flags | EJS_FLAGS_LOCAL); pp = ejsSetProperty(ep, currentObj, procName, func); if (pp == 0) { ejsFreeVar(ep, func); ejsMemoryError(ep); return EJS_STATE_ERR; } funcProp = ejsGetVarPtr(pp); } bodyScript = getInputStruct(ep); /* * Parse the method body. Turn execute off. */ bodyFlags = flags & ~EJS_FLAGS_EXE; ejsLexSaveInputState(ep, bodyScript); do { innerState = ejsParse(ep, EJS_STATE_STMT, bodyFlags); } while (innerState == EJS_STATE_STMT_DONE); tid = ejsLexGetToken(ep, state); if (innerState != EJS_STATE_STMT_BLOCK_DONE || tid != EJS_TOK_RBRACE) { mprFree(procName); ejsFreeVar(ep, func); ejsLexFreeInputState(ep, bodyScript); if (innerState != EJS_STATE_ERR) { ejsSyntaxError(ep, 0); } freeInputStruct(ep, bodyScript); return EJS_STATE_ERR; } if (flags & EJS_FLAGS_EXE) { endScript = getInputStruct(ep); ejsLexSaveInputState(ep, endScript); /* * Save the method body between the starting and ending parse * positions. Overwrite the trailing '}' with a null. */ len = endScript->scriptServp - bodyScript->scriptServp; func->method.body = mprAlloc(ep, len + 1); memcpy(func->method.body, bodyScript->scriptServp, len); if (len <= 0) { func->method.body[0] = '\0'; } else { func->method.body[len - 1] = '\0'; } ejsLexFreeInputState(ep, bodyScript); ejsLexFreeInputState(ep, endScript); freeInputStruct(ep, endScript); /* * If we are in an assignment, don't register the method name, rather * return the method structure in the parser result. */ if (procName) { currentObj = pickSpace(ep, 0, procName, flags | EJS_FLAGS_LOCAL); pp = ejsSetProperty(ep, currentObj, procName, func); if (pp == 0) { ejsFreeVar(ep, func); ejsMemoryError(ep); return EJS_STATE_ERR; } if (currentObj->objectState->className && strcmp(currentObj->objectState->className, procName) == 0) { baseClass = currentObj->objectState->baseClass; if (baseClass) { if (strstr(func->method.body, "super(") != 0) { funcProp->callsSuper = 1; /* * Define super() to point to the baseClass constructor */ vp = ejsGetPropertyAsVar(ep, baseClass, baseClass->objectState->className); if (vp) { mprAssert(vp); if (ejsSetProperty(ep, currentObj, "super", vp) == 0) { ejsFreeVar(ep, func); ejsMemoryError(ep); return EJS_STATE_ERR; } } } } } } /* * Always return the function. Try for all stmts to be expressions. */ /* MOB - rc */ ejsWriteVar(ep, ep->result, func, EJS_SHALLOW_COPY); } freeInputStruct(ep, bodyScript); mprFree(procName); ejsFreeVar(ep, func); return state;}/******************************************************************************//* * Local vars */typedef struct ParseMethod { EjsProc proc, *saveProc; EjsVar *saveObj, *newObj; int saveObjPerm, rc;} ParseMethod;/* * Parse a method name and invoke the method. See parseFunction for * function declarations. */static int parseMethod(Ejs *ep, int state, int flags, char *id){ ParseMethod *sp; if ((sp = pushFrame(ep, sizeof(ParseMethod))) == 0) { return EJS_STATE_ERR; } /* * Must save any current ep->proc value for the current stack frame * to allow for recursive method calls. */ sp->saveProc = (ep->proc) ? ep->proc: 0; memset(&sp->proc, 0, sizeof(EjsProc)); sp->proc.procName = mprStrdup(ep, id); sp->proc.fn = &ep->currentProperty->var; sp->proc.args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS); ep->proc = &sp->proc;#if BLD_DEBUG if (strcmp(sp->proc.procName, "printv") == 0) { flags |= EJS_FLAGS_TRACE_ARGS; }#endif if (flags & EJS_FLAGS_EXE) { ejsClearVar(ep, ep->result); } if (! (flags & EJS_FLAGS_NO_ARGS)) { sp->saveObj = ep->currentObj; sp->saveObjPerm = ejsMakeObjPermanent(sp->saveObj, 1); sp->rc = ejsParse(ep, EJS_STATE_ARG_LIST, flags); ejsMakeObjPermanent(sp->saveObj, sp->saveObjPerm); if (sp->rc < 0) { goto err; } ep->currentObj = sp->saveObj; }#if BLD_DEBUG flags &= ~EJS_FLAGS_TRACE_ARGS;#endif /* * Evaluate the method if required */ if (flags & EJS_FLAGS_EXE) { if (flags & EJS_FLAGS_NEW) { sp->newObj = ejsCreateObjUsingArgv(ep, ep->currentObj, sp->proc.procName, sp->proc.args); if (sp->newObj == 0) { state = EJS_STATE_ERR; } else { mprAssert(! ejsObjIsCollectable(sp->newObj)); /* * Return the newly created object as the result of the * command. NOTE: newObj may not be an object! */ /* MOB - rc */ ejsWriteVar(ep, ep->result, sp->newObj, EJS_SHALLOW_COPY); if (ejsVarIsObject(sp->newObj)) { ejsMakeObjLive(sp->newObj, 1); mprAssert(ejsObjIsCollectable(sp->newObj)); mprAssert(ejsBlockInUse(sp->newObj)); } ejsFreeVar(ep, sp->newObj); } } else { if (evalMethod(ep, ep->currentObj, &sp->proc, flags) < 0) { /* Methods must call ejsError to set exceptions */ state = EJS_STATE_ERR; } } } if (! (flags & EJS_FLAGS_NO_ARGS)) { if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { if (state != EJS_STATE_ERR) { ejsSyntaxError(ep, 0); } state = EJS_STATE_ERR; } }done: freeProc(ep, &sp->proc); ep->proc = sp->saveProc; popFrame(ep, sizeof(ParseMethod)); return state;err: state = EJS_STATE_ERR; goto done;}/******************************************************************************//* * Parse an identifier. This is a segment of a fully qualified variable. * May come here for an initial identifier or for property names * after a "." or "[...]". */static int parseId(Ejs *ep, int state, int flags, char **id, int *done){ EjsVar *null; int tid; mprFree(*id); *id = mprStrdup(ep, ep->token); if (ep->currentObj == 0) { /* First identifier segement */ ep->currentObj = pickSpace(ep, state, *id, flags); } tid = ejsLexGetToken(ep, state); if (tid == EJS_TOK_ASSIGNMENT) { flags |= EJS_FLAGS_LHS; } /* * Find the referenced variable and store it in currentProperty. */ if (flags & EJS_FLAGS_EXE) { ep->currentProperty = searchSpacesForProperty(ep, state, ep->currentObj, *id, flags); /* * Handle properties that have been deleted inside an enumeration */ if (ep->currentProperty && ep->currentProperty->delayedDelete) { ep->currentProperty = 0; } if (ep->currentProperty && ejsVarIsMethod(&ep->currentProperty->var) && tid != EJS_TOK_LPAREN) { if (ep->currentProperty->var.flags & EJS_GET_ACCESSOR) { ejsLexPutbackToken(ep, tid, ep->token); state = parseMethod(ep, state, flags | EJS_FLAGS_NO_ARGS, *id); if (ep->flags & EJS_FLAGS_EXIT) { state = EJS_STATE_RET; } if (state >= 0) { ejsSetVarName(ep, ep->result, ep->currentProperty->name); } return state; } } /* * OPT. We should not have to do this always */ updateResult(ep, state, flags, ejsGetVarPtr(ep->currentProperty)); } flags &= ~EJS_FLAGS_LHS; if (tid == EJS_TOK_LPAREN) { if (ep->currentProperty == 0 && (flags & EJS_FLAGS_EXE)) { ejsError(ep, EJS_REFERENCE_ERROR, "Method name not defined \"%s\"", *id); return EJS_STATE_ERR; } ejsLexPutbackToken(ep, EJS_TOK_METHOD_NAME, ep->token); return state; } if (tid == EJS_TOK_PERIOD || tid == EJS_TOK_LBRACKET || tid == EJS_TOK_ASSIGNMENT || tid == EJS_TOK_INC_DEC) { ejsLexPutbackToken(ep, tid, ep->token); return state; } if (flags & EJS_FLAGS_CLASS_DEC) { if (tid == EJS_TOK_LBRACE || tid == EJS_TOK_EXTENDS) { ejsLexPutbackToken(ep, tid, ep->token); return state; } } if (flags & EJS_FLAGS_DELETE) { if (tid == EJS_TOK_RBRACE) { ejsLexPutbackToken(ep, tid, ep->token); } } /* * Only come here for variable access and declarations. * Assignment handled elsewhere. */ if (flags & EJS_FLAGS_EXE) { if (state == EJS_STATE_DEC) { /* * Declare a variable. Standard allows: var x ; var x ; */#if DISABLED if (ep->currentProperty != 0) { ejsError(ep, EJS_REFERENCE_ERROR, "Variable already defined \"%s\"", *id); return EJS_STATE_ERR; }#endif /* * Create or overwrite if it already exists * Set newly declared variables to the null value. */ null = ejsCreateNullVar(ep); ep->currentProperty = ejsSetPropertyAndFree(ep, ep->currentObj, *id, null); ejsClearVar(ep, ep->result); } else if (flags & EJS_FLAGS_FORIN) { /* * This allows "for (x" when x has not yet been defined */ if (ep->currentProperty == 0) { /* MOB -- return code */ ep->currentProperty = ejsCreateProperty(ep, ep->currentObj, *id); } } else if (ep->currentProperty == 0) { if (ep->currentObj && ((ep->currentObj == ep->global || (ep->currentObj == ep->local)))) { /* * Test against currentObj and not currentObj->objectState * as we must allow "i = global.x" and not allow * "i = x" where x does not exist. */ ejsError(ep, EJS_REFERENCE_ERROR, "Undefined variable \"%s\"", *id); return EJS_STATE_ERR; } if (flags & EJS_FLAGS_DELETE) { ejsError(ep, EJS_REFERENCE_ERROR, "Undefined variable \"%s\"", *id); return EJS_STATE_ERR; } } } ejsLexPutbackToken(ep, tid, ep->token); if (tid == EJS_TOK_RBRACKET || tid == EJS_TOK_COMMA || tid == EJS_TOK_IN) { *done = 1; } return state;}/******************************************************************************//* * Local vars */typedef struct ParseIf { int ifResult, thenFlags, elseFlags, tid, rs;} ParseIf;/* * Parse an "if" statement */static int parseIf(Ejs *ep, int state, int flags, int *done){ ParseIf *sp; if ((sp = pushFrame(ep, sizeof(ParseIf))) == 0) { return EJS_STATE_ERR; } if (state != EJS_STATE_STMT) { goto err; } if (ejsLexGetToken(ep, state) != EJS_TOK_LPAREN) { goto err; } /* * Evaluate the entire condition list "(condition)" */ if (ejsParse(ep, EJS_STATE_COND, flags) < 0) { goto err; } if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { goto err; } /* * This is the "then" case. We need to always parse both cases and * execute only the relevant case. */ sp->ifResult = ejsVarToBoolean(ep->result); if (sp->ifResult) { sp->thenFlags = flags; sp->elseFlags = flags & ~EJS_FLAGS_EXE; } else { sp->thenFlags = flags & ~EJS_FLAGS_EXE; sp->elseFlags = flags; } /* * Process the "then" case. */ if ((sp->rs = ejsParse(ep, EJS_STATE_STMT, sp->thenFlags)) < 0) { if (! ep->gotException) { state = sp->rs; goto done; } } /* * Check to see if there is an "else" case */ removeNewlines(ep, state); sp->tid = ejsLexGetToken(ep, state); if (sp->tid != EJS_TOK_ELSE) { ejsLexPutbackToken(ep, sp->tid, ep->token); *done = 1; if (ep->gotException) { goto err; } goto done; } /* * Process the "else" case. */ state = ejsParse(ep, EJS_STATE_STMT, sp
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -