diff options
Diffstat (limited to 'source4/lib/appweb/ejs/ejsParser.c')
-rw-r--r-- | source4/lib/appweb/ejs/ejsParser.c | 2436 |
1 files changed, 0 insertions, 2436 deletions
diff --git a/source4/lib/appweb/ejs/ejsParser.c b/source4/lib/appweb/ejs/ejsParser.c deleted file mode 100644 index da922a5728..0000000000 --- a/source4/lib/appweb/ejs/ejsParser.c +++ /dev/null @@ -1,2436 +0,0 @@ -/* - * @file ejsParser.c - * @brief EJS Parser and Execution - */ -/********************************* Copyright **********************************/ -/* - * @copy default.g - * - * Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved. - * Portions Copyright (c) GoAhead Software, 1995-2000. All Rights Reserved. - * - * This software is distributed under commercial and open source licenses. - * You may use the GPL open source license described below or you may acquire - * a commercial license from Mbedthis Software. You agree to be fully bound - * by the terms of either license. Consult the LICENSE.TXT distributed with - * this software for full details. - * - * This software is open source; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. See the GNU General Public License for more - * details at: http://www.mbedthis.com/downloads/gplLicense.html - * - * This program is distributed WITHOUT ANY WARRANTY; without even the - * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - * This GPL license does NOT permit incorporating this software into - * proprietary programs. If you are unable to comply with the GPL, you must - * acquire a commercial license to use this software. Commercial licenses - * for this software and support services are available from Mbedthis - * Software at http://www.mbedthis.com - * - * @end - */ - -/********************************** Includes **********************************/ - -#include "ejsInternal.h" - -#if BLD_FEATURE_EJS - -/****************************** Forward Declarations **************************/ - -static void appendValue(MprVar *v1, MprVar *v2); -static int evalCond(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs); -static int evalExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs); -#if BLD_FEATURE_FLOATING_POINT -static int evalFloatExpr(Ejs *ep, double l, int rel, double r); -#endif -static int evalBoolExpr(Ejs *ep, bool l, int rel, bool r); -static int evalPtrExpr(Ejs *ep, void *l, int rel, void *r); -static int evalNumericExpr(Ejs *ep, MprNum l, int rel, MprNum r); -static int evalStringExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs); -static int evalFunction(Ejs *ep, MprVar *obj, int flags); -static void freeProc(EjsProc *proc); -static int parseArgs(Ejs *ep, int state, int flags); -static int parseAssignment(Ejs *ep, int state, int flags, char *id, - char *fullName); -static int parseCond(Ejs *ep, int state, int flags); -static int parseDeclaration(Ejs *ep, int state, int flags); -static int parseExpr(Ejs *ep, int state, int flags); -static int parseFor(Ejs *ep, int state, int flags); -static int parseForIn(Ejs *ep, int state, int flags); -static int parseFunctionDec(Ejs *ep, int state, int flags); -static int parseFunction(Ejs *ep, int state, int flags, char *id); -static int parseId(Ejs *ep, int state, int flags, char **id, - char **fullName, int *fullNameLen, int *done); -static int parseInc(Ejs *ep, int state, int flags); -static int parseIf(Ejs *ep, int state, int flags, int *done); -static int parseStmt(Ejs *ep, int state, int flags); -static void removeNewlines(Ejs *ep, int state); -static void updateResult(Ejs *ep, int state, int flags, MprVar *vp); - -/************************************* Code ***********************************/ -/* - * Recursive descent parser for EJS - */ - -int ejsParse(Ejs *ep, int state, int flags) -{ - mprAssert(ep); - - switch (state) { - /* - * Any statement, function arguments or conditional expressions - */ - case EJS_STATE_STMT: - if ((state = parseStmt(ep, state, flags)) != EJS_STATE_STMT_DONE && - state != EJS_STATE_EOF && state != EJS_STATE_STMT_BLOCK_DONE && - state != EJS_STATE_RET) { - state = EJS_STATE_ERR; - } - break; - - case EJS_STATE_DEC: - if ((state = parseStmt(ep, state, flags)) != EJS_STATE_DEC_DONE && - state != EJS_STATE_EOF) { - state = EJS_STATE_ERR; - } - break; - - case EJS_STATE_EXPR: - if ((state = parseStmt(ep, state, flags)) != EJS_STATE_EXPR_DONE && - state != EJS_STATE_EOF) { - state = EJS_STATE_ERR; - } - break; - - /* - * Variable declaration list - */ - case EJS_STATE_DEC_LIST: - state = parseDeclaration(ep, state, flags); - break; - - /* - * Function argument string - */ - case EJS_STATE_ARG_LIST: - state = parseArgs(ep, state, flags); - break; - - /* - * Logical condition list (relational operations separated by &&, ||) - */ - case EJS_STATE_COND: - state = parseCond(ep, state, flags); - break; - - /* - * Expression list - */ - case EJS_STATE_RELEXP: - state = parseExpr(ep, state, flags); - break; - } - - if (state == EJS_STATE_ERR && ep->error == NULL) { - ejsError(ep, "Syntax error"); - } - return state; -} - -/******************************************************************************/ -/* - * Parse any statement including functions and simple relational operations - */ - -static int parseStmt(Ejs *ep, int state, int flags) -{ - EjsProc *saveProc; - MprVar *vp, *saveObj; - char *id, *fullName, *initToken; - int done, expectSemi, tid, fullNameLen, rel; - int initId; - - mprAssert(ep); - - expectSemi = 0; - saveProc = NULL; - id = 0; - fullName = 0; - fullNameLen = 0; - - ep->currentObj = 0; - ep->currentProperty = 0; - - for (done = 0; !done && state != EJS_STATE_ERR; ) { - tid = ejsLexGetToken(ep, state); - - switch (tid) { - default: - ejsLexPutbackToken(ep, EJS_TOK_EXPR, ep->token); - done++; - break; - - case EJS_TOK_EXPR: - rel = (int) *ep->token; - if (state == EJS_STATE_EXPR) { - ejsLexPutbackToken(ep, EJS_TOK_EXPR, ep->token); - } - done++; - break; - - case EJS_TOK_LOGICAL: - ejsLexPutbackToken(ep, tid, ep->token); - done++; - break; - - case EJS_TOK_ERR: - state = EJS_STATE_ERR; - done++; - break; - - case EJS_TOK_EOF: - state = EJS_STATE_EOF; - done++; - break; - - case EJS_TOK_NEWLINE: - break; - - case EJS_TOK_SEMI: - /* - * This case is when we discover no statement and just a lone ';' - */ - if (state != EJS_STATE_STMT) { - ejsLexPutbackToken(ep, tid, ep->token); - } - done++; - break; - - case EJS_TOK_PERIOD: - if (flags & EJS_FLAGS_EXE) { - if (ep->currentProperty == 0) { - ejsError(ep, "Undefined object \"%s\"\n", id); - goto error; - } - } - ep->currentObj = ep->currentProperty; - - if ((tid = ejsLexGetToken(ep, state)) != EJS_TOK_ID) { - ejsError(ep, "Bad property after '.': %s\n", ep->token); - goto error; - } - mprFree(id); - id = mprStrdup(ep->token); - - vp = ejsFindProperty(ep, state, ep->currentObj, id, flags); - updateResult(ep, state, flags, vp); - -#if BLD_DEBUG - fullNameLen = mprReallocStrcat(&fullName, MPR_MAX_VAR, fullNameLen, - 0, ".", NULL); -#endif - - ep->currentProperty = vp; - ejsLexPutbackToken(ep, tid, ep->token); - break; - - case EJS_TOK_LBRACKET: - ep->currentObj = ep->currentProperty; - saveObj = ep->currentObj; - if (ejsParse(ep, EJS_STATE_RELEXP, flags) != EJS_STATE_RELEXP_DONE){ - goto error; - } - ep->currentObj = saveObj; - - mprFree(id); - mprVarToString(&id, MPR_MAX_STRING, 0, &ep->result); - - if (id[0] == '\0') { - if (flags & EJS_FLAGS_EXE) { - ejsError(ep, - "[] expression evaluates to the empty string\n"); - goto error; - } - } else { - vp = ejsFindProperty(ep, state, ep->currentObj, id, flags); - ep->currentProperty = vp; - updateResult(ep, state, flags, vp); - } - -#if BLD_DEBUG - if (id[0] && strlen(id) < (MPR_MAX_VAR / 2)) { - /* - * If not executing yet, id may not be known - */ - fullNameLen = mprReallocStrcat(&fullName, MPR_MAX_VAR, - fullNameLen, 0, "[", id, "]", NULL); - } -#endif - - if ((tid = ejsLexGetToken(ep, state)) != EJS_TOK_RBRACKET) { - ejsError(ep, "Missing ']'\n"); - goto error; - } - break; - - case EJS_TOK_ID: - state = parseId(ep, state, flags, &id, &fullName, &fullNameLen, - &done); - if (done && state == EJS_STATE_STMT) { - expectSemi++; - } - break; - - case EJS_TOK_ASSIGNMENT: - state = parseAssignment(ep, state, flags, id, fullName); - if (state == EJS_STATE_STMT) { - expectSemi++; - done++; - } - break; - - case EJS_TOK_INC_DEC: - state = parseInc(ep, state, flags); - if (state == EJS_STATE_STMT) { - expectSemi++; - } - break; - - case EJS_TOK_NEW: - if (ejsParse(ep, EJS_STATE_EXPR, flags | EJS_FLAGS_NEW) - != EJS_STATE_EXPR_DONE) { - goto error; - } - break; - - case EJS_TOK_DELETE: - if (ejsParse(ep, EJS_STATE_EXPR, - flags | EJS_FLAGS_DELETE) != EJS_STATE_EXPR_DONE) { - goto error; - } - if (flags & EJS_FLAGS_EXE) { - mprDeleteProperty(ep->currentObj, ep->currentProperty->name); - } - done++; - break; - - case EJS_TOK_FUNCTION: - state = parseFunctionDec(ep, state, flags); - done++; - break; - - case EJS_TOK_LITERAL: - /* - * Set the result to the string literal - */ - mprCopyVarValue(&ep->result, mprCreateStringVar(ep->token, 0), - MPR_SHALLOW_COPY); - if (state == EJS_STATE_STMT) { - expectSemi++; - } - done++; - break; - - case EJS_TOK_NUMBER: - /* - * Set the result to the parsed number - */ - mprCopyVar(&ep->result, &ep->tokenNumber, 0); - if (state == EJS_STATE_STMT) { - expectSemi++; - } - done++; - break; - - case EJS_TOK_FUNCTION_NAME: - state = parseFunction(ep, state, flags, id); - if (state == EJS_STATE_STMT) { - expectSemi++; - } - if (ep->flags & EJS_FLAGS_EXIT) { - state = EJS_STATE_RET; - } - done++; - break; - - case EJS_TOK_IF: - state = parseIf(ep, state, flags, &done); - if (state == EJS_STATE_RET) { - goto doneParse; - } - break; - - case EJS_TOK_FOR: - if (state != EJS_STATE_STMT) { - goto error; - } - if (ejsLexGetToken(ep, state) != EJS_TOK_LPAREN) { - goto error; - } - /* - * Need to peek 2-3 tokens ahead and see if this is a - * for ([var] x in set) - * or - * for (init ; whileCond; incr) - */ - initId = ejsLexGetToken(ep, EJS_STATE_EXPR); - if (initId == EJS_TOK_ID && strcmp(ep->token, "var") == 0) { - /* Simply eat var tokens */ - initId = ejsLexGetToken(ep, EJS_STATE_EXPR); - } - initToken = mprStrdup(ep->token); - - tid = ejsLexGetToken(ep, EJS_STATE_EXPR); - - ejsLexPutbackToken(ep, tid, ep->token); - ejsLexPutbackToken(ep, initId, initToken); - mprFree(initToken); - - if (tid == EJS_TOK_IN) { - if ((state = parseForIn(ep, state, flags)) < 0) { - goto error; - } - } else { - if ((state = parseFor(ep, state, flags)) < 0) { - goto error; - } - } - done++; - break; - - case EJS_TOK_VAR: - if (ejsParse(ep, EJS_STATE_DEC_LIST, flags) - != EJS_STATE_DEC_LIST_DONE) { - goto error; - } - done++; - break; - - case EJS_TOK_COMMA: - ejsLexPutbackToken(ep, tid, ep->token); - done++; - break; - - case EJS_TOK_LPAREN: - if (state == EJS_STATE_EXPR) { - if (ejsParse(ep, EJS_STATE_RELEXP, flags) - != EJS_STATE_RELEXP_DONE) { - goto error; - } - if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { - goto error; - } - } - done++; - break; - - case EJS_TOK_RPAREN: - ejsLexPutbackToken(ep, tid, ep->token); - done++; - break; - - case EJS_TOK_LBRACE: - /* - * This handles any code in braces except "if () {} else {}" - */ - if (state != EJS_STATE_STMT) { - goto error; - } - - /* - * Parse will return EJS_STATE_STMT_BLOCK_DONE when the RBRACE - * is seen. - */ - do { - state = ejsParse(ep, EJS_STATE_STMT, flags); - } while (state == EJS_STATE_STMT_DONE); - - if (state != EJS_STATE_RET) { - if (ejsLexGetToken(ep, state) != EJS_TOK_RBRACE) { - goto error; - } - state = EJS_STATE_STMT_DONE; - } - done++; - break; - - case EJS_TOK_RBRACE: - if (state == EJS_STATE_STMT) { - ejsLexPutbackToken(ep, tid, ep->token); - state = EJS_STATE_STMT_BLOCK_DONE; - done++; - break; - } - goto error; - - case EJS_TOK_RETURN: - if (ejsParse(ep, EJS_STATE_RELEXP, flags) - != EJS_STATE_RELEXP_DONE) { - goto error; - } - if (flags & EJS_FLAGS_EXE) { - while (ejsLexGetToken(ep, state) != EJS_TOK_EOF) { - ; - } - state = EJS_STATE_RET; - done++; - } - break; - } - } - - if (expectSemi) { - tid = ejsLexGetToken(ep, state); - if (tid != EJS_TOK_SEMI && tid != EJS_TOK_NEWLINE && - tid != EJS_TOK_EOF) { - goto error; - } - - /* - * Skip newline after semi-colon - */ - removeNewlines(ep, state); - } - -/* - * Free resources and return the correct status - */ -doneParse: - mprFree(id); - mprFree(fullName); - - /* - * Advance the state - */ - switch (state) { - case EJS_STATE_STMT: - return EJS_STATE_STMT_DONE; - - case EJS_STATE_DEC: - return EJS_STATE_DEC_DONE; - - case EJS_STATE_EXPR: - return EJS_STATE_EXPR_DONE; - - case EJS_STATE_STMT_DONE: - case EJS_STATE_STMT_BLOCK_DONE: - case EJS_STATE_EOF: - case EJS_STATE_RET: - return state; - - default: - return EJS_STATE_ERR; - } - -/* - * Common error exit - */ -error: - state = EJS_STATE_ERR; - goto doneParse; -} - -/******************************************************************************/ -/* - * Parse function arguments - */ - -static int parseArgs(Ejs *ep, int state, int flags) -{ - int tid; - - mprAssert(ep); - - do { - /* - * Peek and see if there are no args - */ - tid = ejsLexGetToken(ep, state); - ejsLexPutbackToken(ep, tid, ep->token); - if (tid == EJS_TOK_RPAREN) { - break; - } - - state = ejsParse(ep, EJS_STATE_RELEXP, flags); - if (state == EJS_STATE_EOF || state == EJS_STATE_ERR) { - return state; - } - if (state == EJS_STATE_RELEXP_DONE) { - if (flags & EJS_FLAGS_EXE) { - mprAssert(ep->proc->args); - mprAddToArray(ep->proc->args, - mprDupVar(&ep->result, MPR_SHALLOW_COPY)); - } - } - /* - * Peek at the next token, continue if more args (ie. comma seen) - */ - tid = ejsLexGetToken(ep, state); - if (tid != EJS_TOK_COMMA) { - ejsLexPutbackToken(ep, tid, ep->token); - } - } while (tid == EJS_TOK_COMMA); - - if (tid != EJS_TOK_RPAREN && state != EJS_STATE_RELEXP_DONE) { - return EJS_STATE_ERR; - } - return EJS_STATE_ARG_LIST_DONE; -} - -/******************************************************************************/ -/* - * Parse an assignment statement - */ - -static int parseAssignment(Ejs *ep, int state, int flags, char *id, - char *fullName) -{ - MprVar *vp, *saveProperty, *saveObj; - - if (id == 0) { - return -1; - } - - saveObj = ep->currentObj; - saveProperty = ep->currentProperty; - if (ejsParse(ep, EJS_STATE_RELEXP, flags | EJS_FLAGS_ASSIGNMENT) - != EJS_STATE_RELEXP_DONE) { - return -1; - } - ep->currentObj = saveObj; - ep->currentProperty = saveProperty; - - if (! (flags & EJS_FLAGS_EXE)) { - return state; - } - - if (ep->currentProperty) { - /* - * Update the variable. Update the property name if not - * yet defined. - */ - if (ep->currentProperty->name == 0 || - ep->currentProperty->name[0] == '\0') { - mprSetVarName(ep->currentProperty, id); - } - if (mprWriteProperty(ep->currentProperty, &ep->result) < 0){ - ejsError(ep, "Can't write to variable\n"); - return -1; - } - - } else { - /* - * Create the variable - */ - if (ep->currentObj) { - if (ep->currentObj->type != MPR_TYPE_OBJECT) { - if (strcmp(ep->currentObj->name, "session") == 0) { - ejsError(ep, "Variable \"%s\" is not an array or object." - "If using ESP, you need useSession(); in your page.", - ep->currentObj->name); - } else { - ejsError(ep, "Variable \"%s\" is not an array or object", - ep->currentObj->name); - } - return -1; - } - vp = mprCreateProperty(ep->currentObj, id, &ep->result); - - } else { - /* - * Standard says: "var x" means declare locally. - * "x = 2" means declare globally if x is undefined. - */ - if (state == EJS_STATE_DEC) { - vp = mprCreateProperty(ep->local, id, &ep->result); - } else { - vp = mprCreateProperty(ep->global, id, &ep->result); - } - } -#if BLD_DEBUG - mprSetVarFullName(vp, fullName); -#endif - } - return state; -} - -/******************************************************************************/ -/* - * Parse conditional expression (relational ops separated by ||, &&) - */ - -static int parseCond(Ejs *ep, int state, int flags) -{ - MprVar lhs, rhs; - int tid, operator; - - mprAssert(ep); - - mprDestroyVar(&ep->result); - rhs = lhs = mprCreateUndefinedVar(); - operator = 0; - - do { - /* - * Recurse to handle one side of a conditional. Accumulate the - * left hand side and the final result in ep->result. - */ - state = ejsParse(ep, EJS_STATE_RELEXP, flags); - if (state != EJS_STATE_RELEXP_DONE) { - state = EJS_STATE_ERR; - break; - } - - if (operator > 0) { - mprCopyVar(&rhs, &ep->result, MPR_SHALLOW_COPY); - if (evalCond(ep, &lhs, operator, &rhs) < 0) { - state = EJS_STATE_ERR; - break; - } - } - mprCopyVar(&lhs, &ep->result, MPR_SHALLOW_COPY); - - tid = ejsLexGetToken(ep, state); - if (tid == EJS_TOK_LOGICAL) { - operator = (int) *ep->token; - - } else if (tid == EJS_TOK_RPAREN || tid == EJS_TOK_SEMI) { - ejsLexPutbackToken(ep, tid, ep->token); - state = EJS_STATE_COND_DONE; - break; - - } else { - ejsLexPutbackToken(ep, tid, ep->token); - } - tid = (state == EJS_STATE_RELEXP_DONE); - - } while (state == EJS_STATE_RELEXP_DONE); - - mprDestroyVar(&lhs); - mprDestroyVar(&rhs); - return state; -} - -/******************************************************************************/ -/* - * Parse variable declaration list. Declarations can be of the following forms: - * var x; - * var x, y, z; - * var x = 1 + 2 / 3, y = 2 + 4; - * - * We set the variable to NULL if there is no associated assignment. - */ - -static int parseDeclaration(Ejs *ep, int state, int flags) -{ - int tid; - - mprAssert(ep); - - do { - if ((tid = ejsLexGetToken(ep, state)) != EJS_TOK_ID) { - return EJS_STATE_ERR; - } - ejsLexPutbackToken(ep, tid, ep->token); - - /* - * Parse the entire assignment or simple identifier declaration - */ - if (ejsParse(ep, EJS_STATE_DEC, flags) != EJS_STATE_DEC_DONE) { - return EJS_STATE_ERR; - } - - /* - * Peek at the next token, continue if comma seen - */ - tid = ejsLexGetToken(ep, state); - if (tid == EJS_TOK_SEMI) { - return EJS_STATE_DEC_LIST_DONE; - } else if (tid != EJS_TOK_COMMA) { - return EJS_STATE_ERR; - } - } while (tid == EJS_TOK_COMMA); - - if (tid != EJS_TOK_SEMI) { - return EJS_STATE_ERR; - } - return EJS_STATE_DEC_LIST_DONE; -} - -/******************************************************************************/ -/* - * Parse expression (leftHandSide operator rightHandSide) - */ - -static int parseExpr(Ejs *ep, int state, int flags) -{ - MprVar lhs, rhs; - int rel, tid; - - mprAssert(ep); - - mprDestroyVar(&ep->result); - rhs = lhs = mprCreateUndefinedVar(); - rel = 0; - tid = 0; - - do { - /* - * This loop will handle an entire expression list. We call parse - * to evalutate each term which returns the result in ep->result. - */ - if (tid == EJS_TOK_LOGICAL) { - state = ejsParse(ep, EJS_STATE_RELEXP, flags); - if (state != EJS_STATE_RELEXP_DONE) { - state = EJS_STATE_ERR; - break; - } - } else { - tid = ejsLexGetToken(ep, state); - if (tid == EJS_TOK_EXPR && (int) *ep->token == EJS_EXPR_MINUS) { - lhs = mprCreateIntegerVar(0); - rel = (int) *ep->token; - } else { - ejsLexPutbackToken(ep, tid, ep->token); - } - - state = ejsParse(ep, EJS_STATE_EXPR, flags); - if (state != EJS_STATE_EXPR_DONE) { - state = EJS_STATE_ERR; - break; - } - } - - if (rel > 0) { - mprCopyVar(&rhs, &ep->result, MPR_SHALLOW_COPY); - if (tid == EJS_TOK_LOGICAL) { - if (evalCond(ep, &lhs, rel, &rhs) < 0) { - state = EJS_STATE_ERR; - break; - } - } else { - if (evalExpr(ep, &lhs, rel, &rhs) < 0) { - state = EJS_STATE_ERR; - break; - } - } - } - mprCopyVar(&lhs, &ep->result, MPR_SHALLOW_COPY); - - if ((tid = ejsLexGetToken(ep, state)) == EJS_TOK_EXPR || - tid == EJS_TOK_INC_DEC || tid == EJS_TOK_LOGICAL) { - rel = (int) *ep->token; - - } else { - ejsLexPutbackToken(ep, tid, ep->token); - state = EJS_STATE_RELEXP_DONE; - } - - } while (state == EJS_STATE_EXPR_DONE); - - mprDestroyVar(&lhs); - mprDestroyVar(&rhs); - - return state; -} - -/******************************************************************************/ -/* - * Parse the "for ... in" statement. Format for the statement is: - * - * for (var in expr) { - * body; - * } - */ - -static int parseForIn(Ejs *ep, int state, int flags) -{ - EjsInput endScript, bodyScript; - MprVar *iteratorVar, *setVar, *vp, v; - int forFlags, tid; - - mprAssert(ep); - - tid = ejsLexGetToken(ep, state); - if (tid != EJS_TOK_ID) { - return -1; - } - ejsLexPutbackToken(ep, tid, ep->token); - - if (ejsParse(ep, EJS_STATE_EXPR, EJS_FLAGS_FOREACH | EJS_FLAGS_EXE) - != EJS_STATE_EXPR_DONE) { - return -1; - } - if (ep->currentProperty == 0) { - return -1; - } - iteratorVar = ep->currentProperty; - - if (ejsLexGetToken(ep, state) != EJS_TOK_IN) { - return -1; - } - - /* - * Get the set - */ - tid = ejsLexGetToken(ep, state); - if (tid != EJS_TOK_ID) { - return -1; - } - ejsLexPutbackToken(ep, tid, ep->token); - - if (ejsParse(ep, EJS_STATE_EXPR, flags) != EJS_STATE_EXPR_DONE) { - return -1; - } - if (ep->currentProperty == 0 && flags & EJS_FLAGS_EXE) { - return -1; - } - setVar = ep->currentProperty; - - if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { - return -1; - } - - /* - * Parse the body and remember the end of the body script - */ - forFlags = flags & ~EJS_FLAGS_EXE; - ejsLexSaveInputState(ep, &bodyScript); - if (ejsParse(ep, EJS_STATE_STMT, forFlags) != EJS_STATE_STMT_DONE) { - ejsLexFreeInputState(ep, &bodyScript); - return -1; - } - ejsInitInputState(&endScript); - ejsLexSaveInputState(ep, &endScript); - - /* - * Now actually do the for loop. - */ - if (flags & EJS_FLAGS_EXE) { - if (setVar->type == MPR_TYPE_OBJECT) { - vp = mprGetFirstProperty(setVar, MPR_ENUM_DATA); - while (vp) { - if (strcmp(vp->name, "length") != 0) { - v = mprCreateStringVar(vp->name, 0); - if (mprWriteProperty(iteratorVar, &v) < 0) { - ejsError(ep, "Can't write to variable\n"); - ejsLexFreeInputState(ep, &bodyScript); - ejsLexFreeInputState(ep, &endScript); - return -1; - } - - ejsLexRestoreInputState(ep, &bodyScript); - switch (ejsParse(ep, EJS_STATE_STMT, flags)) { - case EJS_STATE_RET: - return EJS_STATE_RET; - case EJS_STATE_STMT_DONE: - break; - default: - ejsLexFreeInputState(ep, &endScript); - ejsLexFreeInputState(ep, &bodyScript); - return -1; - } - } - vp = mprGetNextProperty(setVar, vp, MPR_ENUM_DATA); - } - } else { - ejsError(ep, "Variable \"%s\" is not an array or object", - setVar->name); - ejsLexFreeInputState(ep, &endScript); - ejsLexFreeInputState(ep, &bodyScript); - return -1; - } - } - ejsLexRestoreInputState(ep, &endScript); - - ejsLexFreeInputState(ep, &endScript); - ejsLexFreeInputState(ep, &bodyScript); - - return state; -} - -/******************************************************************************/ -/* - * Parse the for statement. Format for the expression is: - * - * for (initial; condition; incr) { - * body; - * } - */ - -static int parseFor(Ejs *ep, int state, int flags) -{ - EjsInput condScript, endScript, bodyScript, incrScript; - int forFlags, cond; - - ejsInitInputState(&endScript); - ejsInitInputState(&bodyScript); - ejsInitInputState(&incrScript); - ejsInitInputState(&condScript); - - mprAssert(ep); - - /* - * Evaluate the for loop initialization statement - */ - if (ejsParse(ep, EJS_STATE_EXPR, flags) != EJS_STATE_EXPR_DONE) { - return -1; - } - if (ejsLexGetToken(ep, state) != EJS_TOK_SEMI) { - return -1; - } - - /* - * The first time through, we save the current input context just prior - * to each step: prior to the conditional, the loop increment and - * the loop body. - */ - ejsLexSaveInputState(ep, &condScript); - if (ejsParse(ep, EJS_STATE_COND, flags) != EJS_STATE_COND_DONE) { - goto error; - } - cond = (ep->result.boolean != 0); - - if (ejsLexGetToken(ep, state) != EJS_TOK_SEMI) { - goto error; - } - - /* - * Don't execute the loop increment statement or the body - * first time. - */ - forFlags = flags & ~EJS_FLAGS_EXE; - ejsLexSaveInputState(ep, &incrScript); - if (ejsParse(ep, EJS_STATE_EXPR, forFlags) != EJS_STATE_EXPR_DONE) { - goto error; - } - if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { - goto error; - } - - /* - * Parse the body and remember the end of the body script - */ - ejsLexSaveInputState(ep, &bodyScript); - if (ejsParse(ep, EJS_STATE_STMT, forFlags) != EJS_STATE_STMT_DONE) { - goto error; - } - ejsLexSaveInputState(ep, &endScript); - - /* - * Now actually do the for loop. Note loop has been rotated - */ - while (cond && (flags & EJS_FLAGS_EXE)) { - /* - * Evaluate the body - */ - ejsLexRestoreInputState(ep, &bodyScript); - - switch (ejsParse(ep, EJS_STATE_STMT, flags)) { - case EJS_STATE_RET: - return EJS_STATE_RET; - case EJS_STATE_STMT_DONE: - break; - default: - goto error; - } - /* - * Evaluate the increment script - */ - ejsLexRestoreInputState(ep, &incrScript); - if (ejsParse(ep, EJS_STATE_EXPR, flags) != EJS_STATE_EXPR_DONE){ - goto error; - } - /* - * Evaluate the condition - */ - ejsLexRestoreInputState(ep, &condScript); - if (ejsParse(ep, EJS_STATE_COND, flags) != EJS_STATE_COND_DONE) { - goto error; - } - mprAssert(ep->result.type == MPR_TYPE_BOOL); - cond = (ep->result.boolean != 0); - } - - ejsLexRestoreInputState(ep, &endScript); - -done: - ejsLexFreeInputState(ep, &condScript); - ejsLexFreeInputState(ep, &incrScript); - ejsLexFreeInputState(ep, &endScript); - ejsLexFreeInputState(ep, &bodyScript); - return state; - -error: - state = EJS_STATE_ERR; - goto done; -} - -/******************************************************************************/ -/* - * Parse a function declaration - */ - -static int parseFunctionDec(Ejs *ep, int state, int flags) -{ - EjsInput endScript, bodyScript; - MprVar v, *currentObj, *vp; - char *procName; - int len, tid, bodyFlags; - - mprAssert(ep); - mprAssert(ejsPtr(ep->eid)); - - /* - * function <name>(arg, arg, arg) { body }; - * function name(arg, arg, arg) { body }; - */ - - tid = ejsLexGetToken(ep, state); - if (tid == EJS_TOK_ID) { - procName = mprStrdup(ep->token); - tid = ejsLexGetToken(ep, state); - } else { - procName = 0; - } - if (tid != EJS_TOK_LPAREN) { - mprFree(procName); - return EJS_STATE_ERR; - } - - /* - * Hand craft the function value structure. - */ - v = mprCreateFunctionVar(0, 0, 0); - tid = ejsLexGetToken(ep, state); - while (tid == EJS_TOK_ID) { - mprAddToArray(v.function.args, mprStrdup(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); - mprDestroyVar(&v); - 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); - mprDestroyVar(&v); - return EJS_STATE_ERR; - } - - /* - * Register the function name early to allow for recursive - * function calls (see note in ECMA standard, page 71) - */ - if (!(flags & EJS_FLAGS_ASSIGNMENT)) { - currentObj = ejsFindObj(ep, 0, procName, flags); - vp = mprSetProperty(currentObj, procName, &v); - } - - /* - * Parse the function body. Turn execute off. - */ - bodyFlags = flags & ~EJS_FLAGS_EXE; - ejsLexSaveInputState(ep, &bodyScript); - - do { - state = ejsParse(ep, EJS_STATE_STMT, bodyFlags); - } while (state == EJS_STATE_STMT_DONE); - - tid = ejsLexGetToken(ep, state); - if (state != EJS_STATE_STMT_BLOCK_DONE || tid != EJS_TOK_RBRACE) { - mprFree(procName); - mprDestroyVar(&v); - ejsLexFreeInputState(ep, &bodyScript); - return EJS_STATE_ERR; - } - ejsLexSaveInputState(ep, &endScript); - - /* - * Save the function body between the starting and ending parse positions. - * Overwrite the trailing '}' with a null. - */ - len = endScript.scriptServp - bodyScript.scriptServp; - v.function.body = mprMalloc(len + 1); - memcpy(v.function.body, bodyScript.scriptServp, len); - - if (len <= 0) { - v.function.body[0] = '\0'; - } else { - v.function.body[len - 1] = '\0'; - } - ejsLexFreeInputState(ep, &bodyScript); - ejsLexFreeInputState(ep, &endScript); - - /* - * If we are in an assignment, don't register the function name, rather - * return the function structure in the parser result. - */ - if (flags & EJS_FLAGS_ASSIGNMENT) { - mprCopyVar(&ep->result, &v, MPR_SHALLOW_COPY); - } else { - currentObj = ejsFindObj(ep, 0, procName, flags); - vp = mprSetProperty(currentObj, procName, &v); - } - - mprFree(procName); - mprDestroyVar(&v); - - return EJS_STATE_STMT; -} - -/******************************************************************************/ -/* - * Parse a function name and invoke the function - */ - -static int parseFunction(Ejs *ep, int state, int flags, char *id) -{ - EjsProc proc, *saveProc; - MprVar *saveObj; - - /* - * Must save any current ep->proc value for the current stack frame - * to allow for recursive function calls. - */ - saveProc = (ep->proc) ? ep->proc: 0; - - memset(&proc, 0, sizeof(EjsProc)); - proc.procName = mprStrdup(id); - proc.fn = ep->currentProperty; - proc.args = mprCreateArray(); - ep->proc = &proc; - - mprDestroyVar(&ep->result); - - saveObj = ep->currentObj; - if (ejsParse(ep, EJS_STATE_ARG_LIST, flags) != EJS_STATE_ARG_LIST_DONE) { - freeProc(&proc); - ep->proc = saveProc; - return -1; - } - ep->currentObj = saveObj; - - /* - * Evaluate the function if required - */ - if (flags & EJS_FLAGS_EXE) { - if (evalFunction(ep, ep->currentObj, flags) < 0) { - freeProc(&proc); - ep->proc = saveProc; - return -1; - } - } - - freeProc(&proc); - ep->proc = saveProc; - - if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { - return -1; - } - return state; -} - -/******************************************************************************/ -/* - * 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, char **fullName, - int *fullNameLen, int *done) -{ - int tid; - - mprFree(*id); - *id = mprStrdup(ep->token); -#if BLD_DEBUG - *fullNameLen = mprReallocStrcat(fullName, MPR_MAX_VAR, *fullNameLen, - 0, *id, NULL); -#endif - if (ep->currentObj == 0) { - ep->currentObj = ejsFindObj(ep, state, *id, flags); - } - - /* - * Find the referenced variable and store it in currentProperty. - */ - ep->currentProperty = ejsFindProperty(ep, state, ep->currentObj, - *id, flags); - updateResult(ep, state, flags, ep->currentProperty); - -#if BLD_DEBUG - if (ep->currentProperty && (ep->currentProperty->name == 0 || - ep->currentProperty->name[0] == '\0')) { - mprSetVarName(ep->currentProperty, *id); - } -#endif - - tid = ejsLexGetToken(ep, state); - if (tid == EJS_TOK_LPAREN) { - if (ep->currentProperty == 0 && (flags & EJS_FLAGS_EXE)) { - ejsError(ep, "Function name not defined \"%s\"\n", *id); - return -1; - } - ejsLexPutbackToken(ep, EJS_TOK_FUNCTION_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; - } - - /* - * 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, "Variable already defined \"%s\"\n", *id); - return -1; - } -#endif - /* - * Create or overwrite if it already exists - */ - mprSetPropertyValue(ep->currentObj, *id, - mprCreateUndefinedVar()); - ep->currentProperty = 0; - mprDestroyVar(&ep->result); - - } else if (flags & EJS_FLAGS_FOREACH) { - if (ep->currentProperty == 0) { - ep->currentProperty = - mprCreatePropertyValue(ep->currentObj, *id, - mprCreateUndefinedVar()); - } - - } else { - if (ep->currentProperty == 0) { - if (ep->currentObj == ep->global || - ep->currentObj == ep->local) { - ejsError(ep, "Undefined variable \"%s\"\n", *id); - return -1; - } - ep->currentProperty = mprCreatePropertyValue(ep->currentObj, - *id, mprCreateUndefinedVar()); - } - } - } - ejsLexPutbackToken(ep, tid, ep->token); - if (tid == EJS_TOK_RBRACKET || tid == EJS_TOK_COMMA || - tid == EJS_TOK_IN) { - *done = 1; - } - return state; -} - -/******************************************************************************/ -/* - * Parse an "if" statement - */ - -static int parseIf(Ejs *ep, int state, int flags, int *done) -{ - bool ifResult; - int thenFlags, elseFlags, tid; - - if (state != EJS_STATE_STMT) { - return -1; - } - if (ejsLexGetToken(ep, state) != EJS_TOK_LPAREN) { - return -1; - } - - /* - * Evaluate the entire condition list "(condition)" - */ - if (ejsParse(ep, EJS_STATE_COND, flags) != EJS_STATE_COND_DONE) { - return -1; - } - if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { - return -1; - } - - /* - * This is the "then" case. We need to always parse both cases and - * execute only the relevant case. - */ - ifResult = mprVarToBool(&ep->result); - if (ifResult) { - thenFlags = flags; - elseFlags = flags & ~EJS_FLAGS_EXE; - } else { - thenFlags = flags & ~EJS_FLAGS_EXE; - elseFlags = flags; - } - - /* - * Process the "then" case. - */ - switch (ejsParse(ep, EJS_STATE_STMT, thenFlags)) { - case EJS_STATE_RET: - state = EJS_STATE_RET; - return state; - case EJS_STATE_STMT_DONE: - break; - default: - return -1; - } - - /* - * Check to see if there is an "else" case - */ - removeNewlines(ep, state); - tid = ejsLexGetToken(ep, state); - if (tid != EJS_TOK_ELSE) { - ejsLexPutbackToken(ep, tid, ep->token); - *done = 1; - return state; - } - - /* - * Process the "else" case. - */ - switch (ejsParse(ep, EJS_STATE_STMT, elseFlags)) { - case EJS_STATE_RET: - state = EJS_STATE_RET; - return state; - case EJS_STATE_STMT_DONE: - break; - default: - return -1; - } - *done = 1; - return state; -} - -/******************************************************************************/ -/* - * Parse an "++" or "--" statement - */ - -static int parseInc(Ejs *ep, int state, int flags) -{ - MprVar one; - - if (! (flags & EJS_FLAGS_EXE)) { - return state; - } - - if (ep->currentProperty == 0) { - ejsError(ep, "Undefined variable \"%s\"\n", ep->token); - return -1; - } - one = mprCreateIntegerVar(1); - if (evalExpr(ep, ep->currentProperty, (int) *ep->token, - &one) < 0) { - return -1; - } - if (mprWriteProperty(ep->currentProperty, &ep->result) < 0) { - ejsError(ep, "Can't write to variable\n"); - return -1; - } - return state; -} - -/******************************************************************************/ -/* - * Evaluate a condition. Implements &&, ||, !. Returns with a boolean result - * in ep->result. Returns -1 on errors, zero if successful. - */ - -static int evalCond(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs) -{ - bool l, r, lval; - - mprAssert(rel > 0); - - l = mprVarToBool(lhs); - r = mprVarToBool(rhs); - - switch (rel) { - case EJS_COND_AND: - lval = l && r; - break; - case EJS_COND_OR: - lval = l || r; - break; - default: - ejsError(ep, "Bad operator %d", rel); - return -1; - } - - mprCopyVarValue(&ep->result, mprCreateBoolVar(lval), 0); - return 0; -} - - -/* - return true if this string is a valid number -*/ -static int string_is_number(const char *s) -{ - char *endptr = NULL; - if (s == NULL || *s == 0) { - return 0; - } - strtod(s, &endptr); - if (endptr != NULL && *endptr == 0) { - return 1; - } - return 0; -} - -/******************************************************************************/ -/* - * Evaluate an operation. Returns with the result in ep->result. Returns -1 - * on errors, otherwise zero is returned. - */ - -static int evalExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs) -{ - char *str; - MprNum lval, num; - int rc; - - mprAssert(rel > 0); - str = 0; - lval = 0; - - /* - * Type conversion. This is tricky and must be according to the standard. - * Only numbers (including floats) and strings can be compared. All other - * types are first converted to numbers by preference and if that fails, - * to strings. - * - * First convert objects to comparable types. The "===" operator will - * test the sameness of object references. Here, we coerce to comparable - * types first. - */ - if (lhs->type == MPR_TYPE_OBJECT) { - if (ejsRunFunction(ep->eid, lhs, "toValue", 0) == 0) { - mprCopyVar(lhs, &ep->result, MPR_SHALLOW_COPY); - } else { - if (ejsRunFunction(ep->eid, lhs, "toString", 0) == 0) { - mprCopyVar(lhs, &ep->result, MPR_SHALLOW_COPY); - } - } - /* Nothing more can be done */ - } - - if (rhs->type == MPR_TYPE_OBJECT) { - if (ejsRunFunction(ep->eid, rhs, "toValue", 0) == 0) { - mprCopyVar(rhs, &ep->result, MPR_SHALLOW_COPY); - } else { - if (ejsRunFunction(ep->eid, rhs, "toString", 0) == 0) { - mprCopyVar(rhs, &ep->result, MPR_SHALLOW_COPY); - } - } - /* Nothing more can be done */ - } - - /* undefined and null are special, in that they don't get promoted when - comparing */ - if (rel == EJS_EXPR_EQ || rel == EJS_EXPR_NOTEQ) { - if (lhs->type == MPR_TYPE_UNDEFINED || rhs->type == MPR_TYPE_UNDEFINED) { - return evalBoolExpr(ep, - lhs->type == MPR_TYPE_UNDEFINED, - rel, - rhs->type == MPR_TYPE_UNDEFINED); - } - - if (lhs->type == MPR_TYPE_NULL || rhs->type == MPR_TYPE_NULL) { - return evalBoolExpr(ep, - lhs->type == MPR_TYPE_NULL, - rel, - rhs->type == MPR_TYPE_NULL); - } - } - - /* - * From here on, lhs and rhs may contain allocated data (strings), so - * we must always destroy before overwriting. - */ - - /* - * Only allow a few bool operations. Otherwise convert to number. - */ - if (lhs->type == MPR_TYPE_BOOL && rhs->type == MPR_TYPE_BOOL && - (rel != EJS_EXPR_EQ && rel != EJS_EXPR_NOTEQ && - rel != EJS_EXPR_BOOL_COMP)) { - num = mprVarToNumber(lhs); - mprDestroyVar(lhs); - *lhs = mprCreateNumberVar(num); - } - - /* - * Types do not match, so try to coerce the right operand to match the left - * But first, try to convert a left operand that is a numeric stored as a - * string, into a numeric. - */ - if (lhs->type != rhs->type) { - if (lhs->type == MPR_TYPE_STRING) { - if (string_is_number(lhs->string)) { - num = mprVarToNumber(lhs); - lhs->allocatedVar = 0; - mprDestroyVar(lhs); - *lhs = mprCreateNumberVar(num); - /* Examine further below */ - - } else { - /* - * Convert the RHS to a string - */ - mprVarToString(&str, MPR_MAX_STRING, 0, rhs); - rhs->allocatedVar = 0; - mprDestroyVar(rhs); - *rhs = mprCreateStringVar(str, 1); - mprFree(str); - } - -#if BLD_FEATURE_FLOATING_POINT - } else if (lhs->type == MPR_TYPE_FLOAT) { - /* - * Convert rhs to floating - */ - double f = mprVarToFloat(rhs); - mprDestroyVar(rhs); - *rhs = mprCreateFloatVar(f); - -#endif -#if BLD_FEATURE_INT64 - } else if (lhs->type == MPR_TYPE_INT64) { - /* - * Convert the rhs to 64 bit - */ - int64 n = mprVarToInteger64(rhs); - mprDestroyVar(rhs); - *rhs = mprCreateInteger64Var(n); -#endif - } else if (lhs->type == MPR_TYPE_BOOL || lhs->type == MPR_TYPE_INT) { - - if (rhs->type == MPR_TYPE_STRING) { - /* - * Convert to lhs to a string - */ - mprVarToString(&str, MPR_MAX_STRING, 0, lhs); - mprDestroyVar(lhs); - *lhs = mprCreateStringVar(str, 1); - mprFree(str); - -#if BLD_FEATURE_FLOATING_POINT - } else if (rhs->type == MPR_TYPE_FLOAT) { - /* - * Convert lhs to floating - */ - double f = mprVarToFloat(lhs); - mprDestroyVar(lhs); - *lhs = mprCreateFloatVar(f); -#endif - - } else { - /* - * Convert both operands to numbers - */ - num = mprVarToNumber(lhs); - mprDestroyVar(lhs); - *lhs = mprCreateNumberVar(num); - - num = mprVarToNumber(rhs); - mprDestroyVar(rhs); - *rhs = mprCreateNumberVar(num); - } - } - } - - /* - * We have failed to coerce the types to be the same. Special case here - * for undefined and null. We need to allow comparisions against these - * special values. - */ - if (lhs->type == MPR_TYPE_UNDEFINED || lhs->type == MPR_TYPE_NULL) { - switch (rel) { - case EJS_EXPR_EQ: - lval = lhs->type == rhs->type; - break; - case EJS_EXPR_NOTEQ: - lval = lhs->type != rhs->type; - break; - case EJS_EXPR_BOOL_COMP: - lval = ! mprVarToBool(rhs); - break; - default: - lval = 0; - } - mprCopyVarValue(&ep->result, mprCreateBoolVar((bool) lval), 0); - return 0; - } - - /* - * Types are the same here - */ - switch (lhs->type) { - default: - case MPR_TYPE_UNDEFINED: - case MPR_TYPE_NULL: - /* Should be handled above */ - mprAssert(0); - return 0; - - case MPR_TYPE_STRING_CFUNCTION: - case MPR_TYPE_CFUNCTION: - case MPR_TYPE_FUNCTION: - case MPR_TYPE_OBJECT: - mprCopyVarValue(&ep->result, mprCreateBoolVar(0), 0); - return 0; - - case MPR_TYPE_PTR: - rc = evalPtrExpr(ep, lhs->ptr, rel, rhs->ptr); - break; - - case MPR_TYPE_BOOL: - rc = evalBoolExpr(ep, lhs->boolean, rel, rhs->boolean); - break; - -#if BLD_FEATURE_FLOATING_POINT - case MPR_TYPE_FLOAT: - rc = evalFloatExpr(ep, lhs->floating, rel, rhs->floating); - break; -#endif - - case MPR_TYPE_INT: - rc = evalNumericExpr(ep, (MprNum) lhs->integer, rel, - (MprNum) rhs->integer); - break; - -#if BLD_FEATURE_INT64 - case MPR_TYPE_INT64: - rc = evalNumericExpr(ep, (MprNum) lhs->integer64, rel, - (MprNum) rhs->integer64); - break; -#endif - - case MPR_TYPE_STRING: - rc = evalStringExpr(ep, lhs, rel, rhs); - } - return rc; -} - -/******************************************************************************/ -#if BLD_FEATURE_FLOATING_POINT -/* - * Expressions with floating operands - */ - -static int evalFloatExpr(Ejs *ep, double l, int rel, double r) -{ - double lval; - bool logical; - - lval = 0; - logical = 0; - - switch (rel) { - case EJS_EXPR_PLUS: - lval = l + r; - break; - case EJS_EXPR_INC: - lval = l + 1; - break; - case EJS_EXPR_MINUS: - lval = l - r; - break; - case EJS_EXPR_DEC: - lval = l - 1; - break; - case EJS_EXPR_MUL: - lval = l * r; - break; - case EJS_EXPR_DIV: - lval = l / r; - break; - default: - logical++; - break; - } - - /* - * Logical operators - */ - if (logical) { - - switch (rel) { - case EJS_EXPR_EQ: - lval = l == r; - break; - case EJS_EXPR_NOTEQ: - lval = l != r; - break; - case EJS_EXPR_LESS: - lval = (l < r) ? 1 : 0; - break; - case EJS_EXPR_LESSEQ: - lval = (l <= r) ? 1 : 0; - break; - case EJS_EXPR_GREATER: - lval = (l > r) ? 1 : 0; - break; - case EJS_EXPR_GREATEREQ: - lval = (l >= r) ? 1 : 0; - break; - case EJS_EXPR_BOOL_COMP: - lval = (r == 0) ? 1 : 0; - break; - default: - ejsError(ep, "Bad operator %d", rel); - return -1; - } - mprCopyVarValue(&ep->result, mprCreateBoolVar(lval != 0), 0); - - } else { - mprCopyVarValue(&ep->result, mprCreateFloatVar(lval), 0); - } - return 0; -} - -#endif /* BLD_FEATURE_FLOATING_POINT */ -/******************************************************************************/ -/* - * Expressions with boolean operands - */ - -static int evalBoolExpr(Ejs *ep, bool l, int rel, bool r) -{ - bool lval; - - switch (rel) { - case EJS_EXPR_EQ: - lval = l == r; - break; - case EJS_EXPR_NOTEQ: - lval = l != r; - break; - case EJS_EXPR_BOOL_COMP: - lval = (r == 0) ? 1 : 0; - break; - default: - ejsError(ep, "Bad operator %d", rel); - return -1; - } - mprCopyVarValue(&ep->result, mprCreateBoolVar(lval), 0); - return 0; -} - -static int evalPtrExpr(Ejs *ep, void *l, int rel, void *r) -{ - bool lval; - - switch (rel) { - case EJS_EXPR_EQ: - lval = l == r; - break; - case EJS_EXPR_NOTEQ: - lval = l != r; - break; - case EJS_EXPR_BOOL_COMP: - lval = (r == NULL) ? 1 : 0; - break; - default: - ejsError(ep, "Bad operator %d", rel); - return -1; - } - mprCopyVarValue(&ep->result, mprCreateBoolVar(lval), 0); - return 0; -} - -/******************************************************************************/ -/* - * Expressions with numeric operands - */ - -static int evalNumericExpr(Ejs *ep, MprNum l, int rel, MprNum r) -{ - MprNum lval; - bool logical; - - lval = 0; - logical = 0; - - switch (rel) { - case EJS_EXPR_PLUS: - lval = l + r; - break; - case EJS_EXPR_INC: - lval = l + 1; - break; - case EJS_EXPR_MINUS: - lval = l - r; - break; - case EJS_EXPR_DEC: - lval = l - 1; - break; - case EJS_EXPR_MUL: - lval = l * r; - break; - case EJS_EXPR_DIV: - if (r != 0) { - lval = l / r; - } else { - ejsError(ep, "Divide by zero"); - return -1; - } - break; - case EJS_EXPR_MOD: - if (r != 0) { - lval = l % r; - } else { - ejsError(ep, "Modulo zero"); - return -1; - } - break; - case EJS_EXPR_LSHIFT: - lval = l << r; - break; - case EJS_EXPR_RSHIFT: - lval = l >> r; - break; - - default: - logical++; - break; - } - - /* - * Logical operators - */ - if (logical) { - - switch (rel) { - case EJS_EXPR_EQ: - lval = l == r; - break; - case EJS_EXPR_NOTEQ: - lval = l != r; - break; - case EJS_EXPR_LESS: - lval = (l < r) ? 1 : 0; - break; - case EJS_EXPR_LESSEQ: - lval = (l <= r) ? 1 : 0; - break; - case EJS_EXPR_GREATER: - lval = (l > r) ? 1 : 0; - break; - case EJS_EXPR_GREATEREQ: - lval = (l >= r) ? 1 : 0; - break; - case EJS_EXPR_BOOL_COMP: - lval = (r == 0) ? 1 : 0; - break; - default: - ejsError(ep, "Bad operator %d", rel); - return -1; - } - mprCopyVarValue(&ep->result, mprCreateBoolVar(lval != 0), 0); - - } else { - mprCopyVarValue(&ep->result, mprCreateNumberVar(lval), 0); - } - return 0; -} - -/******************************************************************************/ -/* - * Expressions with string operands - */ - -static int evalStringExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs) -{ - int lval; - - mprAssert(ep); - mprAssert(lhs); - mprAssert(rhs); - - switch (rel) { - case EJS_EXPR_LESS: - lval = strcmp(lhs->string, rhs->string) < 0; - break; - case EJS_EXPR_LESSEQ: - lval = strcmp(lhs->string, rhs->string) <= 0; - break; - case EJS_EXPR_GREATER: - lval = strcmp(lhs->string, rhs->string) > 0; - break; - case EJS_EXPR_GREATEREQ: - lval = strcmp(lhs->string, rhs->string) >= 0; - break; - case EJS_EXPR_EQ: - lval = strcmp(lhs->string, rhs->string) == 0; - break; - case EJS_EXPR_NOTEQ: - lval = strcmp(lhs->string, rhs->string) != 0; - break; - case EJS_EXPR_PLUS: - /* - * This differs from all the above operations. We append rhs to lhs. - */ - mprDestroyVar(&ep->result); - appendValue(&ep->result, lhs); - appendValue(&ep->result, rhs); - return 0; - - case EJS_EXPR_INC: - case EJS_EXPR_DEC: - case EJS_EXPR_MINUS: - case EJS_EXPR_DIV: - case EJS_EXPR_MOD: - case EJS_EXPR_LSHIFT: - case EJS_EXPR_RSHIFT: - default: - ejsError(ep, "Bad operator"); - return -1; - } - - mprCopyVarValue(&ep->result, mprCreateBoolVar(lval), 0); - return 0; -} - -/******************************************************************************/ -/* - * Evaluate a function. obj is set to the current object if a function is being - * run. - */ - -static int evalFunction(Ejs *ep, MprVar *obj, int flags) -{ - EjsProc *proc; - MprVar arguments, callee, thisObject, *prototype, **argValues; - MprArray *formalArgs, *actualArgs; - char buf[16], **argNames, **argBuf; - int i, rc, fid; - - mprAssert(ep); - mprAssert(ejsPtr(ep->eid)); - - rc = -1; - proc = ep->proc; - prototype = proc->fn; - actualArgs = proc->args; - argValues = (MprVar**) actualArgs->handles; - - /* - * Create a new variable stack frame. ie. new local variables. - */ - fid = ejsOpenBlock(ep->eid); - - if (flags & EJS_FLAGS_NEW) { - /* - * Create a new bare object and pass it into the constructor as the - * "this" local variable. - */ - thisObject = ejsCreateObj("this", EJS_OBJ_HASH_SIZE); - mprCreatePropertyValue(ep->local, "this", thisObject); - - } else if (obj) { - mprCreateProperty(ep->local, "this", obj); - } - - switch (prototype->type) { - default: - mprAssert(0); - break; - - case MPR_TYPE_STRING_CFUNCTION: - if (actualArgs->used > 0) { - argBuf = mprMalloc((1+actualArgs->used) * sizeof(char*)); - for (i = 0; i < actualArgs->used; i++) { - mprVarToString(&argBuf[i], MPR_MAX_STRING, 0, argValues[i]); - } - argBuf[i] = NULL; - } else { - argBuf = 0; - } - - /* - * Call the function depending on the various handle flags - */ - ep->thisPtr = prototype->cFunctionWithStrings.thisPtr; - if (prototype->flags & MPR_VAR_ALT_HANDLE) { - rc = ((EjsAltStringCFunction) prototype->cFunctionWithStrings.fn) - (ep->eid, ep->altHandle, actualArgs->used, argBuf); - } else if (prototype->flags & MPR_VAR_SCRIPT_HANDLE) { - rc = (prototype->cFunctionWithStrings.fn)(ep->eid, - actualArgs->used, argBuf); - } else { - rc = (prototype->cFunctionWithStrings.fn)(ep->primaryHandle, - actualArgs->used, argBuf); - } - - if (actualArgs->used > 0) { - for (i = 0; i < actualArgs->used; i++) { - mprFree(argBuf[i]); - } - mprFree(argBuf); - } - ep->thisPtr = 0; - break; - - case MPR_TYPE_CFUNCTION: - /* - * Call the function depending on the various handle flags - */ - ep->thisPtr = prototype->cFunction.thisPtr; - if (prototype->flags & MPR_VAR_ALT_HANDLE) { - rc = ((EjsAltCFunction) prototype->cFunction.fn) - (ep->eid, ep->altHandle, actualArgs->used, argValues); - } else if (prototype->flags & MPR_VAR_SCRIPT_HANDLE) { - rc = (prototype->cFunction.fn)(ep->eid, actualArgs->used, - argValues); - } else { - rc = (prototype->cFunction.fn)(ep->primaryHandle, - actualArgs->used, argValues); - } - ep->thisPtr = 0; - break; - - case MPR_TYPE_FUNCTION: - - formalArgs = prototype->function.args; - argNames = (char**) formalArgs->handles; - - if (formalArgs->used > actualArgs->used) { - ejsError(ep, "Bad number of args. Should be %d", - formalArgs->used); - return -1; - } - - /* - * Create the arguments and callee variables - */ - arguments = ejsCreateObj("arguments", EJS_SMALL_OBJ_HASH_SIZE); - callee = ejsCreateObj("callee", EJS_SMALL_OBJ_HASH_SIZE); - - /* - * Overwrite the length property - */ - mprCreatePropertyValue(&arguments, "length", - mprCreateIntegerVar(actualArgs->used)); - mprCreatePropertyValue(&callee, "length", - mprCreateIntegerVar(formalArgs->used)); - - /* - * Define all the agruments to be set to the actual parameters - */ - for (i = 0; i < formalArgs->used; i++) { - mprCreateProperty(ep->local, argNames[i], argValues[i]); - } - for (i = 0; i < actualArgs->used; i++) { - mprItoa(i, buf, sizeof(buf)); - mprCreateProperty(&arguments, buf, argValues[i]); - } - - mprCreateProperty(&arguments, "callee", &callee); - mprCreateProperty(ep->local, "arguments", &arguments); - - /* - * Can destroy our variables here as they are now referenced via - * "local" - */ - mprDestroyVar(&callee); - mprDestroyVar(&arguments); - - /* - * Actually run the function - */ - rc = ejsEvalScript(ep->eid, prototype->function.body, 0, 0); - break; - } - - ejsCloseBlock(ep->eid, fid); - - /* - * New statements return the newly created object as the result of the - * command - */ - if (flags & EJS_FLAGS_NEW) { - mprDestroyVar(&ep->result); - /* - * Don't copy, we want to assign the actual object into result. - * (mprCopyVar would inc the refCount to 2). - */ - ep->result = thisObject; - } - return rc; -} - -/******************************************************************************/ -/* - * Run a function - */ - -int ejsRunFunction(int eid, MprVar *obj, const char *functionName, - MprArray *args) -{ - EjsProc proc, *saveProc; - Ejs *ep; - int rc; - - mprAssert(obj); - mprAssert(functionName && *functionName); - - if ((ep = ejsPtr(eid)) == NULL) { - mprAssert(ep); - return MPR_ERR_NOT_FOUND; - } - saveProc = ep->proc; - ep->proc = &proc; - - memset(&proc, 0, sizeof(EjsProc)); - mprDestroyVar(&ep->result); - - proc.fn = mprGetProperty(obj, functionName, 0); - if (proc.fn == 0 || proc.fn->type == MPR_TYPE_UNDEFINED) { - ep->proc = saveProc; - return MPR_ERR_NOT_FOUND; - } - proc.procName = mprStrdup(functionName); - if (args == 0) { - proc.args = mprCreateArray(); - rc = evalFunction(ep, obj, 0); - } else { - proc.args = args; - rc = evalFunction(ep, obj, 0); - proc.args = 0; - } - - freeProc(&proc); - ep->proc = saveProc; - - return rc; -} - -/******************************************************************************/ -/* - * Find which object contains the property given the current context. - * Only used for top level properties. - */ - -MprVar *ejsFindObj(Ejs *ep, int state, const char *property, int flags) -{ - MprVar *obj; - - mprAssert(ep); - mprAssert(property && *property); - - if (flags & EJS_FLAGS_GLOBAL) { - obj = ep->global; - - } else if (state == EJS_STATE_DEC || flags & EJS_FLAGS_LOCAL) { - obj = ep->local; - - } else { - /* First look local, then look global */ - if (mprGetProperty(ep->local, property, 0)) { - obj = ep->local; - } else { - obj = ep->global; - } - } - return obj; -} - -/******************************************************************************/ -/* - * Find an object property given a object and a property name. We - * intelligently look in the local and global namespaces depending on - * our state. If not found in local or global, try base classes for function - * names only. Returns the property or NULL. - */ - -MprVar *ejsFindProperty(Ejs *ep, int state, MprVar *obj, char *property, - int flags) -{ - MprVar *vp; - - mprAssert(ep); - if (flags & EJS_FLAGS_EXE) { - mprAssert(property && *property); - } - - if (obj != 0) { -#if FUTURE && MB - op = obj; - do { - vp = mprGetProperty(op, property, 0); - if (vp != 0) { - if (op != obj && mprVarIsFunction(vp->type)) { - } - break; - } - op = op->baseObj; - } while (op); -#endif - vp = mprGetProperty(obj, property, 0); - - } else { - if (state == EJS_STATE_DEC) { - vp = mprGetProperty(ep->local, property, 0); - - } else { - /* Look local first, then global */ - vp = mprGetProperty(ep->local, property, 0); - if (vp == NULL) { - vp = mprGetProperty(ep->global, property, 0); - } - } - } - return vp; -} - -/******************************************************************************/ -/* - * Update result - */ - -static void updateResult(Ejs *ep, int state, int flags, MprVar *vp) -{ - if (flags & EJS_FLAGS_EXE && state != EJS_STATE_DEC) { - mprDestroyVar(&ep->result); - if (vp) { - mprCopyProperty(&ep->result, vp, MPR_SHALLOW_COPY); - } - } -} - -/******************************************************************************/ -/* - * Append to the pointer value - */ - -static void appendValue(MprVar *dest, MprVar *src) -{ - char *value, *oldBuf, *buf; - int len, oldLen; - - mprAssert(dest); - - mprVarToString(&value, MPR_MAX_STRING, 0, src); - - if (mprVarIsValid(dest)) { - len = strlen(value); - oldBuf = dest->string; - oldLen = strlen(oldBuf); - buf = mprRealloc(oldBuf, (len + oldLen + 1) * sizeof(char)); - dest->string = buf; - strcpy(&buf[oldLen], value); - - } else { - *dest = mprCreateStringVar(value, 1); - } - mprFree(value); -} - -/******************************************************************************/ -/* - * Exit with status - */ - -void ejsSetExitStatus(int eid, int status) -{ - Ejs *ep; - - if ((ep = ejsPtr(eid)) == NULL) { - mprAssert(ep); - return; - } - ep->exitStatus = status; - ep->flags |= EJS_FLAGS_EXIT; -} - -/******************************************************************************/ -/* - * Free an argument list - */ - -static void freeProc(EjsProc *proc) -{ - MprVar **argValues; - int i; - - if (proc->args) { - argValues = (MprVar**) proc->args->handles; - - for (i = 0; i < proc->args->max; i++) { - if (argValues[i]) { - mprDestroyVar(argValues[i]); - mprFree(argValues[i]); - mprRemoveFromArray(proc->args, i); - } - } - - mprDestroyArray(proc->args); - } - - if (proc->procName) { - mprFree(proc->procName); - proc->procName = NULL; - } -} - -/******************************************************************************/ -/* - * This function removes any new lines. Used for else cases, etc. - */ - -static void removeNewlines(Ejs *ep, int state) -{ - int tid; - - do { - tid = ejsLexGetToken(ep, state); - } while (tid == EJS_TOK_NEWLINE); - - ejsLexPutbackToken(ep, tid, ep->token); -} - -/******************************************************************************/ - -#else -void ejsParserDummy() {} - -/******************************************************************************/ -#endif /* BLD_FEATURE_EJS */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim:tw=78 - * vim600: sw=4 ts=4 fdm=marker - * vim<600: sw=4 ts=4 - */ |