772ed574c5e401c5d0949d7d212d74ae4c2a64be
[jelmer/samba4-debian.git] / source / lib / ejs / ejsParser.c
1 /*
2  *      @file   ejsParser.c
3  *      @brief  EJS Parser and Execution 
4  */
5 /********************************* Copyright **********************************/
6 /*
7  *      @copy   default.g
8  *      
9  *      Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved.
10  *      Portions Copyright (c) GoAhead Software, 1995-2000. All Rights Reserved.
11  *      
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.
17  *      
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
23  *      
24  *      This program is distributed WITHOUT ANY WARRANTY; without even the 
25  *      implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
26  *      
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 
32  *      
33  *      @end
34  */
35
36 /********************************** Includes **********************************/
37
38 #include        "ejsInternal.h"
39
40 #if BLD_FEATURE_EJS
41
42 /****************************** Forward Declarations **************************/
43
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);
49 #endif 
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, 
57                                         char *fullName);
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);
72
73 /************************************* Code ***********************************/
74 /*
75  *      Recursive descent parser for EJS
76  */
77
78 int ejsParse(Ejs *ep, int state, int flags)
79 {
80         mprAssert(ep);
81
82         switch (state) {
83         /*
84          *      Any statement, function arguments or conditional expressions
85          */
86         case EJS_STATE_STMT:
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;
91                 }
92                 break;
93
94         case EJS_STATE_DEC:
95                 if ((state = parseStmt(ep, state, flags)) != EJS_STATE_DEC_DONE &&
96                         state != EJS_STATE_EOF) {
97                         state = EJS_STATE_ERR;
98                 }
99                 break;
100
101         case EJS_STATE_EXPR:
102                 if ((state = parseStmt(ep, state, flags)) != EJS_STATE_EXPR_DONE &&
103                         state != EJS_STATE_EOF) {
104                         state = EJS_STATE_ERR;
105                 }
106                 break;
107
108         /*
109          *      Variable declaration list
110          */
111         case EJS_STATE_DEC_LIST:
112                 state = parseDeclaration(ep, state, flags);
113                 break;
114
115         /*
116          *      Function argument string
117          */
118         case EJS_STATE_ARG_LIST:
119                 state = parseArgs(ep, state, flags);
120                 break;
121
122         /*
123          *      Logical condition list (relational operations separated by &&, ||)
124          */
125         case EJS_STATE_COND:
126                 state = parseCond(ep, state, flags);
127                 break;
128
129         /*
130          *      Expression list
131          */
132         case EJS_STATE_RELEXP:
133                 state = parseExpr(ep, state, flags);
134                 break;
135         }
136
137         if (state == EJS_STATE_ERR && ep->error == NULL) {
138                 ejsError(ep, "Syntax error");
139         }
140         return state;
141 }
142
143 /******************************************************************************/
144 /*
145  *      Parse any statement including functions and simple relational operations
146  */
147
148 static int parseStmt(Ejs *ep, int state, int flags)
149 {
150         EjsProc         *saveProc;
151         MprVar          *vp, *saveObj;
152         char            *id, *fullName, *initToken;
153         int             done, expectSemi, tid, fullNameLen, rel;
154         int             initId;
155
156         mprAssert(ep);
157
158         expectSemi = 0;
159         saveProc = NULL;
160         id = 0;
161         fullName = 0;
162         fullNameLen = 0;
163
164         ep->currentObj = 0;
165         ep->currentProperty = 0;
166
167         for (done = 0; !done && state != EJS_STATE_ERR; ) {
168                 tid = ejsLexGetToken(ep, state);
169
170                 switch (tid) {
171                 default:
172                         ejsLexPutbackToken(ep, EJS_TOK_EXPR, ep->token);
173                         done++;
174                         break;
175
176                 case EJS_TOK_EXPR:
177                         rel = (int) *ep->token;
178                         if (state == EJS_STATE_EXPR) {
179                                 ejsLexPutbackToken(ep, EJS_TOK_EXPR, ep->token);
180                         }
181                         done++;
182                         break;
183
184                 case EJS_TOK_LOGICAL:
185                         ejsLexPutbackToken(ep, tid, ep->token);
186                         done++;
187                         break;
188
189                 case EJS_TOK_ERR:
190                         state = EJS_STATE_ERR;
191                         done++;
192                         break;
193
194                 case EJS_TOK_EOF:
195                         state = EJS_STATE_EOF;
196                         done++;
197                         break;
198
199                 case EJS_TOK_NEWLINE:
200                         break;
201
202                 case EJS_TOK_SEMI:
203                         /*
204                          *      This case is when we discover no statement and just a lone ';'
205                          */
206                         if (state != EJS_STATE_STMT) {
207                                 ejsLexPutbackToken(ep, tid, ep->token);
208                         }
209                         done++;
210                         break;
211
212                 case EJS_TOK_PERIOD:
213                         if (flags & EJS_FLAGS_EXE) {
214                                 if (ep->currentProperty == 0) {
215                                         ejsError(ep, "Undefined object \"%s\"\n", id);
216                                         goto error;
217                                 }
218                         }
219                         ep->currentObj = ep->currentProperty;
220
221                         if ((tid = ejsLexGetToken(ep, state)) != EJS_TOK_ID) {
222                                 ejsError(ep, "Bad property after '.': %s\n", ep->token);
223                                 goto error;
224                         }
225                         mprFree(id);
226                         id = mprStrdup(ep->token);
227
228                         vp = ejsFindProperty(ep, state, ep->currentObj, id, flags);
229                         updateResult(ep, state, flags, vp);
230
231 #if BLD_DEBUG
232                         fullNameLen = mprReallocStrcat(&fullName, MPR_MAX_VAR, fullNameLen,
233                                 0, ".", 0);
234 #endif
235
236                         ep->currentProperty = vp;
237                         ejsLexPutbackToken(ep, tid, ep->token);
238                         break;
239
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){
244                                 goto error;
245                         }
246                         ep->currentObj = saveObj;
247
248                         mprFree(id);
249                         mprVarToString(&id, MPR_MAX_STRING, 0, &ep->result);
250
251                         if (id[0] == '\0') {
252                                 if (flags & EJS_FLAGS_EXE) {
253                                         ejsError(ep, 
254                                                 "[] expression evaluates to the empty string\n");
255                                         goto error;
256                                 }
257                         } else {
258                                 vp = ejsFindProperty(ep, state, ep->currentObj, id, flags);
259                                 ep->currentProperty = vp;
260                                 updateResult(ep, state, flags, vp);
261                         }
262
263 #if BLD_DEBUG
264                         if (id[0] && strlen(id) < (MPR_MAX_VAR / 2)) {
265                                 /*
266                                  *      If not executing yet, id may not be known
267                                  */
268                                 fullNameLen = mprReallocStrcat(&fullName, MPR_MAX_VAR, 
269                                         fullNameLen, 0, "[", id, "]", 0);
270                         }
271 #endif
272
273                         if ((tid = ejsLexGetToken(ep, state)) != EJS_TOK_RBRACKET) {
274                                 ejsError(ep, "Missing ']'\n");
275                                 goto error;
276                         }
277                         break;
278
279                 case EJS_TOK_ID:
280                         state = parseId(ep, state, flags, &id, &fullName, &fullNameLen, 
281                                 &done);
282                         if (done && state == EJS_STATE_STMT) {
283                                 expectSemi++;
284                         }
285                         break;
286
287                 case EJS_TOK_ASSIGNMENT:
288                         state = parseAssignment(ep, state, flags, id, fullName);
289                         if (state == EJS_STATE_STMT) {
290                                 expectSemi++;
291                                 done++;
292                         }
293                         break;
294
295                 case EJS_TOK_INC_DEC:
296                         state = parseInc(ep, state, flags);
297                         if (state == EJS_STATE_STMT) {
298                                 expectSemi++;
299                         }
300                         break;
301
302                 case EJS_TOK_NEW:
303                         if (ejsParse(ep, EJS_STATE_EXPR, flags | EJS_FLAGS_NEW) 
304                                         != EJS_STATE_EXPR_DONE) {
305                                 goto error;
306                         }
307                         break;
308
309                 case EJS_TOK_DELETE:
310                         if (ejsParse(ep, EJS_STATE_EXPR, 
311                                         flags | EJS_FLAGS_DELETE) != EJS_STATE_EXPR_DONE) {
312                                 goto error;
313                         }
314                         mprDeleteProperty(ep->currentObj, ep->currentProperty->name);
315                         done++;
316                         break;
317
318                 case EJS_TOK_FUNCTION:
319                         state = parseFunctionDec(ep, state, flags);
320                         done++;
321                         break;
322
323                 case EJS_TOK_LITERAL:
324                         /*
325                          *      Set the result to the string literal 
326                          */
327                         mprCopyVarValue(&ep->result, mprCreateStringVar(ep->token, 0), 
328                                 MPR_SHALLOW_COPY);
329                         if (state == EJS_STATE_STMT) {
330                                 expectSemi++;
331                         }
332                         done++;
333                         break;
334
335                 case EJS_TOK_NUMBER:
336                         /*
337                          *      Set the result to the parsed number
338                          */
339                         mprCopyVar(&ep->result, &ep->tokenNumber, 0);
340                         if (state == EJS_STATE_STMT) {
341                                 expectSemi++;
342                         }
343                         done++;
344                         break;
345
346                 case EJS_TOK_FUNCTION_NAME:
347                         state = parseFunction(ep, state, flags, id);
348                         if (state == EJS_STATE_STMT) {
349                                 expectSemi++;
350                         }
351                         if (ep->flags & EJS_FLAGS_EXIT) {
352                                 state = EJS_STATE_RET;
353                         }
354                         done++;
355                         break;
356
357                 case EJS_TOK_IF:
358                         state = parseIf(ep, state, flags, &done);
359                         if (state == EJS_STATE_RET) {
360                                 goto doneParse;
361                         }
362                         break;
363
364                 case EJS_TOK_FOR:
365                         if (state != EJS_STATE_STMT) {
366                                 goto error;
367                         }
368                         if (ejsLexGetToken(ep, state) != EJS_TOK_LPAREN) {
369                                 goto error;
370                         }
371                         /*
372                          *      Need to peek 2-3 tokens ahead and see if this is a 
373                          *              for ([var] x in set) 
374                          *      or
375                          *              for (init ; whileCond; incr)
376                          */
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);
381                         }
382                         initToken = mprStrdup(ep->token);
383
384                         tid = ejsLexGetToken(ep, EJS_STATE_EXPR);
385
386                         ejsLexPutbackToken(ep, tid, ep->token);
387                         ejsLexPutbackToken(ep, initId, initToken);
388                         mprFree(initToken);
389
390                         if (tid == EJS_TOK_IN) {
391                                 if ((state = parseForIn(ep, state, flags)) < 0) {
392                                         goto error;
393                                 }
394                         } else {
395                                 if ((state = parseFor(ep, state, flags)) < 0) {
396                                         goto error;
397                                 }
398                         }
399                         done++;
400                         break;
401
402                 case EJS_TOK_VAR:
403                         if (ejsParse(ep, EJS_STATE_DEC_LIST, flags) 
404                                         != EJS_STATE_DEC_LIST_DONE) {
405                                 goto error;
406                         }
407                         done++;
408                         break;
409
410                 case EJS_TOK_COMMA:
411                         ejsLexPutbackToken(ep, tid, ep->token);
412                         done++;
413                         break;
414
415                 case EJS_TOK_LPAREN:
416                         if (state == EJS_STATE_EXPR) {
417                                 if (ejsParse(ep, EJS_STATE_RELEXP, flags) 
418                                                 != EJS_STATE_RELEXP_DONE) {
419                                         goto error;
420                                 }
421                                 if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) {
422                                         goto error;
423                                 }
424                         }
425                         done++;
426                         break;
427
428                 case EJS_TOK_RPAREN:
429                         ejsLexPutbackToken(ep, tid, ep->token);
430                         done++;
431                         break;
432
433                 case EJS_TOK_LBRACE:
434                         /*
435                          *      This handles any code in braces except "if () {} else {}"
436                          */
437                         if (state != EJS_STATE_STMT) {
438                                 goto error;
439                         }
440
441                         /*
442                          *      Parse will return EJS_STATE_STMT_BLOCK_DONE when the RBRACE 
443                          *      is seen.
444                          */
445                         do {
446                                 state = ejsParse(ep, EJS_STATE_STMT, flags);
447                         } while (state == EJS_STATE_STMT_DONE);
448
449                         if (state != EJS_STATE_RET) {
450                                 if (ejsLexGetToken(ep, state) != EJS_TOK_RBRACE) {
451                                         goto error;
452                                 }
453                                 state = EJS_STATE_STMT_DONE;
454                         }
455                         done++;
456                         break;
457
458                 case EJS_TOK_RBRACE:
459                         if (state == EJS_STATE_STMT) {
460                                 ejsLexPutbackToken(ep, tid, ep->token);
461                                 state = EJS_STATE_STMT_BLOCK_DONE;
462                                 done++;
463                                 break;
464                         }
465                         goto error;
466
467                 case EJS_TOK_RETURN:
468                         if (ejsParse(ep, EJS_STATE_RELEXP, flags) 
469                                         != EJS_STATE_RELEXP_DONE) {
470                                 goto error;
471                         }
472                         if (flags & EJS_FLAGS_EXE) {
473                                 while (ejsLexGetToken(ep, state) != EJS_TOK_EOF) {
474                                         ;
475                                 }
476                                 state = EJS_STATE_RET;
477                                 done++;
478                         }
479                         break;
480                 }
481         }
482
483         if (expectSemi) {
484                 tid = ejsLexGetToken(ep, state);
485                 if (tid != EJS_TOK_SEMI && tid != EJS_TOK_NEWLINE && 
486                                 tid != EJS_TOK_EOF) {
487                         goto error;
488                 }
489
490                 /*
491                  *      Skip newline after semi-colon
492                  */
493                 removeNewlines(ep, state);
494         }
495
496 /*
497  *      Free resources and return the correct status
498  */
499 doneParse:
500         mprFree(id);
501         mprFree(fullName);
502
503         /*
504          *      Advance the state
505          */
506         switch (state) {
507         case EJS_STATE_STMT:
508                 return EJS_STATE_STMT_DONE;
509
510         case EJS_STATE_DEC:
511                 return EJS_STATE_DEC_DONE;
512
513         case EJS_STATE_EXPR:
514                 return EJS_STATE_EXPR_DONE;
515
516         case EJS_STATE_STMT_DONE:
517         case EJS_STATE_STMT_BLOCK_DONE:
518         case EJS_STATE_EOF:
519         case EJS_STATE_RET:
520                 return state;
521
522         default:
523                 return EJS_STATE_ERR;
524         }
525
526 /*
527  *      Common error exit
528  */
529 error:
530         state = EJS_STATE_ERR;
531         goto doneParse;
532 }
533
534 /******************************************************************************/
535 /*
536  *      Parse function arguments
537  */
538
539 static int parseArgs(Ejs *ep, int state, int flags)
540 {
541         int             tid;
542
543         mprAssert(ep);
544
545         do {
546                 /*
547                  *      Peek and see if there are no args
548                  */
549                 tid = ejsLexGetToken(ep, state);
550                 ejsLexPutbackToken(ep, tid, ep->token);
551                 if (tid == EJS_TOK_RPAREN) {
552                         break;
553                 }
554
555                 state = ejsParse(ep, EJS_STATE_RELEXP, flags);
556                 if (state == EJS_STATE_EOF || state == EJS_STATE_ERR) {
557                         return state;
558                 }
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));
564                         }
565                 }
566                 /*
567                  *      Peek at the next token, continue if more args (ie. comma seen)
568                  */
569                 tid = ejsLexGetToken(ep, state);
570                 if (tid != EJS_TOK_COMMA) {
571                         ejsLexPutbackToken(ep, tid, ep->token);
572                 }
573         } while (tid == EJS_TOK_COMMA);
574
575         if (tid != EJS_TOK_RPAREN && state != EJS_STATE_RELEXP_DONE) {
576                 return EJS_STATE_ERR;
577         }
578         return EJS_STATE_ARG_LIST_DONE;
579 }
580
581 /******************************************************************************/
582 /*
583  *      Parse an assignment statement
584  */
585
586 static int parseAssignment(Ejs *ep, int state, int flags, char *id, 
587         char *fullName)
588 {
589         MprVar          *vp, *saveProperty, *saveObj;
590
591         if (id == 0) {
592                 return -1;
593         }
594
595         saveObj = ep->currentObj;
596         saveProperty = ep->currentProperty;
597         if (ejsParse(ep, EJS_STATE_RELEXP, flags | EJS_FLAGS_ASSIGNMENT) 
598                         != EJS_STATE_RELEXP_DONE) {
599                 return -1;
600         }
601         ep->currentObj = saveObj;
602         ep->currentProperty = saveProperty;
603
604         if (! (flags & EJS_FLAGS_EXE)) {
605                 return state;
606         }
607
608         if (ep->currentProperty) {
609                 /*
610                  *      Update the variable. Update the property name if not
611                  *      yet defined.
612                  */
613                 if (ep->currentProperty->name == 0 || 
614                                 ep->currentProperty->name[0] == '\0') {
615                         mprSetVarName(ep->currentProperty, id);
616                 }
617                 if (mprWriteProperty(ep->currentProperty, &ep->result) < 0){
618                         ejsError(ep, "Can't write to variable\n");
619                         return -1;
620                 }
621
622         } else {
623                 /*
624                  *      Create the variable
625                  */
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);
632                                 } else {
633                                         ejsError(ep, "Variable \"%s\" is not an array or object", 
634                                                 ep->currentObj->name);
635                                 }
636                                 return -1;
637                         }
638                         vp = mprCreateProperty(ep->currentObj, id, &ep->result);
639
640                 } else {
641                         /*
642                          *      Standard says: "var x" means declare locally.
643                          *      "x = 2" means declare globally if x is undefined.
644                          */
645                         if (state == EJS_STATE_DEC) {
646                                 vp = mprCreateProperty(ep->local, id, &ep->result);
647                         } else {
648                                 vp = mprCreateProperty(ep->global, id, &ep->result);
649                         }
650                 }
651 #if BLD_DEBUG
652                 mprSetVarFullName(vp, fullName);
653 #endif
654         }
655         return state;
656 }
657
658 /******************************************************************************/
659 /*
660  *      Parse conditional expression (relational ops separated by ||, &&)
661  */
662
663 static int parseCond(Ejs *ep, int state, int flags)
664 {
665         MprVar          lhs, rhs;
666         int                     tid, operator;
667
668         mprAssert(ep);
669
670         mprDestroyVar(&ep->result);
671         rhs = lhs = mprCreateUndefinedVar();
672         operator = 0;
673
674         do {
675                 /*
676                  *      Recurse to handle one side of a conditional. Accumulate the
677                  *      left hand side and the final result in ep->result.
678                  */
679                 state = ejsParse(ep, EJS_STATE_RELEXP, flags);
680                 if (state != EJS_STATE_RELEXP_DONE) {
681                         state = EJS_STATE_ERR;
682                         break;
683                 }
684
685                 if (operator > 0) {
686                         mprCopyVar(&rhs, &ep->result, MPR_SHALLOW_COPY);
687                         if (evalCond(ep, &lhs, operator, &rhs) < 0) {
688                                 state = EJS_STATE_ERR;
689                                 break;
690                         }
691                 }
692                 mprCopyVar(&lhs, &ep->result, MPR_SHALLOW_COPY);
693
694                 tid = ejsLexGetToken(ep, state);
695                 if (tid == EJS_TOK_LOGICAL) {
696                         operator = (int) *ep->token;
697
698                 } else if (tid == EJS_TOK_RPAREN || tid == EJS_TOK_SEMI) {
699                         ejsLexPutbackToken(ep, tid, ep->token);
700                         state = EJS_STATE_COND_DONE;
701                         break;
702
703                 } else {
704                         ejsLexPutbackToken(ep, tid, ep->token);
705                 }
706                 tid = (state == EJS_STATE_RELEXP_DONE);
707
708         } while (state == EJS_STATE_RELEXP_DONE);
709
710         mprDestroyVar(&lhs);
711         mprDestroyVar(&rhs);
712         return state;
713 }
714
715 /******************************************************************************/
716 /*
717  *      Parse variable declaration list. Declarations can be of the following forms:
718  *              var x;
719  *              var x, y, z;
720  *              var x = 1 + 2 / 3, y = 2 + 4;
721  *
722  *      We set the variable to NULL if there is no associated assignment.
723  */
724
725 static int parseDeclaration(Ejs *ep, int state, int flags)
726 {
727         int             tid;
728
729         mprAssert(ep);
730
731         do {
732                 if ((tid = ejsLexGetToken(ep, state)) != EJS_TOK_ID) {
733                         return EJS_STATE_ERR;
734                 }
735                 ejsLexPutbackToken(ep, tid, ep->token);
736
737                 /*
738                  *      Parse the entire assignment or simple identifier declaration
739                  */
740                 if (ejsParse(ep, EJS_STATE_DEC, flags) != EJS_STATE_DEC_DONE) {
741                         return EJS_STATE_ERR;
742                 }
743
744                 /*
745                  *      Peek at the next token, continue if comma seen
746                  */
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;
752                 }
753         } while (tid == EJS_TOK_COMMA);
754
755         if (tid != EJS_TOK_SEMI) {
756                 return EJS_STATE_ERR;
757         }
758         return EJS_STATE_DEC_LIST_DONE;
759 }
760
761 /******************************************************************************/
762 /*
763  *      Parse expression (leftHandSide operator rightHandSide)
764  */
765
766 static int parseExpr(Ejs *ep, int state, int flags)
767 {
768         MprVar          lhs, rhs;
769         int                     rel, tid;
770
771         mprAssert(ep);
772
773         mprDestroyVar(&ep->result);
774         rhs = lhs = mprCreateUndefinedVar();
775         rel = 0;
776         tid = 0;
777
778         do {
779                 /*
780                  *      This loop will handle an entire expression list. We call parse
781                  *      to evalutate each term which returns the result in ep->result.
782                  */
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;
787                                 break;
788                         }
789                 } else {
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;
794                         } else {
795                                 ejsLexPutbackToken(ep, tid, ep->token);
796                         }
797
798                         state = ejsParse(ep, EJS_STATE_EXPR, flags);
799                         if (state != EJS_STATE_EXPR_DONE) {
800                                 state = EJS_STATE_ERR;
801                                 break;
802                         }
803                 }
804
805                 if (rel > 0) {
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;
810                                         break;
811                                 }
812                         } else {
813                                 if (evalExpr(ep, &lhs, rel, &rhs) < 0) {
814                                         state = EJS_STATE_ERR;
815                                         break;
816                                 }
817                         }
818                 }
819                 mprCopyVar(&lhs, &ep->result, MPR_SHALLOW_COPY);
820
821                 if ((tid = ejsLexGetToken(ep, state)) == EJS_TOK_EXPR ||
822                          tid == EJS_TOK_INC_DEC || tid == EJS_TOK_LOGICAL) {
823                         rel = (int) *ep->token;
824
825                 } else {
826                         ejsLexPutbackToken(ep, tid, ep->token);
827                         state = EJS_STATE_RELEXP_DONE;
828                 }
829
830         } while (state == EJS_STATE_EXPR_DONE);
831
832         mprDestroyVar(&lhs);
833         mprDestroyVar(&rhs);
834
835         return state;
836 }
837
838 /******************************************************************************/
839 /*
840  *      Parse the "for ... in" statement. Format for the statement is:
841  *
842  *              for (var in expr) {
843  *                      body;
844  *              }
845  */
846
847 static int parseForIn(Ejs *ep, int state, int flags)
848 {
849         EjsInput        endScript, bodyScript;
850         MprVar          *iteratorVar, *setVar, *vp, v;
851         int                     forFlags, tid;
852
853         mprAssert(ep);
854
855         tid = ejsLexGetToken(ep, state);
856         if (tid != EJS_TOK_ID) {
857                 return -1;
858         }
859         ejsLexPutbackToken(ep, tid, ep->token);
860
861         if (ejsParse(ep, EJS_STATE_EXPR, EJS_FLAGS_FOREACH | EJS_FLAGS_EXE)
862                         != EJS_STATE_EXPR_DONE) {
863                 return -1;
864         }
865         if (ep->currentProperty == 0) {
866                 return -1;
867         }
868         iteratorVar = ep->currentProperty;
869         
870         if (ejsLexGetToken(ep, state) != EJS_TOK_IN) {
871                 return -1;
872         }
873
874         /*
875          *      Get the set
876          */
877         tid = ejsLexGetToken(ep, state);
878         if (tid != EJS_TOK_ID) {
879                 return -1;
880         }
881         ejsLexPutbackToken(ep, tid, ep->token);
882
883         if (ejsParse(ep, EJS_STATE_EXPR, flags) != EJS_STATE_EXPR_DONE) {
884                 return -1;
885         }
886         if (ep->currentProperty == 0 && flags & EJS_FLAGS_EXE) {
887                 return -1;
888         }
889         setVar = ep->currentProperty;
890         
891         if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) {
892                 return -1;
893         }
894
895         /*
896          *      Parse the body and remember the end of the body script
897          */
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);
902                 return -1;
903         }
904         ejsInitInputState(&endScript);
905         ejsLexSaveInputState(ep, &endScript);
906
907         /*
908          *      Now actually do the for loop.
909          */
910         if (flags & EJS_FLAGS_EXE) {
911                 if (setVar->type == MPR_TYPE_OBJECT) {
912                         vp = mprGetFirstProperty(setVar, MPR_ENUM_DATA);
913                         while (vp) {
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);
920                                                 return -1;
921                                         }
922
923                                         ejsLexRestoreInputState(ep, &bodyScript);
924                                         switch (ejsParse(ep, EJS_STATE_STMT, flags)) {
925                                         case EJS_STATE_RET:
926                                                 return EJS_STATE_RET;
927                                         case EJS_STATE_STMT_DONE:
928                                                 break;
929                                         default:
930                                                 ejsLexFreeInputState(ep, &endScript);
931                                                 ejsLexFreeInputState(ep, &bodyScript);
932                                                 return -1;
933                                         }
934                                 }
935                                 vp = mprGetNextProperty(setVar, vp, MPR_ENUM_DATA);
936                         }
937                 } else {
938                         ejsError(ep, "Variable \"%s\" is not an array or object", 
939                                 setVar->name);
940                         ejsLexFreeInputState(ep, &endScript);
941                         ejsLexFreeInputState(ep, &bodyScript);
942                         return -1;
943                 }
944         }
945         ejsLexRestoreInputState(ep, &endScript);
946
947         ejsLexFreeInputState(ep, &endScript);
948         ejsLexFreeInputState(ep, &bodyScript);
949
950         return state;
951 }
952
953 /******************************************************************************/
954 /*
955  *      Parse the for statement. Format for the expression is:
956  *
957  *              for (initial; condition; incr) {
958  *                      body;
959  *              }
960  */
961
962 static int parseFor(Ejs *ep, int state, int flags)
963 {
964         EjsInput        condScript, endScript, bodyScript, incrScript;
965         int                     forFlags, cond;
966
967         ejsInitInputState(&endScript);
968         ejsInitInputState(&bodyScript);
969         ejsInitInputState(&incrScript);
970         ejsInitInputState(&condScript);
971
972         mprAssert(ep);
973
974         /*
975          *      Evaluate the for loop initialization statement
976          */
977         if (ejsParse(ep, EJS_STATE_EXPR, flags) != EJS_STATE_EXPR_DONE) {
978                 return -1;
979         }
980         if (ejsLexGetToken(ep, state) != EJS_TOK_SEMI) {
981                 return -1;
982         }
983
984         /*
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 
987          *      the loop body.
988          */
989         ejsLexSaveInputState(ep, &condScript);
990         if (ejsParse(ep, EJS_STATE_COND, flags) != EJS_STATE_COND_DONE) {
991                 goto error;
992         }
993         cond = (ep->result.boolean != 0);
994
995         if (ejsLexGetToken(ep, state) != EJS_TOK_SEMI) {
996                 goto error;
997         }
998
999         /*
1000          *      Don't execute the loop increment statement or the body 
1001          *      first time.
1002          */
1003         forFlags = flags & ~EJS_FLAGS_EXE;
1004         ejsLexSaveInputState(ep, &incrScript);
1005         if (ejsParse(ep, EJS_STATE_EXPR, forFlags) != EJS_STATE_EXPR_DONE) {
1006                 goto error;
1007         }
1008         if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) {
1009                 goto error;
1010         }
1011
1012         /*
1013          *      Parse the body and remember the end of the body script
1014          */
1015         ejsLexSaveInputState(ep, &bodyScript);
1016         if (ejsParse(ep, EJS_STATE_STMT, forFlags) != EJS_STATE_STMT_DONE) {
1017                 goto error;
1018         }
1019         ejsLexSaveInputState(ep, &endScript);
1020
1021         /*
1022          *      Now actually do the for loop. Note loop has been rotated
1023          */
1024         while (cond && (flags & EJS_FLAGS_EXE)) {
1025                 /*
1026                  *      Evaluate the body
1027                  */
1028                 ejsLexRestoreInputState(ep, &bodyScript);
1029
1030                 switch (ejsParse(ep, EJS_STATE_STMT, flags)) {
1031                 case EJS_STATE_RET:
1032                         return EJS_STATE_RET;
1033                 case EJS_STATE_STMT_DONE:
1034                         break;
1035                 default:
1036                         goto error;
1037                 }
1038                 /*
1039                  *      Evaluate the increment script
1040                  */
1041                 ejsLexRestoreInputState(ep, &incrScript);
1042                 if (ejsParse(ep, EJS_STATE_EXPR, flags) != EJS_STATE_EXPR_DONE){
1043                         goto error;
1044                 }
1045                 /*
1046                  *      Evaluate the condition
1047                  */
1048                 ejsLexRestoreInputState(ep, &condScript);
1049                 if (ejsParse(ep, EJS_STATE_COND, flags) != EJS_STATE_COND_DONE) {
1050                         goto error;
1051                 }
1052                 mprAssert(ep->result.type == MPR_TYPE_BOOL);
1053                 cond = (ep->result.boolean != 0);
1054         }
1055
1056         ejsLexRestoreInputState(ep, &endScript);
1057
1058 done:
1059         ejsLexFreeInputState(ep, &condScript);
1060         ejsLexFreeInputState(ep, &incrScript);
1061         ejsLexFreeInputState(ep, &endScript);
1062         ejsLexFreeInputState(ep, &bodyScript);
1063         return state;
1064
1065 error:
1066         state = EJS_STATE_ERR;
1067         goto done;
1068 }
1069
1070 /******************************************************************************/
1071 /*
1072  *      Parse a function declaration
1073  */
1074
1075 static int parseFunctionDec(Ejs *ep, int state, int flags)
1076 {
1077         EjsInput        endScript, bodyScript;
1078         MprVar          v, *currentObj, *vp;
1079         char            *procName;
1080         int                     len, tid, bodyFlags;
1081
1082         mprAssert(ep);
1083         mprAssert(ejsPtr(ep->eid));
1084
1085         /*      
1086          *      function <name>(arg, arg, arg) { body };
1087          *      function name(arg, arg, arg) { body };
1088          */
1089
1090         tid = ejsLexGetToken(ep, state);
1091         if (tid == EJS_TOK_ID) {
1092                 procName = mprStrdup(ep->token);
1093                 tid = ejsLexGetToken(ep, state);
1094         }  else {
1095                 procName = 0;
1096         }
1097         if (tid != EJS_TOK_LPAREN) {
1098                 mprFree(procName);
1099                 return EJS_STATE_ERR;
1100         }
1101
1102         /*
1103          *      Hand craft the function value structure.
1104          */
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) {
1111                         break;
1112                 }
1113                 tid = ejsLexGetToken(ep, state);
1114         }
1115         if (tid != EJS_TOK_RPAREN) {
1116                 mprFree(procName);
1117                 mprDestroyVar(&v);
1118                 return EJS_STATE_ERR;
1119         }
1120
1121         /* Allow new lines before opening brace */
1122         do {
1123                 tid = ejsLexGetToken(ep, state);
1124         } while (tid == EJS_TOK_NEWLINE);
1125
1126         if (tid != EJS_TOK_LBRACE) {
1127                 mprFree(procName);
1128                 mprDestroyVar(&v);
1129                 return EJS_STATE_ERR;
1130         }
1131         
1132         /* 
1133          *      Register the function name early to allow for recursive
1134          *      function calls (see note in ECMA standard, page 71) 
1135          */
1136         if (!(flags & EJS_FLAGS_ASSIGNMENT)) {
1137                 currentObj = ejsFindObj(ep, 0, procName, flags);
1138                 vp = mprSetProperty(currentObj, procName, &v);
1139         }
1140         
1141         /*
1142          *      Parse the function body. Turn execute off.
1143          */
1144         bodyFlags = flags & ~EJS_FLAGS_EXE;
1145         ejsLexSaveInputState(ep, &bodyScript);
1146
1147         do {
1148                 state = ejsParse(ep, EJS_STATE_STMT, bodyFlags);
1149         } while (state == EJS_STATE_STMT_DONE);
1150
1151         tid = ejsLexGetToken(ep, state);
1152         if (state != EJS_STATE_STMT_BLOCK_DONE || tid != EJS_TOK_RBRACE) {
1153                 mprFree(procName);
1154                 mprDestroyVar(&v);
1155                 ejsLexFreeInputState(ep, &bodyScript);
1156                 return EJS_STATE_ERR;
1157         }
1158         ejsLexSaveInputState(ep, &endScript);
1159
1160         /*
1161          *      Save the function body between the starting and ending parse positions.
1162          *      Overwrite the trailing '}' with a null.
1163          */
1164         len = endScript.scriptServp - bodyScript.scriptServp;
1165         v.function.body = mprMalloc(len + 1);
1166         memcpy(v.function.body, bodyScript.scriptServp, len);
1167
1168         if (len <= 0) {
1169                 v.function.body[0] = '\0';
1170         } else {
1171                 v.function.body[len - 1] = '\0';
1172         }
1173         ejsLexFreeInputState(ep, &bodyScript);
1174         ejsLexFreeInputState(ep, &endScript);
1175
1176         /*
1177          *      If we are in an assignment, don't register the function name, rather
1178          *      return the function structure in the parser result.
1179          */
1180         if (flags & EJS_FLAGS_ASSIGNMENT) {
1181                 mprCopyVar(&ep->result, &v, MPR_SHALLOW_COPY);
1182         } else {
1183                 currentObj = ejsFindObj(ep, 0, procName, flags);
1184                 vp = mprSetProperty(currentObj, procName, &v);
1185         }
1186
1187         mprFree(procName);
1188         mprDestroyVar(&v);
1189
1190         return EJS_STATE_STMT;
1191 }
1192
1193 /******************************************************************************/
1194 /*
1195  *      Parse a function name and invoke the function
1196  */
1197
1198 static int parseFunction(Ejs *ep, int state, int flags, char *id)
1199 {
1200         EjsProc         proc, *saveProc;
1201         MprVar          *saveObj;
1202
1203         /*
1204          *      Must save any current ep->proc value for the current stack frame
1205          *      to allow for recursive function calls.
1206          */
1207         saveProc = (ep->proc) ? ep->proc: 0;
1208
1209         memset(&proc, 0, sizeof(EjsProc));
1210         proc.procName = mprStrdup(id);
1211         proc.fn = ep->currentProperty;
1212         proc.args = mprCreateArray();
1213         ep->proc = &proc;
1214
1215         mprDestroyVar(&ep->result);
1216
1217         saveObj = ep->currentObj;
1218         if (ejsParse(ep, EJS_STATE_ARG_LIST, flags) != EJS_STATE_ARG_LIST_DONE) {
1219                 freeProc(&proc);
1220                 ep->proc = saveProc;
1221                 return -1;
1222         }
1223         ep->currentObj = saveObj;
1224
1225         /*
1226          *      Evaluate the function if required
1227          */
1228         if (flags & EJS_FLAGS_EXE) {
1229                 if (evalFunction(ep, ep->currentObj, flags) < 0) {
1230                         freeProc(&proc);
1231                         ep->proc = saveProc;
1232                         return -1;
1233                 }
1234         }
1235
1236         freeProc(&proc);
1237         ep->proc = saveProc;
1238
1239         if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) {
1240                 return -1;
1241         }
1242         return state;
1243 }
1244
1245 /******************************************************************************/
1246 /*
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 "[...]".
1250  */
1251
1252 static int parseId(Ejs *ep, int state, int flags, char **id, char **fullName, 
1253         int *fullNameLen, int *done)
1254 {
1255         int             tid;
1256
1257         mprFree(*id);
1258         *id = mprStrdup(ep->token);
1259 #if BLD_DEBUG
1260         *fullNameLen = mprReallocStrcat(fullName, MPR_MAX_VAR, *fullNameLen,
1261                 0, *id, 0);
1262 #endif
1263         if (ep->currentObj == 0) {
1264                 ep->currentObj = ejsFindObj(ep, state, *id, flags);
1265         }
1266
1267         /*
1268          *      Find the referenced variable and store it in currentProperty.
1269           */
1270         ep->currentProperty = ejsFindProperty(ep, state, ep->currentObj, 
1271                 *id, flags);
1272         updateResult(ep, state, flags, ep->currentProperty);
1273
1274 #if BLD_DEBUG
1275         if (ep->currentProperty && (ep->currentProperty->name == 0 || 
1276                         ep->currentProperty->name[0] == '\0')) {
1277                 mprSetVarName(ep->currentProperty, *id);
1278         }
1279 #endif
1280
1281         tid = ejsLexGetToken(ep, state);
1282         if (tid == EJS_TOK_LPAREN) {
1283                 ejsLexPutbackToken(ep, EJS_TOK_FUNCTION_NAME, ep->token);
1284                 return state;
1285         }
1286
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);
1290                 return state;
1291         }
1292
1293         /*
1294          *      Only come here for variable access and declarations.
1295          *      Assignment handled elsewhere.
1296          */
1297         if (flags & EJS_FLAGS_EXE) {
1298                 if (state == EJS_STATE_DEC) {
1299                         /*
1300                          *      Declare a variable. Standard allows: var x ; var x ;
1301                          */
1302 #if DISABLED
1303                         if (ep->currentProperty != 0) {
1304                                 ejsError(ep, "Variable already defined \"%s\"\n", *id);
1305                                 return -1;
1306                         }
1307 #endif
1308                         /*
1309                          *      Create or overwrite if it already exists
1310                          */
1311                         mprSetPropertyValue(ep->currentObj, *id, 
1312                                 mprCreateUndefinedVar());
1313                         ep->currentProperty = 0;
1314                         mprDestroyVar(&ep->result);
1315
1316                 } else if (flags & EJS_FLAGS_FOREACH) {
1317                         if (ep->currentProperty == 0) {
1318                                 ep->currentProperty = 
1319                                         mprCreatePropertyValue(ep->currentObj, *id, 
1320                                                 mprCreateUndefinedVar());
1321                         }
1322
1323                 } else {
1324                         if (ep->currentProperty == 0) {
1325                                 if (ep->currentObj == ep->global || 
1326                                                 ep->currentObj == ep->local) {
1327                                         ejsError(ep, "Undefined variable \"%s\"\n", *id);
1328                                         return -1;
1329                                 }
1330                                 ep->currentProperty = mprCreatePropertyValue(ep->currentObj, 
1331                                         *id, mprCreateUndefinedVar());
1332                         }
1333                 }
1334         }
1335         ejsLexPutbackToken(ep, tid, ep->token);
1336         if (tid == EJS_TOK_RBRACKET || tid == EJS_TOK_COMMA || 
1337                         tid == EJS_TOK_IN) {
1338                 *done = 1;
1339         }
1340         return state;
1341 }
1342
1343 /******************************************************************************/
1344 /*
1345  *      Parse an "if" statement
1346  */
1347
1348 static int parseIf(Ejs *ep, int state, int flags, int *done)
1349 {
1350         bool    ifResult;
1351         int             thenFlags, elseFlags, tid;
1352
1353         if (state != EJS_STATE_STMT) {
1354                 return -1;
1355         }
1356         if (ejsLexGetToken(ep, state) != EJS_TOK_LPAREN) {
1357                 return -1;
1358         }
1359
1360         /*
1361          *      Evaluate the entire condition list "(condition)"
1362          */
1363         if (ejsParse(ep, EJS_STATE_COND, flags) != EJS_STATE_COND_DONE) {
1364                 return -1;
1365         }
1366         if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) {
1367                 return -1;
1368         }
1369
1370         /*
1371          *      This is the "then" case. We need to always parse both cases and
1372          *      execute only the relevant case.
1373          */
1374         ifResult = mprVarToBool(&ep->result);
1375         if (ifResult) {
1376                 thenFlags = flags;
1377                 elseFlags = flags & ~EJS_FLAGS_EXE;
1378         } else {
1379                 thenFlags = flags & ~EJS_FLAGS_EXE;
1380                 elseFlags = flags;
1381         }
1382
1383         /*
1384          *      Process the "then" case.
1385          */
1386         switch (ejsParse(ep, EJS_STATE_STMT, thenFlags)) {
1387         case EJS_STATE_RET:
1388                 state = EJS_STATE_RET;
1389                 return state;
1390         case EJS_STATE_STMT_DONE:
1391                 break;
1392         default:
1393                 return -1;
1394         }
1395
1396         /*
1397          *      Check to see if there is an "else" case
1398          */
1399         removeNewlines(ep, state);
1400         tid = ejsLexGetToken(ep, state);
1401         if (tid != EJS_TOK_ELSE) {
1402                 ejsLexPutbackToken(ep, tid, ep->token);
1403                 *done = 1;
1404                 return state;
1405         }
1406
1407         /*
1408          *      Process the "else" case.
1409          */
1410         switch (ejsParse(ep, EJS_STATE_STMT, elseFlags)) {
1411         case EJS_STATE_RET:
1412                 state = EJS_STATE_RET;
1413                 return state;
1414         case EJS_STATE_STMT_DONE:
1415                 break;
1416         default:
1417                 return -1;
1418         }
1419         *done = 1;
1420         return state;
1421 }
1422
1423 /******************************************************************************/
1424 /*
1425  *      Parse an "++" or "--" statement
1426  */
1427
1428 static int parseInc(Ejs *ep, int state, int flags)
1429 {
1430         MprVar  one;
1431
1432         if (! (flags & EJS_FLAGS_EXE)) {
1433                 return state;
1434         }
1435
1436         if (ep->currentProperty == 0) {
1437                 ejsError(ep, "Undefined variable \"%s\"\n", ep->token);
1438                 return -1;
1439         }
1440         one = mprCreateIntegerVar(1);
1441         if (evalExpr(ep, ep->currentProperty, (int) *ep->token, 
1442                         &one) < 0) {
1443                 return -1;
1444         }
1445         if (mprWriteProperty(ep->currentProperty, &ep->result) < 0) {
1446                 ejsError(ep, "Can't write to variable\n");
1447                 return -1;
1448         }
1449         return state;
1450 }
1451
1452 /******************************************************************************/
1453 /*
1454  *      Evaluate a condition. Implements &&, ||, !. Returns with a boolean result
1455  *      in ep->result. Returns -1 on errors, zero if successful.
1456  */
1457
1458 static int evalCond(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs)
1459 {
1460         bool    l, r, lval;
1461
1462         mprAssert(rel > 0);
1463
1464         l = mprVarToBool(lhs);
1465         r = mprVarToBool(rhs);
1466
1467         switch (rel) {
1468         case EJS_COND_AND:
1469                 lval = l && r;
1470                 break;
1471         case EJS_COND_OR:
1472                 lval = l || r;
1473                 break;
1474         default:
1475                 ejsError(ep, "Bad operator %d", rel);
1476                 return -1;
1477         }
1478
1479         mprCopyVarValue(&ep->result, mprCreateBoolVar(lval), 0);
1480         return 0;
1481 }
1482
1483 /******************************************************************************/
1484 /*
1485  *      Evaluate an operation. Returns with the result in ep->result. Returns -1
1486  *      on errors, otherwise zero is returned.
1487  */
1488
1489 static int evalExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs)
1490 {
1491         char    *str;
1492         MprNum  lval, num;
1493         int             rc;
1494
1495         mprAssert(rel > 0);
1496         str = 0;
1497         lval = 0;
1498
1499         /*
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,
1503          *      to strings.
1504          *
1505          *      First convert objects to comparable types. The "===" operator will
1506          *      test the sameness of object references. Here, we coerce to comparable
1507          *      types first.
1508          */
1509         if (lhs->type == MPR_TYPE_OBJECT) {
1510                 if (ejsRunFunction(ep->eid, lhs, "toValue", 0) == 0) {
1511                         mprCopyVar(lhs, &ep->result, MPR_SHALLOW_COPY);
1512                 } else {
1513                         if (ejsRunFunction(ep->eid, lhs, "toString", 0) == 0) {
1514                                 mprCopyVar(lhs, &ep->result, MPR_SHALLOW_COPY);
1515                         }
1516                 }
1517                 /* Nothing more can be done */
1518         }
1519
1520         if (rhs->type == MPR_TYPE_OBJECT) {
1521                 if (ejsRunFunction(ep->eid, rhs, "toValue", 0) == 0) {
1522                         mprCopyVar(rhs, &ep->result, MPR_SHALLOW_COPY);
1523                 } else {
1524                         if (ejsRunFunction(ep->eid, rhs, "toString", 0) == 0) {
1525                                 mprCopyVar(rhs, &ep->result, MPR_SHALLOW_COPY);
1526                         }
1527                 }
1528                 /* Nothing more can be done */
1529         }
1530
1531         /*
1532          *      From here on, lhs and rhs may contain allocated data (strings), so 
1533          *      we must always destroy before overwriting.
1534          */
1535         
1536         /*
1537          *      Only allow a few bool operations. Otherwise convert to number.
1538          */
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);
1543                 mprDestroyVar(lhs);
1544                 *lhs = mprCreateNumberVar(num);
1545         }
1546
1547         /*
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.
1551          */
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;
1557                                 mprDestroyVar(lhs);
1558                                 *lhs = mprCreateNumberVar(num);
1559                                 /* Examine further below */
1560
1561                         } else {
1562                                 /*
1563                                  *      Convert the RHS to a string
1564                                  */
1565                                 mprVarToString(&str, MPR_MAX_STRING, 0, rhs);
1566                                 rhs->allocatedVar = 0;
1567                                 mprDestroyVar(rhs);
1568                                 *rhs = mprCreateStringVar(str, 1);
1569                                 mprFree(str);
1570                         }
1571
1572 #if BLD_FEATURE_FLOATING_POINT
1573                 } else if (lhs->type == MPR_TYPE_FLOAT) {
1574                         /*
1575                          *      Convert rhs to floating
1576                          */
1577                         double f = mprVarToFloat(rhs);
1578                         mprDestroyVar(rhs);
1579                         *rhs = mprCreateFloatVar(f);
1580
1581 #endif
1582 #if BLD_FEATURE_INT64
1583                 } else if (lhs->type == MPR_TYPE_INT64) {
1584                         /*
1585                          *      Convert the rhs to 64 bit
1586                          */
1587                         int64 n = mprVarToInteger64(rhs);
1588                         mprDestroyVar(rhs);
1589                         *rhs = mprCreateInteger64Var(n);
1590 #endif
1591                 } else if (lhs->type == MPR_TYPE_BOOL || lhs->type == MPR_TYPE_INT) {
1592
1593                         if (rhs->type == MPR_TYPE_STRING) {
1594                                 /*
1595                                  *      Convert to lhs to a string
1596                                  */
1597                                 mprVarToString(&str, MPR_MAX_STRING, 0, lhs);
1598                                 mprDestroyVar(lhs);
1599                                 *lhs = mprCreateStringVar(str, 1);
1600                                 mprFree(str);
1601
1602 #if BLD_FEATURE_FLOATING_POINT
1603                         } else if (rhs->type == MPR_TYPE_FLOAT) {
1604                                 /*
1605                                  *      Convert lhs to floating
1606                                  */
1607                                 double f = mprVarToFloat(lhs);
1608                                 mprDestroyVar(lhs);
1609                                 *lhs = mprCreateFloatVar(f);
1610 #endif
1611
1612                         } else {
1613                                 /*
1614                                  *      Convert both operands to numbers
1615                                  */
1616                                 num = mprVarToNumber(lhs);
1617                                 mprDestroyVar(lhs);
1618                                 *lhs = mprCreateNumberVar(num);
1619
1620                                 num = mprVarToNumber(rhs);
1621                                 mprDestroyVar(rhs);
1622                                 *rhs = mprCreateNumberVar(num);
1623                         }
1624                 }
1625         }
1626
1627         /*
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
1630          *      special values.
1631          */
1632         if (lhs->type == MPR_TYPE_UNDEFINED || lhs->type == MPR_TYPE_NULL) {
1633                 switch (rel) {
1634                 case EJS_EXPR_EQ:
1635                         lval = lhs->type == rhs->type;
1636                         break;
1637                 case EJS_EXPR_NOTEQ:
1638                         lval = lhs->type != rhs->type;
1639                         break;
1640                 default:
1641                         lval = 0;
1642                 }
1643                 mprCopyVarValue(&ep->result, mprCreateBoolVar((bool) lval), 0);
1644                 return 0;
1645         }
1646
1647         /*
1648          *      Types are the same here
1649          */
1650         switch (lhs->type) {
1651         default:
1652         case MPR_TYPE_UNDEFINED:
1653         case MPR_TYPE_NULL:
1654                 /* Should be handled above */
1655                 mprAssert(0);
1656                 return 0;
1657
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);
1663                 return 0;
1664
1665         case MPR_TYPE_PTR:
1666                 mprCopyVarValue(&ep->result, mprCreateBoolVar(lhs->ptr == rhs->ptr), 0);
1667                 return 0;
1668
1669         case MPR_TYPE_BOOL:
1670                 rc = evalBoolExpr(ep, lhs->boolean, rel, rhs->boolean);
1671                 break;
1672
1673 #if BLD_FEATURE_FLOATING_POINT
1674         case MPR_TYPE_FLOAT:
1675                 rc = evalFloatExpr(ep, lhs->floating, rel, rhs->floating);
1676                 break;
1677 #endif
1678
1679         case MPR_TYPE_INT:
1680                 rc = evalNumericExpr(ep, (MprNum) lhs->integer, rel, 
1681                         (MprNum) rhs->integer);
1682                 break;
1683
1684 #if BLD_FEATURE_INT64
1685         case MPR_TYPE_INT64:
1686                 rc = evalNumericExpr(ep, (MprNum) lhs->integer64, rel, 
1687                         (MprNum) rhs->integer64);
1688                 break;
1689 #endif
1690
1691         case MPR_TYPE_STRING:
1692                 rc = evalStringExpr(ep, lhs, rel, rhs);
1693         }
1694         return rc;
1695 }
1696
1697 /******************************************************************************/
1698 #if BLD_FEATURE_FLOATING_POINT
1699 /*
1700  *      Expressions with floating operands
1701  */
1702
1703 static int evalFloatExpr(Ejs *ep, double l, int rel, double r) 
1704 {
1705         double  lval;
1706         bool    logical;
1707
1708         lval = 0;
1709         logical = 0;
1710
1711         switch (rel) {
1712         case EJS_EXPR_PLUS:
1713                 lval = l + r;
1714                 break;
1715         case EJS_EXPR_INC:
1716                 lval = l + 1;
1717                 break;
1718         case EJS_EXPR_MINUS:
1719                 lval = l - r;
1720                 break;
1721         case EJS_EXPR_DEC:
1722                 lval = l - 1;
1723                 break;
1724         case EJS_EXPR_MUL:
1725                 lval = l * r;
1726                 break;
1727         case EJS_EXPR_DIV:
1728                 lval = l / r;
1729                 break;
1730         default:
1731                 logical++;
1732                 break;
1733         }
1734
1735         /*
1736          *      Logical operators
1737          */
1738         if (logical) {
1739
1740                 switch (rel) {
1741                 case EJS_EXPR_EQ:
1742                         lval = l == r;
1743                         break;
1744                 case EJS_EXPR_NOTEQ:
1745                         lval = l != r;
1746                         break;
1747                 case EJS_EXPR_LESS:
1748                         lval = (l < r) ? 1 : 0;
1749                         break;
1750                 case EJS_EXPR_LESSEQ:
1751                         lval = (l <= r) ? 1 : 0;
1752                         break;
1753                 case EJS_EXPR_GREATER:
1754                         lval = (l > r) ? 1 : 0;
1755                         break;
1756                 case EJS_EXPR_GREATEREQ:
1757                         lval = (l >= r) ? 1 : 0;
1758                         break;
1759                 case EJS_EXPR_BOOL_COMP:
1760                         lval = (r == 0) ? 1 : 0;
1761                         break;
1762                 default:
1763                         ejsError(ep, "Bad operator %d", rel);
1764                         return -1;
1765                 }
1766                 mprCopyVarValue(&ep->result, mprCreateBoolVar(lval != 0), 0);
1767
1768         } else {
1769                 mprCopyVarValue(&ep->result, mprCreateFloatVar(lval), 0);
1770         }
1771         return 0;
1772 }
1773
1774 #endif /* BLD_FEATURE_FLOATING_POINT */
1775 /******************************************************************************/
1776 /*
1777  *      Expressions with boolean operands
1778  */
1779
1780 static int evalBoolExpr(Ejs *ep, bool l, int rel, bool r) 
1781 {
1782         bool    lval;
1783
1784         switch (rel) {
1785         case EJS_EXPR_EQ:
1786                 lval = l == r;
1787                 break;
1788         case EJS_EXPR_NOTEQ:
1789                 lval = l != r;
1790                 break;
1791         case EJS_EXPR_BOOL_COMP:
1792                 lval = (r == 0) ? 1 : 0;
1793                 break;
1794         default:
1795                 ejsError(ep, "Bad operator %d", rel);
1796                 return -1;
1797         }
1798         mprCopyVarValue(&ep->result, mprCreateBoolVar(lval), 0);
1799         return 0;
1800 }
1801
1802 /******************************************************************************/
1803 /*
1804  *      Expressions with numeric operands
1805  */
1806
1807 static int evalNumericExpr(Ejs *ep, MprNum l, int rel, MprNum r) 
1808 {
1809         MprNum  lval;
1810         bool    logical;
1811
1812         lval = 0;
1813         logical = 0;
1814
1815         switch (rel) {
1816         case EJS_EXPR_PLUS:
1817                 lval = l + r;
1818                 break;
1819         case EJS_EXPR_INC:
1820                 lval = l + 1;
1821                 break;
1822         case EJS_EXPR_MINUS:
1823                 lval = l - r;
1824                 break;
1825         case EJS_EXPR_DEC:
1826                 lval = l - 1;
1827                 break;
1828         case EJS_EXPR_MUL:
1829                 lval = l * r;
1830                 break;
1831         case EJS_EXPR_DIV:
1832                 if (r != 0) {
1833                         lval = l / r;
1834                 } else {
1835                         ejsError(ep, "Divide by zero");
1836                         return -1;
1837                 }
1838                 break;
1839         case EJS_EXPR_MOD:
1840                 if (r != 0) {
1841                         lval = l % r;
1842                 } else {
1843                         ejsError(ep, "Modulo zero");
1844                         return -1;
1845                 }
1846                 break;
1847         case EJS_EXPR_LSHIFT:
1848                 lval = l << r;
1849                 break;
1850         case EJS_EXPR_RSHIFT:
1851                 lval = l >> r;
1852                 break;
1853
1854         default:
1855                 logical++;
1856                 break;
1857         }
1858
1859         /*
1860          *      Logical operators
1861          */
1862         if (logical) {
1863
1864                 switch (rel) {
1865                 case EJS_EXPR_EQ:
1866                         lval = l == r;
1867                         break;
1868                 case EJS_EXPR_NOTEQ:
1869                         lval = l != r;
1870                         break;
1871                 case EJS_EXPR_LESS:
1872                         lval = (l < r) ? 1 : 0;
1873                         break;
1874                 case EJS_EXPR_LESSEQ:
1875                         lval = (l <= r) ? 1 : 0;
1876                         break;
1877                 case EJS_EXPR_GREATER:
1878                         lval = (l > r) ? 1 : 0;
1879                         break;
1880                 case EJS_EXPR_GREATEREQ:
1881                         lval = (l >= r) ? 1 : 0;
1882                         break;
1883                 case EJS_EXPR_BOOL_COMP:
1884                         lval = (r == 0) ? 1 : 0;
1885                         break;
1886                 default:
1887                         ejsError(ep, "Bad operator %d", rel);
1888                         return -1;
1889                 }
1890                 mprCopyVarValue(&ep->result, mprCreateBoolVar(lval != 0), 0);
1891
1892         } else {
1893                 mprCopyVarValue(&ep->result, mprCreateNumberVar(lval), 0);
1894         }
1895         return 0;
1896 }
1897
1898 /******************************************************************************/
1899 /*
1900  *      Expressions with string operands
1901  */
1902
1903 static int evalStringExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs)
1904 {
1905         int             lval;
1906
1907         mprAssert(ep);
1908         mprAssert(lhs);
1909         mprAssert(rhs);
1910
1911         switch (rel) {
1912         case EJS_EXPR_LESS:
1913                 lval = strcmp(lhs->string, rhs->string) < 0;
1914                 break;
1915         case EJS_EXPR_LESSEQ:
1916                 lval = strcmp(lhs->string, rhs->string) <= 0;
1917                 break;
1918         case EJS_EXPR_GREATER:
1919                 lval = strcmp(lhs->string, rhs->string) > 0;
1920                 break;
1921         case EJS_EXPR_GREATEREQ:
1922                 lval = strcmp(lhs->string, rhs->string) >= 0;
1923                 break;
1924         case EJS_EXPR_EQ:
1925                 lval = strcmp(lhs->string, rhs->string) == 0;
1926                 break;
1927         case EJS_EXPR_NOTEQ:
1928                 lval = strcmp(lhs->string, rhs->string) != 0;
1929                 break;
1930         case EJS_EXPR_PLUS:
1931                 /*
1932                  *      This differs from all the above operations. We append rhs to lhs.
1933                  */
1934                 mprDestroyVar(&ep->result);
1935                 appendValue(&ep->result, lhs);
1936                 appendValue(&ep->result, rhs);
1937                 return 0;
1938
1939         case EJS_EXPR_INC:
1940         case EJS_EXPR_DEC:
1941         case EJS_EXPR_MINUS:
1942         case EJS_EXPR_DIV:
1943         case EJS_EXPR_MOD:
1944         case EJS_EXPR_LSHIFT:
1945         case EJS_EXPR_RSHIFT:
1946         default:
1947                 ejsError(ep, "Bad operator");
1948                 return -1;
1949         }
1950
1951         mprCopyVarValue(&ep->result, mprCreateBoolVar(lval), 0);
1952         return 0;
1953 }
1954
1955 /******************************************************************************/
1956 /*
1957  *      Evaluate a function. obj is set to the current object if a function is being
1958  *      run.
1959  */
1960
1961 static int evalFunction(Ejs *ep, MprVar *obj, int flags)
1962 {
1963         EjsProc         *proc;
1964         MprVar          arguments, callee, thisObject, *prototype, **argValues;
1965         MprArray        *formalArgs, *actualArgs;
1966         char            buf[16], **argNames, **argBuf;
1967         int                     i, rc, fid;
1968
1969         mprAssert(ep); 
1970         mprAssert(ejsPtr(ep->eid));
1971
1972         rc = -1;
1973         proc = ep->proc;
1974         prototype = proc->fn;
1975         actualArgs = proc->args;
1976         argValues = (MprVar**) actualArgs->handles;
1977
1978         if (prototype == NULL) {
1979                 ejsError(ep, "Function name not defined '%s'\n", proc->procName);
1980                 return -1;
1981         }
1982
1983         /*
1984          *      Create a new variable stack frame. ie. new local variables.
1985          */
1986         fid = ejsOpenBlock(ep->eid);
1987
1988         if (flags & EJS_FLAGS_NEW) {
1989                 /*
1990                  *      Create a new bare object and pass it into the constructor as the 
1991                  *      "this" local variable. 
1992                  */
1993                 thisObject = ejsCreateObj("this", EJS_OBJ_HASH_SIZE);
1994                 mprCreatePropertyValue(ep->local, "this", thisObject);
1995
1996         } else if (obj) {
1997                 mprCreateProperty(ep->local, "this", obj);
1998         }
1999
2000         switch (prototype->type) {
2001         default:
2002                 mprAssert(0);
2003                 break;
2004
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]);
2010                         }
2011                 } else {
2012                         argBuf = 0;
2013                 }
2014
2015                 /*
2016                  *      Call the function depending on the various handle flags
2017                  */
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);
2025                 } else {
2026                         rc = (prototype->cFunctionWithStrings.fn)(ep->primaryHandle, 
2027                                 actualArgs->used, argBuf);
2028                 }
2029
2030                 if (actualArgs->used > 0) {
2031                         for (i = 0; i < actualArgs->used; i++) {
2032                                 mprFree(argBuf[i]);
2033                         }
2034                         mprFree(argBuf);
2035                 }
2036                 ep->thisPtr = 0;
2037                 break;
2038
2039         case MPR_TYPE_CFUNCTION:
2040                 /*
2041                  *      Call the function depending on the various handle flags
2042                  */
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, 
2049                                 argValues);
2050                 } else {
2051                         rc = (prototype->cFunction.fn)(ep->primaryHandle, 
2052                                 actualArgs->used, argValues);
2053                 }
2054                 ep->thisPtr = 0;
2055                 break;
2056
2057         case MPR_TYPE_FUNCTION:
2058
2059                 formalArgs = prototype->function.args;
2060                 argNames = (char**) formalArgs->handles;
2061
2062 #if FUTURE
2063                 if (formalArgs->used != actualArgs->used) {
2064                         ejsError(ep, "Bad number of args. Should be %d", formalArgs->used);
2065                         return -1;
2066                 }
2067 #endif
2068
2069                 /*
2070                  *      Create the arguments and callee variables
2071                  */
2072                 arguments = ejsCreateObj("arguments", EJS_SMALL_OBJ_HASH_SIZE);
2073                 callee = ejsCreateObj("callee", EJS_SMALL_OBJ_HASH_SIZE);
2074
2075                 /*
2076                  *      Overwrite the length property
2077                  */
2078                 mprCreatePropertyValue(&arguments, "length", 
2079                         mprCreateIntegerVar(actualArgs->used));
2080                 mprCreatePropertyValue(&callee, "length", 
2081                         mprCreateIntegerVar(formalArgs->used));
2082
2083                 /*
2084                  *      Define all the agruments to be set to the actual parameters
2085                  */
2086                 for (i = 0; i < formalArgs->used; i++) {
2087                         mprCreateProperty(ep->local, argNames[i], argValues[i]);
2088                 }
2089                 for (i = 0; i < actualArgs->used; i++) {
2090                         mprItoa(i, buf, sizeof(buf));
2091                         mprCreateProperty(&arguments, buf, argValues[i]);
2092                 }
2093
2094                 mprCreateProperty(&arguments, "callee", &callee);
2095                 mprCreateProperty(ep->local, "arguments", &arguments);
2096
2097                 /*
2098                  *      Can destroy our variables here as they are now referenced via
2099                  *      "local"
2100                  */
2101                 mprDestroyVar(&callee);
2102                 mprDestroyVar(&arguments);
2103
2104                 /*
2105                  *      Actually run the function
2106                  */
2107                 rc = ejsEvalScript(ep->eid, prototype->function.body, 0, 0);
2108                 break;
2109         }
2110
2111         ejsCloseBlock(ep->eid, fid);
2112
2113         /*
2114          *      New statements return the newly created object as the result of the
2115          *      command
2116          */
2117         if (flags & EJS_FLAGS_NEW) {
2118                 mprDestroyVar(&ep->result);
2119                 /*
2120                  *      Don't copy, we want to assign the actual object into result.
2121                  *      (mprCopyVar would inc the refCount to 2).
2122                  */
2123                 ep->result = thisObject;
2124         }
2125         return rc;
2126 }
2127
2128 /******************************************************************************/
2129 /*
2130  *      Run a function
2131  */
2132
2133 int ejsRunFunction(int eid, MprVar *obj, const char *functionName, 
2134         MprArray *args)
2135 {
2136         EjsProc         proc, *saveProc;
2137         Ejs                     *ep;
2138         int                     rc;
2139
2140         mprAssert(obj);
2141         mprAssert(functionName && *functionName);
2142
2143         if ((ep = ejsPtr(eid)) == NULL) {
2144                 mprAssert(ep);
2145                 return MPR_ERR_NOT_FOUND;
2146         }
2147         saveProc = ep->proc;
2148         ep->proc = &proc;
2149
2150         memset(&proc, 0, sizeof(EjsProc));
2151         mprDestroyVar(&ep->result);
2152
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;
2157         }
2158         proc.procName = mprStrdup(functionName);
2159         if (args == 0) {
2160                 proc.args = mprCreateArray();
2161                 rc = evalFunction(ep, obj, 0);
2162         } else {
2163                 proc.args = args;
2164                 rc = evalFunction(ep, obj, 0);
2165                 proc.args = 0;
2166         }
2167
2168         freeProc(&proc);
2169         ep->proc = saveProc;
2170
2171         return rc;
2172 }
2173
2174 /******************************************************************************/
2175 /*
2176  *      Find which object contains the property given the current context.
2177  *      Only used for top level properties. 
2178  */
2179
2180 MprVar *ejsFindObj(Ejs *ep, int state, const char *property, int flags)
2181 {
2182         MprVar          *vp;
2183         MprVar          *obj;
2184
2185         mprAssert(ep);
2186         mprAssert(property && *property);
2187
2188         if (flags & EJS_FLAGS_GLOBAL) {
2189                 obj = ep->global;
2190
2191         } else if (state == EJS_STATE_DEC || flags & EJS_FLAGS_LOCAL) {
2192                 obj = ep->local;
2193
2194         } else {
2195                 /* First look local, then look global */
2196                 vp = mprGetProperty(ep->local, property, 0);
2197                 if (vp) {
2198                         obj = ep->local;
2199                 } else if (mprGetProperty(ep->local, property, 0)) {
2200                         obj = ep->local;
2201                 } else {
2202                         obj = ep->global;
2203                 }
2204         }
2205         return obj;
2206 }
2207
2208 /******************************************************************************/
2209 /*
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.
2214  */
2215
2216 MprVar *ejsFindProperty(Ejs *ep, int state, MprVar *obj, char *property, 
2217         int flags)
2218 {
2219         MprVar  *vp;
2220
2221         mprAssert(ep);
2222         if (flags & EJS_FLAGS_EXE) {
2223                 mprAssert(property && *property);
2224         }
2225
2226         if (obj != 0) {
2227 #if FUTURE && MB
2228                 op = obj;
2229                 do {
2230                         vp = mprGetProperty(op, property, 0);
2231                         if (vp != 0) {
2232                                 if (op != obj && mprVarIsFunction(vp->type)) {
2233                                 }
2234                                 break;
2235                         }
2236                         op = op->baseObj;
2237                 } while (op);
2238 #endif
2239                 vp = mprGetProperty(obj, property, 0);
2240
2241         } else {
2242                 if (state == EJS_STATE_DEC) {
2243                         vp = mprGetProperty(ep->local, property, 0);
2244
2245                 } else {
2246                         /* Look local first, then global */
2247                         vp = mprGetProperty(ep->local, property, 0);
2248                         if (vp == NULL) {
2249                                 vp = mprGetProperty(ep->global, property, 0);
2250                         }
2251                 }
2252         }
2253         return vp;
2254 }
2255
2256 /******************************************************************************/
2257 /*
2258  *      Update result
2259  */
2260
2261 static void updateResult(Ejs *ep, int state, int flags, MprVar *vp)
2262 {
2263         if (flags & EJS_FLAGS_EXE && state != EJS_STATE_DEC) {
2264                 mprDestroyVar(&ep->result);
2265                 if (vp) {
2266                         mprCopyProperty(&ep->result, vp, MPR_SHALLOW_COPY);
2267                 }
2268         }
2269 }
2270
2271 /******************************************************************************/
2272 /*
2273  *      Append to the pointer value
2274  */
2275
2276 static void appendValue(MprVar *dest, MprVar *src)
2277 {
2278         char    *value, *oldBuf, *buf;
2279         int             len, oldLen;
2280
2281         mprAssert(dest);
2282
2283         mprVarToString(&value, MPR_MAX_STRING, 0, src);
2284
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));
2290                 dest->string = buf;
2291                 strcpy(&buf[oldLen], value);
2292
2293         } else {
2294                 *dest = mprCreateStringVar(value, 1);
2295         }
2296         mprFree(value);
2297 }
2298
2299 /******************************************************************************/
2300 /*
2301  *      Exit with status
2302  */
2303
2304 void ejsSetExitStatus(int eid, int status)
2305 {
2306         Ejs             *ep;
2307
2308         if ((ep = ejsPtr(eid)) == NULL) {
2309                 mprAssert(ep);
2310                 return;
2311         }
2312         ep->exitStatus = status;
2313         ep->flags |= EJS_FLAGS_EXIT;
2314 }
2315
2316 /******************************************************************************/
2317 /*
2318  *      Free an argument list
2319  */
2320
2321 static void freeProc(EjsProc *proc)
2322 {
2323         MprVar  **argValues;
2324         int             i;
2325
2326         if (proc->args) {
2327                 argValues = (MprVar**) proc->args->handles;
2328
2329                 for (i = 0; i < proc->args->max; i++) {
2330                         if (argValues[i]) {
2331                                 mprDestroyVar(argValues[i]);
2332                                 mprFree(argValues[i]);
2333                                 mprRemoveFromArray(proc->args, i);
2334                         }
2335                 }
2336
2337                 mprDestroyArray(proc->args);
2338         }
2339
2340         if (proc->procName) {
2341                 mprFree(proc->procName);
2342                 proc->procName = NULL;
2343         }
2344 }
2345
2346 /******************************************************************************/
2347 /*
2348  *      This function removes any new lines.  Used for else     cases, etc.
2349  */
2350
2351 static void removeNewlines(Ejs *ep, int state)
2352 {
2353         int tid;
2354
2355         do {
2356                 tid = ejsLexGetToken(ep, state);
2357         } while (tid == EJS_TOK_NEWLINE);
2358
2359         ejsLexPutbackToken(ep, tid, ep->token);
2360 }
2361
2362 /******************************************************************************/
2363
2364 #else
2365 void ejsParserDummy() {}
2366
2367 /******************************************************************************/
2368 #endif /* BLD_FEATURE_EJS */
2369
2370 /*
2371  * Local variables:
2372  * tab-width: 4
2373  * c-basic-offset: 4
2374  * End:
2375  * vim:tw=78
2376  * vim600: sw=4 ts=4 fdm=marker
2377  * vim<600: sw=4 ts=4
2378  */