3 * @brief EJS Parser and Execution
5 /********************************* Copyright **********************************/
9 * Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved.
10 * Portions Copyright (c) GoAhead Software, 1995-2000. All Rights Reserved.
12 * This software is distributed under commercial and open source licenses.
13 * You may use the GPL open source license described below or you may acquire
14 * a commercial license from Mbedthis Software. You agree to be fully bound
15 * by the terms of either license. Consult the LICENSE.TXT distributed with
16 * this software for full details.
18 * This software is open source; you can redistribute it and/or modify it
19 * under the terms of the GNU General Public License as published by the
20 * Free Software Foundation; either version 2 of the License, or (at your
21 * option) any later version. See the GNU General Public License for more
22 * details at: http://www.mbedthis.com/downloads/gplLicense.html
24 * This program is distributed WITHOUT ANY WARRANTY; without even the
25 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
27 * This GPL license does NOT permit incorporating this software into
28 * proprietary programs. If you are unable to comply with the GPL, you must
29 * acquire a commercial license to use this software. Commercial licenses
30 * for this software and support services are available from Mbedthis
31 * Software at http://www.mbedthis.com
36 /********************************** Includes **********************************/
38 #include "ejsInternal.h"
42 /****************************** Forward Declarations **************************/
44 static void appendValue(MprVar *v1, MprVar *v2);
45 static int evalCond(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs);
46 static int evalExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs);
47 #if BLD_FEATURE_FLOATING_POINT
48 static int evalFloatExpr(Ejs *ep, double l, int rel, double r);
50 static int evalBoolExpr(Ejs *ep, bool l, int rel, bool r);
51 static int evalNumericExpr(Ejs *ep, MprNum l, int rel, MprNum r);
52 static int evalStringExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs);
53 static int evalFunction(Ejs *ep, MprVar *obj, int flags);
54 static void freeProc(EjsProc *proc);
55 static int parseArgs(Ejs *ep, int state, int flags);
56 static int parseAssignment(Ejs *ep, int state, int flags, char *id,
58 static int parseCond(Ejs *ep, int state, int flags);
59 static int parseDeclaration(Ejs *ep, int state, int flags);
60 static int parseExpr(Ejs *ep, int state, int flags);
61 static int parseFor(Ejs *ep, int state, int flags);
62 static int parseForIn(Ejs *ep, int state, int flags);
63 static int parseFunctionDec(Ejs *ep, int state, int flags);
64 static int parseFunction(Ejs *ep, int state, int flags, char *id);
65 static int parseId(Ejs *ep, int state, int flags, char **id,
66 char **fullName, int *fullNameLen, int *done);
67 static int parseInc(Ejs *ep, int state, int flags);
68 static int parseIf(Ejs *ep, int state, int flags, int *done);
69 static int parseStmt(Ejs *ep, int state, int flags);
70 static void removeNewlines(Ejs *ep, int state);
71 static void updateResult(Ejs *ep, int state, int flags, MprVar *vp);
73 /************************************* Code ***********************************/
75 * Recursive descent parser for EJS
78 int ejsParse(Ejs *ep, int state, int flags)
84 * Any statement, function arguments or conditional expressions
87 if ((state = parseStmt(ep, state, flags)) != EJS_STATE_STMT_DONE &&
88 state != EJS_STATE_EOF && state != EJS_STATE_STMT_BLOCK_DONE &&
89 state != EJS_STATE_RET) {
90 state = EJS_STATE_ERR;
95 if ((state = parseStmt(ep, state, flags)) != EJS_STATE_DEC_DONE &&
96 state != EJS_STATE_EOF) {
97 state = EJS_STATE_ERR;
102 if ((state = parseStmt(ep, state, flags)) != EJS_STATE_EXPR_DONE &&
103 state != EJS_STATE_EOF) {
104 state = EJS_STATE_ERR;
109 * Variable declaration list
111 case EJS_STATE_DEC_LIST:
112 state = parseDeclaration(ep, state, flags);
116 * Function argument string
118 case EJS_STATE_ARG_LIST:
119 state = parseArgs(ep, state, flags);
123 * Logical condition list (relational operations separated by &&, ||)
126 state = parseCond(ep, state, flags);
132 case EJS_STATE_RELEXP:
133 state = parseExpr(ep, state, flags);
137 if (state == EJS_STATE_ERR && ep->error == NULL) {
138 ejsError(ep, "Syntax error");
143 /******************************************************************************/
145 * Parse any statement including functions and simple relational operations
148 static int parseStmt(Ejs *ep, int state, int flags)
151 MprVar *vp, *saveObj;
152 char *id, *fullName, *initToken;
153 int done, expectSemi, tid, fullNameLen, rel;
165 ep->currentProperty = 0;
167 for (done = 0; !done && state != EJS_STATE_ERR; ) {
168 tid = ejsLexGetToken(ep, state);
172 ejsLexPutbackToken(ep, EJS_TOK_EXPR, ep->token);
177 rel = (int) *ep->token;
178 if (state == EJS_STATE_EXPR) {
179 ejsLexPutbackToken(ep, EJS_TOK_EXPR, ep->token);
184 case EJS_TOK_LOGICAL:
185 ejsLexPutbackToken(ep, tid, ep->token);
190 state = EJS_STATE_ERR;
195 state = EJS_STATE_EOF;
199 case EJS_TOK_NEWLINE:
204 * This case is when we discover no statement and just a lone ';'
206 if (state != EJS_STATE_STMT) {
207 ejsLexPutbackToken(ep, tid, ep->token);
213 if (flags & EJS_FLAGS_EXE) {
214 if (ep->currentProperty == 0) {
215 ejsError(ep, "Undefined object \"%s\"\n", id);
219 ep->currentObj = ep->currentProperty;
221 if ((tid = ejsLexGetToken(ep, state)) != EJS_TOK_ID) {
222 ejsError(ep, "Bad property after '.': %s\n", ep->token);
226 id = mprStrdup(ep->token);
228 vp = ejsFindProperty(ep, state, ep->currentObj, id, flags);
229 updateResult(ep, state, flags, vp);
232 fullNameLen = mprReallocStrcat(&fullName, MPR_MAX_VAR, fullNameLen,
236 ep->currentProperty = vp;
237 ejsLexPutbackToken(ep, tid, ep->token);
240 case EJS_TOK_LBRACKET:
241 ep->currentObj = ep->currentProperty;
242 saveObj = ep->currentObj;
243 if (ejsParse(ep, EJS_STATE_RELEXP, flags) != EJS_STATE_RELEXP_DONE){
246 ep->currentObj = saveObj;
249 mprVarToString(&id, MPR_MAX_STRING, 0, &ep->result);
252 if (flags & EJS_FLAGS_EXE) {
254 "[] expression evaluates to the empty string\n");
258 vp = ejsFindProperty(ep, state, ep->currentObj, id, flags);
259 ep->currentProperty = vp;
260 updateResult(ep, state, flags, vp);
264 if (id[0] && strlen(id) < (MPR_MAX_VAR / 2)) {
266 * If not executing yet, id may not be known
268 fullNameLen = mprReallocStrcat(&fullName, MPR_MAX_VAR,
269 fullNameLen, 0, "[", id, "]", 0);
273 if ((tid = ejsLexGetToken(ep, state)) != EJS_TOK_RBRACKET) {
274 ejsError(ep, "Missing ']'\n");
280 state = parseId(ep, state, flags, &id, &fullName, &fullNameLen,
282 if (done && state == EJS_STATE_STMT) {
287 case EJS_TOK_ASSIGNMENT:
288 state = parseAssignment(ep, state, flags, id, fullName);
289 if (state == EJS_STATE_STMT) {
295 case EJS_TOK_INC_DEC:
296 state = parseInc(ep, state, flags);
297 if (state == EJS_STATE_STMT) {
303 if (ejsParse(ep, EJS_STATE_EXPR, flags | EJS_FLAGS_NEW)
304 != EJS_STATE_EXPR_DONE) {
310 if (ejsParse(ep, EJS_STATE_EXPR,
311 flags | EJS_FLAGS_DELETE) != EJS_STATE_EXPR_DONE) {
314 mprDeleteProperty(ep->currentObj, ep->currentProperty->name);
318 case EJS_TOK_FUNCTION:
319 state = parseFunctionDec(ep, state, flags);
323 case EJS_TOK_LITERAL:
325 * Set the result to the string literal
327 mprCopyVarValue(&ep->result, mprCreateStringVar(ep->token, 0),
329 if (state == EJS_STATE_STMT) {
337 * Set the result to the parsed number
339 mprCopyVar(&ep->result, &ep->tokenNumber, 0);
340 if (state == EJS_STATE_STMT) {
346 case EJS_TOK_FUNCTION_NAME:
347 state = parseFunction(ep, state, flags, id);
348 if (state == EJS_STATE_STMT) {
351 if (ep->flags & EJS_FLAGS_EXIT) {
352 state = EJS_STATE_RET;
358 state = parseIf(ep, state, flags, &done);
359 if (state == EJS_STATE_RET) {
365 if (state != EJS_STATE_STMT) {
368 if (ejsLexGetToken(ep, state) != EJS_TOK_LPAREN) {
372 * Need to peek 2-3 tokens ahead and see if this is a
373 * for ([var] x in set)
375 * for (init ; whileCond; incr)
377 initId = ejsLexGetToken(ep, EJS_STATE_EXPR);
378 if (initId == EJS_TOK_ID && strcmp(ep->token, "var") == 0) {
379 /* Simply eat var tokens */
380 initId = ejsLexGetToken(ep, EJS_STATE_EXPR);
382 initToken = mprStrdup(ep->token);
384 tid = ejsLexGetToken(ep, EJS_STATE_EXPR);
386 ejsLexPutbackToken(ep, tid, ep->token);
387 ejsLexPutbackToken(ep, initId, initToken);
390 if (tid == EJS_TOK_IN) {
391 if ((state = parseForIn(ep, state, flags)) < 0) {
395 if ((state = parseFor(ep, state, flags)) < 0) {
403 if (ejsParse(ep, EJS_STATE_DEC_LIST, flags)
404 != EJS_STATE_DEC_LIST_DONE) {
411 ejsLexPutbackToken(ep, tid, ep->token);
416 if (state == EJS_STATE_EXPR) {
417 if (ejsParse(ep, EJS_STATE_RELEXP, flags)
418 != EJS_STATE_RELEXP_DONE) {
421 if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) {
429 ejsLexPutbackToken(ep, tid, ep->token);
435 * This handles any code in braces except "if () {} else {}"
437 if (state != EJS_STATE_STMT) {
442 * Parse will return EJS_STATE_STMT_BLOCK_DONE when the RBRACE
446 state = ejsParse(ep, EJS_STATE_STMT, flags);
447 } while (state == EJS_STATE_STMT_DONE);
449 if (state != EJS_STATE_RET) {
450 if (ejsLexGetToken(ep, state) != EJS_TOK_RBRACE) {
453 state = EJS_STATE_STMT_DONE;
459 if (state == EJS_STATE_STMT) {
460 ejsLexPutbackToken(ep, tid, ep->token);
461 state = EJS_STATE_STMT_BLOCK_DONE;
468 if (ejsParse(ep, EJS_STATE_RELEXP, flags)
469 != EJS_STATE_RELEXP_DONE) {
472 if (flags & EJS_FLAGS_EXE) {
473 while (ejsLexGetToken(ep, state) != EJS_TOK_EOF) {
476 state = EJS_STATE_RET;
484 tid = ejsLexGetToken(ep, state);
485 if (tid != EJS_TOK_SEMI && tid != EJS_TOK_NEWLINE &&
486 tid != EJS_TOK_EOF) {
491 * Skip newline after semi-colon
493 removeNewlines(ep, state);
497 * Free resources and return the correct status
508 return EJS_STATE_STMT_DONE;
511 return EJS_STATE_DEC_DONE;
514 return EJS_STATE_EXPR_DONE;
516 case EJS_STATE_STMT_DONE:
517 case EJS_STATE_STMT_BLOCK_DONE:
523 return EJS_STATE_ERR;
530 state = EJS_STATE_ERR;
534 /******************************************************************************/
536 * Parse function arguments
539 static int parseArgs(Ejs *ep, int state, int flags)
547 * Peek and see if there are no args
549 tid = ejsLexGetToken(ep, state);
550 ejsLexPutbackToken(ep, tid, ep->token);
551 if (tid == EJS_TOK_RPAREN) {
555 state = ejsParse(ep, EJS_STATE_RELEXP, flags);
556 if (state == EJS_STATE_EOF || state == EJS_STATE_ERR) {
559 if (state == EJS_STATE_RELEXP_DONE) {
560 if (flags & EJS_FLAGS_EXE) {
561 mprAssert(ep->proc->args);
562 mprAddToArray(ep->proc->args,
563 mprDupVar(&ep->result, MPR_SHALLOW_COPY));
567 * Peek at the next token, continue if more args (ie. comma seen)
569 tid = ejsLexGetToken(ep, state);
570 if (tid != EJS_TOK_COMMA) {
571 ejsLexPutbackToken(ep, tid, ep->token);
573 } while (tid == EJS_TOK_COMMA);
575 if (tid != EJS_TOK_RPAREN && state != EJS_STATE_RELEXP_DONE) {
576 return EJS_STATE_ERR;
578 return EJS_STATE_ARG_LIST_DONE;
581 /******************************************************************************/
583 * Parse an assignment statement
586 static int parseAssignment(Ejs *ep, int state, int flags, char *id,
589 MprVar *vp, *saveProperty, *saveObj;
595 saveObj = ep->currentObj;
596 saveProperty = ep->currentProperty;
597 if (ejsParse(ep, EJS_STATE_RELEXP, flags | EJS_FLAGS_ASSIGNMENT)
598 != EJS_STATE_RELEXP_DONE) {
601 ep->currentObj = saveObj;
602 ep->currentProperty = saveProperty;
604 if (! (flags & EJS_FLAGS_EXE)) {
608 if (ep->currentProperty) {
610 * Update the variable. Update the property name if not
613 if (ep->currentProperty->name == 0 ||
614 ep->currentProperty->name[0] == '\0') {
615 mprSetVarName(ep->currentProperty, id);
617 if (mprWriteProperty(ep->currentProperty, &ep->result) < 0){
618 ejsError(ep, "Can't write to variable\n");
624 * Create the variable
626 if (ep->currentObj) {
627 if (ep->currentObj->type != MPR_TYPE_OBJECT) {
628 if (strcmp(ep->currentObj->name, "session") == 0) {
629 ejsError(ep, "Variable \"%s\" is not an array or object."
630 "If using ESP, you need useSession(); in your page.",
631 ep->currentObj->name);
633 ejsError(ep, "Variable \"%s\" is not an array or object",
634 ep->currentObj->name);
638 vp = mprCreateProperty(ep->currentObj, id, &ep->result);
642 * Standard says: "var x" means declare locally.
643 * "x = 2" means declare globally if x is undefined.
645 if (state == EJS_STATE_DEC) {
646 vp = mprCreateProperty(ep->local, id, &ep->result);
648 vp = mprCreateProperty(ep->global, id, &ep->result);
652 mprSetVarFullName(vp, fullName);
658 /******************************************************************************/
660 * Parse conditional expression (relational ops separated by ||, &&)
663 static int parseCond(Ejs *ep, int state, int flags)
670 mprDestroyVar(&ep->result);
671 rhs = lhs = mprCreateUndefinedVar();
676 * Recurse to handle one side of a conditional. Accumulate the
677 * left hand side and the final result in ep->result.
679 state = ejsParse(ep, EJS_STATE_RELEXP, flags);
680 if (state != EJS_STATE_RELEXP_DONE) {
681 state = EJS_STATE_ERR;
686 mprCopyVar(&rhs, &ep->result, MPR_SHALLOW_COPY);
687 if (evalCond(ep, &lhs, operator, &rhs) < 0) {
688 state = EJS_STATE_ERR;
692 mprCopyVar(&lhs, &ep->result, MPR_SHALLOW_COPY);
694 tid = ejsLexGetToken(ep, state);
695 if (tid == EJS_TOK_LOGICAL) {
696 operator = (int) *ep->token;
698 } else if (tid == EJS_TOK_RPAREN || tid == EJS_TOK_SEMI) {
699 ejsLexPutbackToken(ep, tid, ep->token);
700 state = EJS_STATE_COND_DONE;
704 ejsLexPutbackToken(ep, tid, ep->token);
706 tid = (state == EJS_STATE_RELEXP_DONE);
708 } while (state == EJS_STATE_RELEXP_DONE);
715 /******************************************************************************/
717 * Parse variable declaration list. Declarations can be of the following forms:
720 * var x = 1 + 2 / 3, y = 2 + 4;
722 * We set the variable to NULL if there is no associated assignment.
725 static int parseDeclaration(Ejs *ep, int state, int flags)
732 if ((tid = ejsLexGetToken(ep, state)) != EJS_TOK_ID) {
733 return EJS_STATE_ERR;
735 ejsLexPutbackToken(ep, tid, ep->token);
738 * Parse the entire assignment or simple identifier declaration
740 if (ejsParse(ep, EJS_STATE_DEC, flags) != EJS_STATE_DEC_DONE) {
741 return EJS_STATE_ERR;
745 * Peek at the next token, continue if comma seen
747 tid = ejsLexGetToken(ep, state);
748 if (tid == EJS_TOK_SEMI) {
749 return EJS_STATE_DEC_LIST_DONE;
750 } else if (tid != EJS_TOK_COMMA) {
751 return EJS_STATE_ERR;
753 } while (tid == EJS_TOK_COMMA);
755 if (tid != EJS_TOK_SEMI) {
756 return EJS_STATE_ERR;
758 return EJS_STATE_DEC_LIST_DONE;
761 /******************************************************************************/
763 * Parse expression (leftHandSide operator rightHandSide)
766 static int parseExpr(Ejs *ep, int state, int flags)
773 mprDestroyVar(&ep->result);
774 rhs = lhs = mprCreateUndefinedVar();
780 * This loop will handle an entire expression list. We call parse
781 * to evalutate each term which returns the result in ep->result.
783 if (tid == EJS_TOK_LOGICAL) {
784 state = ejsParse(ep, EJS_STATE_RELEXP, flags);
785 if (state != EJS_STATE_RELEXP_DONE) {
786 state = EJS_STATE_ERR;
790 tid = ejsLexGetToken(ep, state);
791 if (tid == EJS_TOK_EXPR && (int) *ep->token == EJS_EXPR_MINUS) {
792 lhs = mprCreateIntegerVar(0);
793 rel = (int) *ep->token;
795 ejsLexPutbackToken(ep, tid, ep->token);
798 state = ejsParse(ep, EJS_STATE_EXPR, flags);
799 if (state != EJS_STATE_EXPR_DONE) {
800 state = EJS_STATE_ERR;
806 mprCopyVar(&rhs, &ep->result, MPR_SHALLOW_COPY);
807 if (tid == EJS_TOK_LOGICAL) {
808 if (evalCond(ep, &lhs, rel, &rhs) < 0) {
809 state = EJS_STATE_ERR;
813 if (evalExpr(ep, &lhs, rel, &rhs) < 0) {
814 state = EJS_STATE_ERR;
819 mprCopyVar(&lhs, &ep->result, MPR_SHALLOW_COPY);
821 if ((tid = ejsLexGetToken(ep, state)) == EJS_TOK_EXPR ||
822 tid == EJS_TOK_INC_DEC || tid == EJS_TOK_LOGICAL) {
823 rel = (int) *ep->token;
826 ejsLexPutbackToken(ep, tid, ep->token);
827 state = EJS_STATE_RELEXP_DONE;
830 } while (state == EJS_STATE_EXPR_DONE);
838 /******************************************************************************/
840 * Parse the "for ... in" statement. Format for the statement is:
842 * for (var in expr) {
847 static int parseForIn(Ejs *ep, int state, int flags)
849 EjsInput endScript, bodyScript;
850 MprVar *iteratorVar, *setVar, *vp, v;
855 tid = ejsLexGetToken(ep, state);
856 if (tid != EJS_TOK_ID) {
859 ejsLexPutbackToken(ep, tid, ep->token);
861 if (ejsParse(ep, EJS_STATE_EXPR, EJS_FLAGS_FOREACH | EJS_FLAGS_EXE)
862 != EJS_STATE_EXPR_DONE) {
865 if (ep->currentProperty == 0) {
868 iteratorVar = ep->currentProperty;
870 if (ejsLexGetToken(ep, state) != EJS_TOK_IN) {
877 tid = ejsLexGetToken(ep, state);
878 if (tid != EJS_TOK_ID) {
881 ejsLexPutbackToken(ep, tid, ep->token);
883 if (ejsParse(ep, EJS_STATE_EXPR, flags) != EJS_STATE_EXPR_DONE) {
886 if (ep->currentProperty == 0 && flags & EJS_FLAGS_EXE) {
889 setVar = ep->currentProperty;
891 if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) {
896 * Parse the body and remember the end of the body script
898 forFlags = flags & ~EJS_FLAGS_EXE;
899 ejsLexSaveInputState(ep, &bodyScript);
900 if (ejsParse(ep, EJS_STATE_STMT, forFlags) != EJS_STATE_STMT_DONE) {
901 ejsLexFreeInputState(ep, &bodyScript);
904 ejsInitInputState(&endScript);
905 ejsLexSaveInputState(ep, &endScript);
908 * Now actually do the for loop.
910 if (flags & EJS_FLAGS_EXE) {
911 if (setVar->type == MPR_TYPE_OBJECT) {
912 vp = mprGetFirstProperty(setVar, MPR_ENUM_DATA);
914 if (strcmp(vp->name, "length") != 0) {
915 v = mprCreateStringVar(vp->name, 0);
916 if (mprWriteProperty(iteratorVar, &v) < 0) {
917 ejsError(ep, "Can't write to variable\n");
918 ejsLexFreeInputState(ep, &bodyScript);
919 ejsLexFreeInputState(ep, &endScript);
923 ejsLexRestoreInputState(ep, &bodyScript);
924 switch (ejsParse(ep, EJS_STATE_STMT, flags)) {
926 return EJS_STATE_RET;
927 case EJS_STATE_STMT_DONE:
930 ejsLexFreeInputState(ep, &endScript);
931 ejsLexFreeInputState(ep, &bodyScript);
935 vp = mprGetNextProperty(setVar, vp, MPR_ENUM_DATA);
938 ejsError(ep, "Variable \"%s\" is not an array or object",
940 ejsLexFreeInputState(ep, &endScript);
941 ejsLexFreeInputState(ep, &bodyScript);
945 ejsLexRestoreInputState(ep, &endScript);
947 ejsLexFreeInputState(ep, &endScript);
948 ejsLexFreeInputState(ep, &bodyScript);
953 /******************************************************************************/
955 * Parse the for statement. Format for the expression is:
957 * for (initial; condition; incr) {
962 static int parseFor(Ejs *ep, int state, int flags)
964 EjsInput condScript, endScript, bodyScript, incrScript;
967 ejsInitInputState(&endScript);
968 ejsInitInputState(&bodyScript);
969 ejsInitInputState(&incrScript);
970 ejsInitInputState(&condScript);
975 * Evaluate the for loop initialization statement
977 if (ejsParse(ep, EJS_STATE_EXPR, flags) != EJS_STATE_EXPR_DONE) {
980 if (ejsLexGetToken(ep, state) != EJS_TOK_SEMI) {
985 * The first time through, we save the current input context just prior
986 * to each step: prior to the conditional, the loop increment and
989 ejsLexSaveInputState(ep, &condScript);
990 if (ejsParse(ep, EJS_STATE_COND, flags) != EJS_STATE_COND_DONE) {
993 cond = (ep->result.boolean != 0);
995 if (ejsLexGetToken(ep, state) != EJS_TOK_SEMI) {
1000 * Don't execute the loop increment statement or the body
1003 forFlags = flags & ~EJS_FLAGS_EXE;
1004 ejsLexSaveInputState(ep, &incrScript);
1005 if (ejsParse(ep, EJS_STATE_EXPR, forFlags) != EJS_STATE_EXPR_DONE) {
1008 if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) {
1013 * Parse the body and remember the end of the body script
1015 ejsLexSaveInputState(ep, &bodyScript);
1016 if (ejsParse(ep, EJS_STATE_STMT, forFlags) != EJS_STATE_STMT_DONE) {
1019 ejsLexSaveInputState(ep, &endScript);
1022 * Now actually do the for loop. Note loop has been rotated
1024 while (cond && (flags & EJS_FLAGS_EXE)) {
1028 ejsLexRestoreInputState(ep, &bodyScript);
1030 switch (ejsParse(ep, EJS_STATE_STMT, flags)) {
1032 return EJS_STATE_RET;
1033 case EJS_STATE_STMT_DONE:
1039 * Evaluate the increment script
1041 ejsLexRestoreInputState(ep, &incrScript);
1042 if (ejsParse(ep, EJS_STATE_EXPR, flags) != EJS_STATE_EXPR_DONE){
1046 * Evaluate the condition
1048 ejsLexRestoreInputState(ep, &condScript);
1049 if (ejsParse(ep, EJS_STATE_COND, flags) != EJS_STATE_COND_DONE) {
1052 mprAssert(ep->result.type == MPR_TYPE_BOOL);
1053 cond = (ep->result.boolean != 0);
1056 ejsLexRestoreInputState(ep, &endScript);
1059 ejsLexFreeInputState(ep, &condScript);
1060 ejsLexFreeInputState(ep, &incrScript);
1061 ejsLexFreeInputState(ep, &endScript);
1062 ejsLexFreeInputState(ep, &bodyScript);
1066 state = EJS_STATE_ERR;
1070 /******************************************************************************/
1072 * Parse a function declaration
1075 static int parseFunctionDec(Ejs *ep, int state, int flags)
1077 EjsInput endScript, bodyScript;
1078 MprVar v, *currentObj, *vp;
1080 int len, tid, bodyFlags;
1083 mprAssert(ejsPtr(ep->eid));
1086 * function <name>(arg, arg, arg) { body };
1087 * function name(arg, arg, arg) { body };
1090 tid = ejsLexGetToken(ep, state);
1091 if (tid == EJS_TOK_ID) {
1092 procName = mprStrdup(ep->token);
1093 tid = ejsLexGetToken(ep, state);
1097 if (tid != EJS_TOK_LPAREN) {
1099 return EJS_STATE_ERR;
1103 * Hand craft the function value structure.
1105 v = mprCreateFunctionVar(0, 0, 0);
1106 tid = ejsLexGetToken(ep, state);
1107 while (tid == EJS_TOK_ID) {
1108 mprAddToArray(v.function.args, mprStrdup(ep->token));
1109 tid = ejsLexGetToken(ep, state);
1110 if (tid == EJS_TOK_RPAREN || tid != EJS_TOK_COMMA) {
1113 tid = ejsLexGetToken(ep, state);
1115 if (tid != EJS_TOK_RPAREN) {
1118 return EJS_STATE_ERR;
1121 /* Allow new lines before opening brace */
1123 tid = ejsLexGetToken(ep, state);
1124 } while (tid == EJS_TOK_NEWLINE);
1126 if (tid != EJS_TOK_LBRACE) {
1129 return EJS_STATE_ERR;
1133 * Register the function name early to allow for recursive
1134 * function calls (see note in ECMA standard, page 71)
1136 if (!(flags & EJS_FLAGS_ASSIGNMENT)) {
1137 currentObj = ejsFindObj(ep, 0, procName, flags);
1138 vp = mprSetProperty(currentObj, procName, &v);
1142 * Parse the function body. Turn execute off.
1144 bodyFlags = flags & ~EJS_FLAGS_EXE;
1145 ejsLexSaveInputState(ep, &bodyScript);
1148 state = ejsParse(ep, EJS_STATE_STMT, bodyFlags);
1149 } while (state == EJS_STATE_STMT_DONE);
1151 tid = ejsLexGetToken(ep, state);
1152 if (state != EJS_STATE_STMT_BLOCK_DONE || tid != EJS_TOK_RBRACE) {
1155 ejsLexFreeInputState(ep, &bodyScript);
1156 return EJS_STATE_ERR;
1158 ejsLexSaveInputState(ep, &endScript);
1161 * Save the function body between the starting and ending parse positions.
1162 * Overwrite the trailing '}' with a null.
1164 len = endScript.scriptServp - bodyScript.scriptServp;
1165 v.function.body = mprMalloc(len + 1);
1166 memcpy(v.function.body, bodyScript.scriptServp, len);
1169 v.function.body[0] = '\0';
1171 v.function.body[len - 1] = '\0';
1173 ejsLexFreeInputState(ep, &bodyScript);
1174 ejsLexFreeInputState(ep, &endScript);
1177 * If we are in an assignment, don't register the function name, rather
1178 * return the function structure in the parser result.
1180 if (flags & EJS_FLAGS_ASSIGNMENT) {
1181 mprCopyVar(&ep->result, &v, MPR_SHALLOW_COPY);
1183 currentObj = ejsFindObj(ep, 0, procName, flags);
1184 vp = mprSetProperty(currentObj, procName, &v);
1190 return EJS_STATE_STMT;
1193 /******************************************************************************/
1195 * Parse a function name and invoke the function
1198 static int parseFunction(Ejs *ep, int state, int flags, char *id)
1200 EjsProc proc, *saveProc;
1204 * Must save any current ep->proc value for the current stack frame
1205 * to allow for recursive function calls.
1207 saveProc = (ep->proc) ? ep->proc: 0;
1209 memset(&proc, 0, sizeof(EjsProc));
1210 proc.procName = mprStrdup(id);
1211 proc.fn = ep->currentProperty;
1212 proc.args = mprCreateArray();
1215 mprDestroyVar(&ep->result);
1217 saveObj = ep->currentObj;
1218 if (ejsParse(ep, EJS_STATE_ARG_LIST, flags) != EJS_STATE_ARG_LIST_DONE) {
1220 ep->proc = saveProc;
1223 ep->currentObj = saveObj;
1226 * Evaluate the function if required
1228 if (flags & EJS_FLAGS_EXE) {
1229 if (evalFunction(ep, ep->currentObj, flags) < 0) {
1231 ep->proc = saveProc;
1237 ep->proc = saveProc;
1239 if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) {
1245 /******************************************************************************/
1247 * Parse an identifier. This is a segment of a fully qualified variable.
1248 * May come here for an initial identifier or for property names
1249 * after a "." or "[...]".
1252 static int parseId(Ejs *ep, int state, int flags, char **id, char **fullName,
1253 int *fullNameLen, int *done)
1258 *id = mprStrdup(ep->token);
1260 *fullNameLen = mprReallocStrcat(fullName, MPR_MAX_VAR, *fullNameLen,
1263 if (ep->currentObj == 0) {
1264 ep->currentObj = ejsFindObj(ep, state, *id, flags);
1268 * Find the referenced variable and store it in currentProperty.
1270 ep->currentProperty = ejsFindProperty(ep, state, ep->currentObj,
1272 updateResult(ep, state, flags, ep->currentProperty);
1275 if (ep->currentProperty && (ep->currentProperty->name == 0 ||
1276 ep->currentProperty->name[0] == '\0')) {
1277 mprSetVarName(ep->currentProperty, *id);
1281 tid = ejsLexGetToken(ep, state);
1282 if (tid == EJS_TOK_LPAREN) {
1283 ejsLexPutbackToken(ep, EJS_TOK_FUNCTION_NAME, ep->token);
1287 if (tid == EJS_TOK_PERIOD || tid == EJS_TOK_LBRACKET ||
1288 tid == EJS_TOK_ASSIGNMENT || tid == EJS_TOK_INC_DEC) {
1289 ejsLexPutbackToken(ep, tid, ep->token);
1294 * Only come here for variable access and declarations.
1295 * Assignment handled elsewhere.
1297 if (flags & EJS_FLAGS_EXE) {
1298 if (state == EJS_STATE_DEC) {
1300 * Declare a variable. Standard allows: var x ; var x ;
1303 if (ep->currentProperty != 0) {
1304 ejsError(ep, "Variable already defined \"%s\"\n", *id);
1309 * Create or overwrite if it already exists
1311 mprSetPropertyValue(ep->currentObj, *id,
1312 mprCreateUndefinedVar());
1313 ep->currentProperty = 0;
1314 mprDestroyVar(&ep->result);
1316 } else if (flags & EJS_FLAGS_FOREACH) {
1317 if (ep->currentProperty == 0) {
1318 ep->currentProperty =
1319 mprCreatePropertyValue(ep->currentObj, *id,
1320 mprCreateUndefinedVar());
1324 if (ep->currentProperty == 0) {
1325 if (ep->currentObj == ep->global ||
1326 ep->currentObj == ep->local) {
1327 ejsError(ep, "Undefined variable \"%s\"\n", *id);
1330 ep->currentProperty = mprCreatePropertyValue(ep->currentObj,
1331 *id, mprCreateUndefinedVar());
1335 ejsLexPutbackToken(ep, tid, ep->token);
1336 if (tid == EJS_TOK_RBRACKET || tid == EJS_TOK_COMMA ||
1337 tid == EJS_TOK_IN) {
1343 /******************************************************************************/
1345 * Parse an "if" statement
1348 static int parseIf(Ejs *ep, int state, int flags, int *done)
1351 int thenFlags, elseFlags, tid;
1353 if (state != EJS_STATE_STMT) {
1356 if (ejsLexGetToken(ep, state) != EJS_TOK_LPAREN) {
1361 * Evaluate the entire condition list "(condition)"
1363 if (ejsParse(ep, EJS_STATE_COND, flags) != EJS_STATE_COND_DONE) {
1366 if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) {
1371 * This is the "then" case. We need to always parse both cases and
1372 * execute only the relevant case.
1374 ifResult = mprVarToBool(&ep->result);
1377 elseFlags = flags & ~EJS_FLAGS_EXE;
1379 thenFlags = flags & ~EJS_FLAGS_EXE;
1384 * Process the "then" case.
1386 switch (ejsParse(ep, EJS_STATE_STMT, thenFlags)) {
1388 state = EJS_STATE_RET;
1390 case EJS_STATE_STMT_DONE:
1397 * Check to see if there is an "else" case
1399 removeNewlines(ep, state);
1400 tid = ejsLexGetToken(ep, state);
1401 if (tid != EJS_TOK_ELSE) {
1402 ejsLexPutbackToken(ep, tid, ep->token);
1408 * Process the "else" case.
1410 switch (ejsParse(ep, EJS_STATE_STMT, elseFlags)) {
1412 state = EJS_STATE_RET;
1414 case EJS_STATE_STMT_DONE:
1423 /******************************************************************************/
1425 * Parse an "++" or "--" statement
1428 static int parseInc(Ejs *ep, int state, int flags)
1432 if (! (flags & EJS_FLAGS_EXE)) {
1436 if (ep->currentProperty == 0) {
1437 ejsError(ep, "Undefined variable \"%s\"\n", ep->token);
1440 one = mprCreateIntegerVar(1);
1441 if (evalExpr(ep, ep->currentProperty, (int) *ep->token,
1445 if (mprWriteProperty(ep->currentProperty, &ep->result) < 0) {
1446 ejsError(ep, "Can't write to variable\n");
1452 /******************************************************************************/
1454 * Evaluate a condition. Implements &&, ||, !. Returns with a boolean result
1455 * in ep->result. Returns -1 on errors, zero if successful.
1458 static int evalCond(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs)
1464 l = mprVarToBool(lhs);
1465 r = mprVarToBool(rhs);
1475 ejsError(ep, "Bad operator %d", rel);
1479 mprCopyVarValue(&ep->result, mprCreateBoolVar(lval), 0);
1483 /******************************************************************************/
1485 * Evaluate an operation. Returns with the result in ep->result. Returns -1
1486 * on errors, otherwise zero is returned.
1489 static int evalExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs)
1500 * Type conversion. This is tricky and must be according to the standard.
1501 * Only numbers (including floats) and strings can be compared. All other
1502 * types are first converted to numbers by preference and if that fails,
1505 * First convert objects to comparable types. The "===" operator will
1506 * test the sameness of object references. Here, we coerce to comparable
1509 if (lhs->type == MPR_TYPE_OBJECT) {
1510 if (ejsRunFunction(ep->eid, lhs, "toValue", 0) == 0) {
1511 mprCopyVar(lhs, &ep->result, MPR_SHALLOW_COPY);
1513 if (ejsRunFunction(ep->eid, lhs, "toString", 0) == 0) {
1514 mprCopyVar(lhs, &ep->result, MPR_SHALLOW_COPY);
1517 /* Nothing more can be done */
1520 if (rhs->type == MPR_TYPE_OBJECT) {
1521 if (ejsRunFunction(ep->eid, rhs, "toValue", 0) == 0) {
1522 mprCopyVar(rhs, &ep->result, MPR_SHALLOW_COPY);
1524 if (ejsRunFunction(ep->eid, rhs, "toString", 0) == 0) {
1525 mprCopyVar(rhs, &ep->result, MPR_SHALLOW_COPY);
1528 /* Nothing more can be done */
1532 * From here on, lhs and rhs may contain allocated data (strings), so
1533 * we must always destroy before overwriting.
1537 * Only allow a few bool operations. Otherwise convert to number.
1539 if (lhs->type == MPR_TYPE_BOOL && rhs->type == MPR_TYPE_BOOL &&
1540 (rel != EJS_EXPR_EQ && rel != EJS_EXPR_NOTEQ &&
1541 rel != EJS_EXPR_BOOL_COMP)) {
1542 num = mprVarToNumber(lhs);
1544 *lhs = mprCreateNumberVar(num);
1548 * Types do not match, so try to coerce the right operand to match the left
1549 * But first, try to convert a left operand that is a numeric stored as a
1550 * string, into a numeric.
1552 if (lhs->type != rhs->type) {
1553 if (lhs->type == MPR_TYPE_STRING) {
1554 if (isdigit((int) lhs->string[0])) {
1555 num = mprVarToNumber(lhs);
1556 lhs->allocatedVar = 0;
1558 *lhs = mprCreateNumberVar(num);
1559 /* Examine further below */
1563 * Convert the RHS to a string
1565 mprVarToString(&str, MPR_MAX_STRING, 0, rhs);
1566 rhs->allocatedVar = 0;
1568 *rhs = mprCreateStringVar(str, 1);
1572 #if BLD_FEATURE_FLOATING_POINT
1573 } else if (lhs->type == MPR_TYPE_FLOAT) {
1575 * Convert rhs to floating
1577 double f = mprVarToFloat(rhs);
1579 *rhs = mprCreateFloatVar(f);
1582 #if BLD_FEATURE_INT64
1583 } else if (lhs->type == MPR_TYPE_INT64) {
1585 * Convert the rhs to 64 bit
1587 int64 n = mprVarToInteger64(rhs);
1589 *rhs = mprCreateInteger64Var(n);
1591 } else if (lhs->type == MPR_TYPE_BOOL || lhs->type == MPR_TYPE_INT) {
1593 if (rhs->type == MPR_TYPE_STRING) {
1595 * Convert to lhs to a string
1597 mprVarToString(&str, MPR_MAX_STRING, 0, lhs);
1599 *lhs = mprCreateStringVar(str, 1);
1602 #if BLD_FEATURE_FLOATING_POINT
1603 } else if (rhs->type == MPR_TYPE_FLOAT) {
1605 * Convert lhs to floating
1607 double f = mprVarToFloat(lhs);
1609 *lhs = mprCreateFloatVar(f);
1614 * Convert both operands to numbers
1616 num = mprVarToNumber(lhs);
1618 *lhs = mprCreateNumberVar(num);
1620 num = mprVarToNumber(rhs);
1622 *rhs = mprCreateNumberVar(num);
1628 * We have failed to coerce the types to be the same. Special case here
1629 * for undefined and null. We need to allow comparisions against these
1632 if (lhs->type == MPR_TYPE_UNDEFINED || lhs->type == MPR_TYPE_NULL) {
1635 lval = lhs->type == rhs->type;
1637 case EJS_EXPR_NOTEQ:
1638 lval = lhs->type != rhs->type;
1643 mprCopyVarValue(&ep->result, mprCreateBoolVar((bool) lval), 0);
1648 * Types are the same here
1650 switch (lhs->type) {
1652 case MPR_TYPE_UNDEFINED:
1654 /* Should be handled above */
1658 case MPR_TYPE_STRING_CFUNCTION:
1659 case MPR_TYPE_CFUNCTION:
1660 case MPR_TYPE_FUNCTION:
1661 case MPR_TYPE_OBJECT:
1662 mprCopyVarValue(&ep->result, mprCreateBoolVar(0), 0);
1666 mprCopyVarValue(&ep->result, mprCreateBoolVar(lhs->ptr == rhs->ptr), 0);
1670 rc = evalBoolExpr(ep, lhs->boolean, rel, rhs->boolean);
1673 #if BLD_FEATURE_FLOATING_POINT
1674 case MPR_TYPE_FLOAT:
1675 rc = evalFloatExpr(ep, lhs->floating, rel, rhs->floating);
1680 rc = evalNumericExpr(ep, (MprNum) lhs->integer, rel,
1681 (MprNum) rhs->integer);
1684 #if BLD_FEATURE_INT64
1685 case MPR_TYPE_INT64:
1686 rc = evalNumericExpr(ep, (MprNum) lhs->integer64, rel,
1687 (MprNum) rhs->integer64);
1691 case MPR_TYPE_STRING:
1692 rc = evalStringExpr(ep, lhs, rel, rhs);
1697 /******************************************************************************/
1698 #if BLD_FEATURE_FLOATING_POINT
1700 * Expressions with floating operands
1703 static int evalFloatExpr(Ejs *ep, double l, int rel, double r)
1718 case EJS_EXPR_MINUS:
1744 case EJS_EXPR_NOTEQ:
1748 lval = (l < r) ? 1 : 0;
1750 case EJS_EXPR_LESSEQ:
1751 lval = (l <= r) ? 1 : 0;
1753 case EJS_EXPR_GREATER:
1754 lval = (l > r) ? 1 : 0;
1756 case EJS_EXPR_GREATEREQ:
1757 lval = (l >= r) ? 1 : 0;
1759 case EJS_EXPR_BOOL_COMP:
1760 lval = (r == 0) ? 1 : 0;
1763 ejsError(ep, "Bad operator %d", rel);
1766 mprCopyVarValue(&ep->result, mprCreateBoolVar(lval != 0), 0);
1769 mprCopyVarValue(&ep->result, mprCreateFloatVar(lval), 0);
1774 #endif /* BLD_FEATURE_FLOATING_POINT */
1775 /******************************************************************************/
1777 * Expressions with boolean operands
1780 static int evalBoolExpr(Ejs *ep, bool l, int rel, bool r)
1788 case EJS_EXPR_NOTEQ:
1791 case EJS_EXPR_BOOL_COMP:
1792 lval = (r == 0) ? 1 : 0;
1795 ejsError(ep, "Bad operator %d", rel);
1798 mprCopyVarValue(&ep->result, mprCreateBoolVar(lval), 0);
1802 /******************************************************************************/
1804 * Expressions with numeric operands
1807 static int evalNumericExpr(Ejs *ep, MprNum l, int rel, MprNum r)
1822 case EJS_EXPR_MINUS:
1835 ejsError(ep, "Divide by zero");
1843 ejsError(ep, "Modulo zero");
1847 case EJS_EXPR_LSHIFT:
1850 case EJS_EXPR_RSHIFT:
1868 case EJS_EXPR_NOTEQ:
1872 lval = (l < r) ? 1 : 0;
1874 case EJS_EXPR_LESSEQ:
1875 lval = (l <= r) ? 1 : 0;
1877 case EJS_EXPR_GREATER:
1878 lval = (l > r) ? 1 : 0;
1880 case EJS_EXPR_GREATEREQ:
1881 lval = (l >= r) ? 1 : 0;
1883 case EJS_EXPR_BOOL_COMP:
1884 lval = (r == 0) ? 1 : 0;
1887 ejsError(ep, "Bad operator %d", rel);
1890 mprCopyVarValue(&ep->result, mprCreateBoolVar(lval != 0), 0);
1893 mprCopyVarValue(&ep->result, mprCreateNumberVar(lval), 0);
1898 /******************************************************************************/
1900 * Expressions with string operands
1903 static int evalStringExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs)
1913 lval = strcmp(lhs->string, rhs->string) < 0;
1915 case EJS_EXPR_LESSEQ:
1916 lval = strcmp(lhs->string, rhs->string) <= 0;
1918 case EJS_EXPR_GREATER:
1919 lval = strcmp(lhs->string, rhs->string) > 0;
1921 case EJS_EXPR_GREATEREQ:
1922 lval = strcmp(lhs->string, rhs->string) >= 0;
1925 lval = strcmp(lhs->string, rhs->string) == 0;
1927 case EJS_EXPR_NOTEQ:
1928 lval = strcmp(lhs->string, rhs->string) != 0;
1932 * This differs from all the above operations. We append rhs to lhs.
1934 mprDestroyVar(&ep->result);
1935 appendValue(&ep->result, lhs);
1936 appendValue(&ep->result, rhs);
1941 case EJS_EXPR_MINUS:
1944 case EJS_EXPR_LSHIFT:
1945 case EJS_EXPR_RSHIFT:
1947 ejsError(ep, "Bad operator");
1951 mprCopyVarValue(&ep->result, mprCreateBoolVar(lval), 0);
1955 /******************************************************************************/
1957 * Evaluate a function. obj is set to the current object if a function is being
1961 static int evalFunction(Ejs *ep, MprVar *obj, int flags)
1964 MprVar arguments, callee, thisObject, *prototype, **argValues;
1965 MprArray *formalArgs, *actualArgs;
1966 char buf[16], **argNames, **argBuf;
1970 mprAssert(ejsPtr(ep->eid));
1974 prototype = proc->fn;
1975 actualArgs = proc->args;
1976 argValues = (MprVar**) actualArgs->handles;
1978 if (prototype == NULL) {
1979 ejsError(ep, "Function name not defined '%s'\n", proc->procName);
1984 * Create a new variable stack frame. ie. new local variables.
1986 fid = ejsOpenBlock(ep->eid);
1988 if (flags & EJS_FLAGS_NEW) {
1990 * Create a new bare object and pass it into the constructor as the
1991 * "this" local variable.
1993 thisObject = ejsCreateObj("this", EJS_OBJ_HASH_SIZE);
1994 mprCreatePropertyValue(ep->local, "this", thisObject);
1997 mprCreateProperty(ep->local, "this", obj);
2000 switch (prototype->type) {
2005 case MPR_TYPE_STRING_CFUNCTION:
2006 if (actualArgs->used > 0) {
2007 argBuf = mprMalloc(actualArgs->used * sizeof(char*));
2008 for (i = 0; i < actualArgs->used; i++) {
2009 mprVarToString(&argBuf[i], MPR_MAX_STRING, 0, argValues[i]);
2016 * Call the function depending on the various handle flags
2018 ep->thisPtr = prototype->cFunctionWithStrings.thisPtr;
2019 if (prototype->flags & MPR_VAR_ALT_HANDLE) {
2020 rc = ((EjsAltStringCFunction) prototype->cFunctionWithStrings.fn)
2021 (ep->eid, ep->altHandle, actualArgs->used, argBuf);
2022 } else if (prototype->flags & MPR_VAR_SCRIPT_HANDLE) {
2023 rc = (prototype->cFunctionWithStrings.fn)(ep->eid,
2024 actualArgs->used, argBuf);
2026 rc = (prototype->cFunctionWithStrings.fn)(ep->primaryHandle,
2027 actualArgs->used, argBuf);
2030 if (actualArgs->used > 0) {
2031 for (i = 0; i < actualArgs->used; i++) {
2039 case MPR_TYPE_CFUNCTION:
2041 * Call the function depending on the various handle flags
2043 ep->thisPtr = prototype->cFunction.thisPtr;
2044 if (prototype->flags & MPR_VAR_ALT_HANDLE) {
2045 rc = ((EjsAltCFunction) prototype->cFunction.fn)
2046 (ep->eid, ep->altHandle, actualArgs->used, argValues);
2047 } else if (prototype->flags & MPR_VAR_SCRIPT_HANDLE) {
2048 rc = (prototype->cFunction.fn)(ep->eid, actualArgs->used,
2051 rc = (prototype->cFunction.fn)(ep->primaryHandle,
2052 actualArgs->used, argValues);
2057 case MPR_TYPE_FUNCTION:
2059 formalArgs = prototype->function.args;
2060 argNames = (char**) formalArgs->handles;
2063 if (formalArgs->used != actualArgs->used) {
2064 ejsError(ep, "Bad number of args. Should be %d", formalArgs->used);
2070 * Create the arguments and callee variables
2072 arguments = ejsCreateObj("arguments", EJS_SMALL_OBJ_HASH_SIZE);
2073 callee = ejsCreateObj("callee", EJS_SMALL_OBJ_HASH_SIZE);
2076 * Overwrite the length property
2078 mprCreatePropertyValue(&arguments, "length",
2079 mprCreateIntegerVar(actualArgs->used));
2080 mprCreatePropertyValue(&callee, "length",
2081 mprCreateIntegerVar(formalArgs->used));
2084 * Define all the agruments to be set to the actual parameters
2086 for (i = 0; i < formalArgs->used; i++) {
2087 mprCreateProperty(ep->local, argNames[i], argValues[i]);
2089 for (i = 0; i < actualArgs->used; i++) {
2090 mprItoa(i, buf, sizeof(buf));
2091 mprCreateProperty(&arguments, buf, argValues[i]);
2094 mprCreateProperty(&arguments, "callee", &callee);
2095 mprCreateProperty(ep->local, "arguments", &arguments);
2098 * Can destroy our variables here as they are now referenced via
2101 mprDestroyVar(&callee);
2102 mprDestroyVar(&arguments);
2105 * Actually run the function
2107 rc = ejsEvalScript(ep->eid, prototype->function.body, 0, 0);
2111 ejsCloseBlock(ep->eid, fid);
2114 * New statements return the newly created object as the result of the
2117 if (flags & EJS_FLAGS_NEW) {
2118 mprDestroyVar(&ep->result);
2120 * Don't copy, we want to assign the actual object into result.
2121 * (mprCopyVar would inc the refCount to 2).
2123 ep->result = thisObject;
2128 /******************************************************************************/
2133 int ejsRunFunction(int eid, MprVar *obj, const char *functionName,
2136 EjsProc proc, *saveProc;
2141 mprAssert(functionName && *functionName);
2143 if ((ep = ejsPtr(eid)) == NULL) {
2145 return MPR_ERR_NOT_FOUND;
2147 saveProc = ep->proc;
2150 memset(&proc, 0, sizeof(EjsProc));
2151 mprDestroyVar(&ep->result);
2153 proc.fn = mprGetProperty(obj, functionName, 0);
2154 if (proc.fn == 0 || proc.fn->type == MPR_TYPE_UNDEFINED) {
2155 ep->proc = saveProc;
2156 return MPR_ERR_NOT_FOUND;
2158 proc.procName = mprStrdup(functionName);
2160 proc.args = mprCreateArray();
2161 rc = evalFunction(ep, obj, 0);
2164 rc = evalFunction(ep, obj, 0);
2169 ep->proc = saveProc;
2174 /******************************************************************************/
2176 * Find which object contains the property given the current context.
2177 * Only used for top level properties.
2180 MprVar *ejsFindObj(Ejs *ep, int state, const char *property, int flags)
2186 mprAssert(property && *property);
2188 if (flags & EJS_FLAGS_GLOBAL) {
2191 } else if (state == EJS_STATE_DEC || flags & EJS_FLAGS_LOCAL) {
2195 /* First look local, then look global */
2196 vp = mprGetProperty(ep->local, property, 0);
2199 } else if (mprGetProperty(ep->local, property, 0)) {
2208 /******************************************************************************/
2210 * Find an object property given a object and a property name. We
2211 * intelligently look in the local and global namespaces depending on
2212 * our state. If not found in local or global, try base classes for function
2213 * names only. Returns the property or NULL.
2216 MprVar *ejsFindProperty(Ejs *ep, int state, MprVar *obj, char *property,
2222 if (flags & EJS_FLAGS_EXE) {
2223 mprAssert(property && *property);
2230 vp = mprGetProperty(op, property, 0);
2232 if (op != obj && mprVarIsFunction(vp->type)) {
2239 vp = mprGetProperty(obj, property, 0);
2242 if (state == EJS_STATE_DEC) {
2243 vp = mprGetProperty(ep->local, property, 0);
2246 /* Look local first, then global */
2247 vp = mprGetProperty(ep->local, property, 0);
2249 vp = mprGetProperty(ep->global, property, 0);
2256 /******************************************************************************/
2261 static void updateResult(Ejs *ep, int state, int flags, MprVar *vp)
2263 if (flags & EJS_FLAGS_EXE && state != EJS_STATE_DEC) {
2264 mprDestroyVar(&ep->result);
2266 mprCopyProperty(&ep->result, vp, MPR_SHALLOW_COPY);
2271 /******************************************************************************/
2273 * Append to the pointer value
2276 static void appendValue(MprVar *dest, MprVar *src)
2278 char *value, *oldBuf, *buf;
2283 mprVarToString(&value, MPR_MAX_STRING, 0, src);
2285 if (mprVarIsValid(dest)) {
2286 len = strlen(value);
2287 oldBuf = dest->string;
2288 oldLen = strlen(oldBuf);
2289 buf = mprRealloc(oldBuf, (len + oldLen + 1) * sizeof(char));
2291 strcpy(&buf[oldLen], value);
2294 *dest = mprCreateStringVar(value, 1);
2299 /******************************************************************************/
2304 void ejsSetExitStatus(int eid, int status)
2308 if ((ep = ejsPtr(eid)) == NULL) {
2312 ep->exitStatus = status;
2313 ep->flags |= EJS_FLAGS_EXIT;
2316 /******************************************************************************/
2318 * Free an argument list
2321 static void freeProc(EjsProc *proc)
2327 argValues = (MprVar**) proc->args->handles;
2329 for (i = 0; i < proc->args->max; i++) {
2331 mprDestroyVar(argValues[i]);
2332 mprFree(argValues[i]);
2333 mprRemoveFromArray(proc->args, i);
2337 mprDestroyArray(proc->args);
2340 if (proc->procName) {
2341 mprFree(proc->procName);
2342 proc->procName = NULL;
2346 /******************************************************************************/
2348 * This function removes any new lines. Used for else cases, etc.
2351 static void removeNewlines(Ejs *ep, int state)
2356 tid = ejsLexGetToken(ep, state);
2357 } while (tid == EJS_TOK_NEWLINE);
2359 ejsLexPutbackToken(ep, tid, ep->token);
2362 /******************************************************************************/
2365 void ejsParserDummy() {}
2367 /******************************************************************************/
2368 #endif /* BLD_FEATURE_EJS */
2376 * vim600: sw=4 ts=4 fdm=marker
2377 * vim<600: sw=4 ts=4