source: rtems/cpukit/httpd/ejparse.c @ df49c60

4.104.114.84.95
Last change on this file since df49c60 was c1cdaa0, checked in by Joel Sherrill <joel.sherrill@…>, on 10/27/99 at 12:50:33

Patch from Emmanuel Raguet <raguet@…> and Eric Valette
<valette@…> to add a port of the GoAhead? web server
(httpd) to the RTEMS build tree. They have successfully used
this BSP on i386/pc386 and PowerPC/mcp750.

Mark and Joel spoke with Nick Berliner <nickb@…> on
26 Oct 1999 about this port and got verbal approval to include
it in RTEMS distributions.

  • Property mode set to 100644
File size: 33.1 KB
Line 
1/*
2 * ejparse.c -- Ejscript(TM) Parser
3 *
4 * Copyright (c) Go Ahead Software, Inc., 1995-1999
5 *
6 * See the file "license.txt" for usage and redistribution license requirements
7 */
8
9/******************************** Description *********************************/
10
11/*
12 *      Ejscript parser. This implementes a subset of the JavaScript language.
13 *      Multiple Ejscript parsers can be opened at a time.
14 */
15
16/********************************** Includes **********************************/
17
18#include        "ej.h"
19
20/********************************** Local Data ********************************/
21
22ej_t                    **ejHandles;                                                    /* List of ej handles */
23int                             ejMax = -1;                                                             /* Maximum size of      */
24
25/****************************** Forward Declarations **************************/
26
27static ej_t             *ejPtr(int eid);
28static void             clearString(char_t **ptr);
29static void             setString(char_t **ptr, char_t *s);
30static void             appendString(char_t **ptr, char_t *s);
31static void             freeVar(sym_t* sp);
32static int              parse(ej_t *ep, int state, int flags);
33static int              parseStmt(ej_t *ep, int state, int flags);
34static int              parseDeclaration(ej_t *ep, int state, int flags);
35static int              parseArgs(ej_t *ep, int state, int flags);
36static int              parseCond(ej_t *ep, int state, int flags);
37static int              parseExpr(ej_t *ep, int state, int flags);
38static int              evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs);
39static int              evalCond(ej_t *ep, char_t *lhs, int rel, char_t *rhs);
40static int              evalFunction(ej_t *ep);
41static void             freeFunc(ejfunc_t *func);
42
43/************************************* Code ***********************************/
44/*
45 *      Initialize a Ejscript engine
46 */
47
48int ejOpenEngine(sym_fd_t variables, sym_fd_t functions)
49{
50        ej_t    *ep;
51        int             eid, vid;
52
53        if ((eid = hAllocEntry((void***) &ejHandles, &ejMax, sizeof(ej_t))) < 0) {
54                return -1;
55        }
56        ep = ejHandles[eid];
57        ep->eid = eid;
58
59/*
60 *      Create a top level symbol table if one is not provided for variables and
61 *      functions. Variables may create other symbol tables for block level
62 *      declarations so we use hAlloc to manage a list of variable tables.
63 */
64        if ((vid = hAlloc((void***) &ep->variables)) < 0) {
65                ejMax = hFree((void***) &ejHandles, ep->eid);
66                return -1;
67        }
68        if (vid >= ep->variableMax) {
69                ep->variableMax = vid + 1;
70        }
71
72        if (variables == -1) {
73                ep->variables[vid] = symOpen(64) + EJ_OFFSET;
74                ep->flags |= FLAGS_VARIABLES;
75
76        } else {
77                ep->variables[vid] = variables + EJ_OFFSET;
78        }
79
80        if (functions == -1) {
81                ep->functions = symOpen(64);
82                ep->flags |= FLAGS_FUNCTIONS;
83        } else {
84                ep->functions = functions;
85        }
86
87        ejLexOpen(ep);
88
89/*
90 *      Define standard constants
91 */
92        ejSetGlobalVar(ep->eid, T("null"), NULL);
93
94#if EMF
95        ejEmfOpen(ep->eid);
96#endif
97        return ep->eid;
98}
99
100/******************************************************************************/
101/*
102 *      Close
103 */
104
105void ejCloseEngine(int eid)
106{
107        ej_t    *ep;
108        int             i;
109
110        if ((ep = ejPtr(eid)) == NULL) {
111                return;
112        }
113
114#if EMF
115        ejEmfClose(eid);
116#endif
117
118        bfreeSafe(B_L, ep->error);
119        ep->error = NULL;
120        bfreeSafe(B_L, ep->result);
121        ep->result = NULL;
122
123        ejLexClose(ep);
124
125        if (ep->flags & FLAGS_VARIABLES) {
126                for (i = ep->variableMax - 1; i >= 0; i--) {
127                        symClose(ep->variables[i] - EJ_OFFSET, freeVar);
128                        ep->variableMax = hFree((void***) &ep->variables, i);
129                }
130        }
131        if (ep->flags & FLAGS_FUNCTIONS) {
132                symClose(ep->functions, freeVar);
133        }
134
135        ejMax = hFree((void***) &ejHandles, ep->eid);
136        bfree(B_L, ep);
137}
138
139/******************************************************************************/
140/*
141 *      Callback from symClose. Free the variable.
142 */
143
144static void freeVar(sym_t* sp)
145{
146        valueFree(&sp->content);
147}
148
149/******************************************************************************/
150/*
151 *      Evaluate a Ejscript file
152 */
153
154#if DEV
155char_t *ejEvalFile(int eid, char_t *path, char_t **emsg)
156{
157        gstat_t sbuf;
158        ej_t    *ep;
159        char_t  *script, *rs;
160        char    *fileBuf;
161        int             fd;
162
163        a_assert(path && *path);
164
165        if (emsg) {
166                *emsg = NULL;
167        }
168        if ((ep = ejPtr(eid)) == NULL) {
169                return NULL;
170        }
171
172        if ((fd = gopen(path, O_RDONLY | O_BINARY, 0666)) < 0) {
173                ejError(ep, T("Bad handle %d"), eid);
174                return NULL;
175        }
176        if (gstat(path, &sbuf) < 0) {
177                close(fd);
178                ejError(ep, T("Cant stat %s"), path);
179                return NULL;
180        }
181        if ((fileBuf = balloc(B_L, sbuf.st_size + 1)) == NULL) {
182                close(fd);
183                ejError(ep, T("Cant malloc %d"), sbuf.st_size);
184                return NULL;
185        }
186        if (read(fd, fileBuf, sbuf.st_size) != (int)sbuf.st_size) {
187                close(fd);
188                bfree(B_L, fileBuf);
189                ejError(ep, T("Error reading %s"), path);
190                return NULL;
191        }
192        fileBuf[sbuf.st_size] = '\0';
193        close(fd);
194
195        if ((script = ballocAscToUni(fileBuf)) == NULL) {
196                bfree(B_L, fileBuf);
197                ejError(ep, T("Cant malloc %d"), sbuf.st_size + 1);
198                return NULL;
199        }
200        bfree(B_L, fileBuf);
201
202        rs = ejEvalBlock(eid, script, emsg);
203
204        bfree(B_L, script);
205        return rs;
206}
207#endif
208
209/******************************************************************************/
210/*
211 *      Create a new variable scope block so that consecutive ejEval calls may
212 *      be made with the same varible scope. This space MUST be closed with
213 *      ejCloseBlock when the evaluations are complete.         
214 */
215
216int ejOpenBlock(int eid)
217{
218        ej_t    *ep;
219        int             vid;   
220
221        if((ep = ejPtr(eid)) == NULL) {
222                return -1;
223        }       
224        if ((vid = hAlloc((void***) &ep->variables)) < 0) {
225                return -1;
226        }
227        if (vid >= ep->variableMax) {
228                ep->variableMax = vid + 1;
229        }       
230        ep->variables[vid] = symOpen(64) + EJ_OFFSET;
231        return vid;
232
233}
234
235/******************************************************************************/
236/*
237 *      Close a variable scope block. The vid parameter is the return value from
238 *      the call to ejOpenBlock
239 */
240
241int ejCloseBlock(int eid, int vid)
242{
243        ej_t    *ep;
244       
245        if((ep = ejPtr(eid)) == NULL) {
246                return -1;
247        }
248        symClose(ep->variables[vid] - EJ_OFFSET, freeVar);
249        ep->variableMax = hFree((void***) &ep->variables, vid);
250        return 0;       
251
252}
253/******************************************************************************/
254/*
255 *      Create a new variable scope block and evaluate a script. All variables
256 *      created during this context will be automatically deleted when complete.
257 */
258
259char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg)
260{
261        char_t* returnVal;
262        int             vid;
263
264        a_assert(script);
265
266        vid = ejOpenBlock(eid);
267        returnVal = ejEval(eid, script, emsg);
268        ejCloseBlock(eid, vid);
269
270        return returnVal;
271}
272
273/******************************************************************************/
274/*
275 *      Parse and evaluate a Ejscript. The caller may provide a symbol table to
276 *      use for variables and function definitions. Return char_t pointer on
277 *      success otherwise NULL pointer is returned.
278 */
279
280char_t *ejEval(int eid, char_t *script, char_t **emsg)
281{
282        ej_t    *ep;
283        ejinput_t       *oldBlock;
284        int             state;
285
286        a_assert(script);
287
288        if (emsg) {
289                *emsg = NULL;
290        }
291        if ((ep = ejPtr(eid)) == NULL) {
292                return NULL;
293        }
294
295        setString(&ep->result, T(""));
296
297/*
298 *      Allocate a new evaluation block, and save the old one
299 */
300        oldBlock = ep->input;
301        ejLexOpenScript(ep, script);
302
303/*
304 *      Do the actual parsing and evaluation
305 */
306        do {
307                state = parse(ep, STATE_BEGIN, FLAGS_EXE);
308        } while (state != STATE_EOF && state != STATE_ERR);
309
310        ejLexCloseScript(ep);
311
312/*
313 *      Return any error string to the user
314 */
315        if (state == STATE_ERR && emsg) {
316                *emsg = bstrdup(B_L, ep->error);
317        }
318
319/*
320 *      Restore the old evaluation block
321 */
322        ep->input = oldBlock;
323
324        if (state == STATE_EOF) {
325                return ep->result;
326        }
327        if (state == STATE_ERR) {
328                return NULL;
329        }
330        return ep->result;
331}
332
333/******************************************************************************/
334/*
335 *      Recursive descent parser for Ejscript
336 */
337
338static int parse(ej_t *ep, int state, int flags)
339{
340        a_assert(ep);
341
342        switch (state) {
343/*
344 *      Any statement, function arguments or conditional expressions
345 */
346        case STATE_STMT:
347        case STATE_DEC:
348                state = parseStmt(ep, state, flags);
349                break;
350
351        case STATE_EXPR:
352                state = parseStmt(ep, state, flags);
353                break;
354
355/*
356 *      Variable declaration list
357 */
358        case STATE_DEC_LIST:
359                state = parseDeclaration(ep, state, flags);
360                break;
361
362/*
363 *      Function argument string
364 */
365        case STATE_ARG_LIST:
366                state = parseArgs(ep, state, flags);
367                break;
368
369/*
370 *      Logical condition list (relational operations separated by &&, ||)
371 */
372        case STATE_COND:
373                state = parseCond(ep, state, flags);
374                break;
375
376/*
377 *      Expression list
378 */
379        case STATE_RELEXP:
380                state = parseExpr(ep, state, flags);
381                break;
382        }
383
384        if (state == STATE_ERR && ep->error == NULL) {
385                ejError(ep, T("Syntax error"));
386        }
387        return state;
388}
389
390/******************************************************************************/
391/*
392 *      Parse any statement including functions and simple relational operations
393 */
394
395static int parseStmt(ej_t *ep, int state, int flags)
396{
397        ejfunc_t        func;
398        ejfunc_t        *saveFunc;
399        ejinput_t       condScript, endScript, bodyScript, incrScript;
400        char_t          *value;
401        char_t          *identifier;
402        int                     done, expectSemi, thenFlags, elseFlags, tid, cond, forFlags;
403
404        a_assert(ep);
405
406/*
407 *      Set these to NULL, else we try to free them if an error occurs.
408 */
409        endScript.putBackToken = NULL;
410        bodyScript.putBackToken = NULL;
411        incrScript.putBackToken = NULL;
412        condScript.putBackToken = NULL;
413
414        expectSemi = 0;
415        saveFunc = NULL;
416
417        for (done = 0; !done; ) {
418                tid = ejLexGetToken(ep, state);
419
420                switch (tid) {
421                default:
422                        ejLexPutbackToken(ep, TOK_EXPR, ep->token);
423                        done++;
424                        break;
425
426                case TOK_ERR:
427                        state = STATE_ERR;
428                        done++;
429                        break;
430
431                case TOK_EOF:
432                        state = STATE_EOF;
433                        done++;
434                        break;
435
436                case TOK_NEWLINE:
437                        break;
438
439                case TOK_SEMI:
440/*
441 *                      This case is when we discover no statement and just a lone ';'
442 */
443                        if (state != STATE_STMT) {
444                                ejLexPutbackToken(ep, tid, ep->token);
445                        }
446                        done++;
447                        break;
448
449                case TOK_ID:
450/*
451 *                      This could either be a reference to a variable or an assignment
452 */
453                        identifier = NULL;
454                        setString(&identifier, ep->token);
455/*
456 *                      Peek ahead to see if this is an assignment
457 */
458                        tid = ejLexGetToken(ep, state);
459                        if (tid == TOK_ASSIGNMENT) {
460                                if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) {
461                                        goto error;
462                                }
463                                if (flags & FLAGS_EXE) {
464                                        if ( state == STATE_DEC ) {
465                                                ejSetLocalVar(ep->eid, identifier, ep->result);
466                                        }
467                                        else {
468                                                if (ejGetVar(ep->eid, identifier, &value) > 0) {
469                                                        ejSetLocalVar(ep->eid, identifier, ep->result);
470                                                } else {
471                                                        ejSetGlobalVar(ep->eid, identifier, ep->result);
472                                                }
473                                        }
474                                }
475
476                        } else if (tid == TOK_INC_DEC ) {
477                                value = NULL;
478                                if ( flags & FLAGS_EXE ) {
479                                        if (ejGetVar(ep->eid, identifier, &value) < 0) {
480                                                ejError(ep, T("Undefined variable %s\n"), identifier);
481                                                goto error;
482                                        }
483                                        setString(&ep->result, value);
484                                        if (evalExpr(ep, value, (int) *ep->token, T("1")) < 0) {
485                                                state = STATE_ERR;
486                                                break;
487                                        }
488                                        ejSetGlobalVar(ep->eid, identifier, ep->result);
489                                }
490
491                        } else {
492/*
493 *                              If we are processing a declaration, allow undefined vars
494 */
495                                value = NULL;
496                                if (state == STATE_DEC) {
497                                        if (ejGetVar(ep->eid, identifier, &value) > 0) {
498                                                ejError(ep, T("Variable already declared"),
499                                                        identifier);
500                                                clearString(&identifier);
501                                                goto error;
502                                        }
503                                        ejSetLocalVar(ep->eid, identifier, NULL);
504                                } else {
505                                        if ( flags & FLAGS_EXE ) {
506                                                if (ejGetVar(ep->eid, identifier, &value) < 0) {
507                                                        ejError(ep, T("Undefined variable %s\n"),
508                                                                identifier);
509                                                        clearString(&identifier);
510                                                        goto error;
511                                                }
512                                        }
513                                }
514                                setString(&ep->result, value);
515                                ejLexPutbackToken(ep, tid, ep->token);
516                        }
517                        clearString(&identifier);
518
519                        if (state == STATE_STMT) {
520                                expectSemi++;
521                        }
522                        done++;
523                        break;
524
525                case TOK_LITERAL:
526/*
527 *                      Set the result to the literal (number or string constant)
528 */
529                        setString(&ep->result, ep->token);
530                        if (state == STATE_STMT) {
531                                expectSemi++;
532                        }
533                        done++;
534                        break;
535
536                case TOK_FUNCTION:
537/*
538 *                      We must save any current ep->func value for the current stack frame
539 */
540                        if (ep->func) {
541                                saveFunc = ep->func;
542                        }
543                        memset(&func, 0, sizeof(ejfunc_t));
544                        setString(&func.fname, ep->token);
545                        ep->func = &func;
546
547                        setString(&ep->result, T(""));
548                        if (ejLexGetToken(ep, state) != TOK_LPAREN) {
549                                freeFunc(&func);
550                                goto error;
551                        }
552
553                        if (parse(ep, STATE_ARG_LIST, flags) != STATE_ARG_LIST_DONE) {
554                                freeFunc(&func);
555                                ep->func = saveFunc;
556                                goto error;
557                        }
558/*
559 *                      Evaluate the function if required
560 */
561                        if (flags & FLAGS_EXE && evalFunction(ep) < 0) {
562                                freeFunc(&func);
563                                ep->func = saveFunc;
564                                goto error;
565                        }
566
567                        freeFunc(&func);
568                        ep->func = saveFunc;
569
570                        if (ejLexGetToken(ep, state) != TOK_RPAREN) {
571                                goto error;
572                        }
573                        if (state == STATE_STMT) {
574                                expectSemi++;
575                        }
576                        done++;
577                        break;
578
579                case TOK_IF:
580                        if (state != STATE_STMT) {
581                                goto error;
582                        }
583                        if (ejLexGetToken(ep, state) != TOK_LPAREN) {
584                                goto error;
585                        }
586/*
587 *                      Evaluate the entire condition list "(condition)"
588 */
589                        if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) {
590                                goto error;
591                        }
592                        if (ejLexGetToken(ep, state) != TOK_RPAREN) {
593                                goto error;
594                        }
595/*
596 *                      This is the "then" case. We need to always parse both cases and
597 *                      execute only the relevant case.
598 */
599                        if (*ep->result == '1') {
600                                thenFlags = flags;
601                                elseFlags = flags & ~FLAGS_EXE;
602                        } else {
603                                thenFlags = flags & ~FLAGS_EXE;
604                                elseFlags = flags;
605                        }
606/*
607 *                      Process the "then" case
608 */
609                        if (parse(ep, STATE_STMT, thenFlags) != STATE_STMT_DONE) {
610                                goto error;
611                        }
612                        tid = ejLexGetToken(ep, state);
613                        if (tid != TOK_ELSE) {
614                                ejLexPutbackToken(ep, tid, ep->token);
615                                done++;
616                                break;
617                        }
618/*
619 *                      Process the "else" case
620 */
621                        if (parse(ep, STATE_STMT, elseFlags) != STATE_STMT_DONE) {
622                                goto error;
623                        }
624                        done++;
625                        break;
626
627                case TOK_FOR:
628/*
629 *                      Format for the expression is:
630 *
631 *                              for (initial; condition; incr) {
632 *                                      body;
633 *                              }
634 */
635                        if (state != STATE_STMT) {
636                                goto error;
637                        }
638                        if (ejLexGetToken(ep, state) != TOK_LPAREN) {
639                                goto error;
640                        }
641
642/*
643 *                      Evaluate the for loop initialization statement
644 */
645                        if (parse(ep, STATE_EXPR, flags) != STATE_EXPR_DONE) {
646                                goto error;
647                        }
648                        if (ejLexGetToken(ep, state) != TOK_SEMI) {
649                                goto error;
650                        }
651
652/*
653 *                      The first time through, we save the current input context just
654 *                      to each step: prior to the conditional, the loop increment and the
655 *                      loop body.
656 */
657                        ejLexSaveInputState(ep, &condScript);
658                        if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) {
659                                goto error;
660                        }
661                        cond = (*ep->result != '0');
662
663                        if (ejLexGetToken(ep, state) != TOK_SEMI) {
664                                goto error;
665                        }
666                       
667/*
668 *                      Don't execute the loop increment statement or the body first time
669 */
670                        forFlags = flags & ~FLAGS_EXE;
671                        ejLexSaveInputState(ep, &incrScript);
672                        if (parse(ep, STATE_EXPR, forFlags) != STATE_EXPR_DONE) {
673                                goto error;
674                        }
675                        if (ejLexGetToken(ep, state) != TOK_RPAREN) {
676                                goto error;
677                        }
678
679/*
680 *                      Parse the body and remember the end of the body script
681 */
682                        ejLexSaveInputState(ep, &bodyScript);
683                        if (parse(ep, STATE_STMT, forFlags) != STATE_STMT_DONE) {
684                                goto error;
685                        }
686                        ejLexSaveInputState(ep, &endScript);
687
688/*
689 *                      Now actually do the for loop. Note loop has been rotated
690 */
691                        while (cond && (flags & FLAGS_EXE) ) {
692/*
693 *                              Evaluate the body
694 */
695                                ejLexRestoreInputState(ep, &bodyScript);
696                                if (parse(ep, STATE_STMT, flags) != STATE_STMT_DONE) {
697                                        goto error;
698                                }
699/*
700 *                              Evaluate the increment script
701 */
702                                ejLexRestoreInputState(ep, &incrScript);
703                                if (parse(ep, STATE_EXPR, flags) != STATE_EXPR_DONE) {
704                                        goto error;
705                                }
706/*
707 *                              Evaluate the condition
708 */
709                                ejLexRestoreInputState(ep, &condScript);
710                                if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) {
711                                        goto error;
712                                }
713                                cond = (*ep->result != '0');
714                        }
715                        ejLexRestoreInputState(ep, &endScript);
716                        done++;
717                        break;
718
719                case TOK_VAR:
720                        if (parse(ep, STATE_DEC_LIST, flags) != STATE_DEC_LIST_DONE) {
721                                goto error;
722                        }
723                        done++;
724                        break;
725
726                case TOK_COMMA:
727                        ejLexPutbackToken(ep, TOK_EXPR, ep->token);
728                        done++;
729                        break;
730
731                case TOK_LPAREN:
732                        if (state == STATE_EXPR) {
733                                if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) {
734                                        goto error;
735                                }
736                                if (ejLexGetToken(ep, state) != TOK_RPAREN) {
737                                        goto error;
738                                }
739                                return STATE_EXPR_DONE;
740                        }
741                        done++;
742                        break;
743
744                case TOK_RPAREN:
745                        ejLexPutbackToken(ep, tid, ep->token);
746                        return STATE_EXPR_DONE;
747
748                case TOK_LBRACE:
749/*
750 *                      This handles any code in braces except "if () {} else {}"
751 */
752                        if (state != STATE_STMT) {
753                                goto error;
754                        }
755
756/*
757 *                      Parse will return STATE_STMT_BLOCK_DONE when the RBRACE is seen
758 */
759                        do {
760                                state = parse(ep, STATE_STMT, flags);
761                        } while (state == STATE_STMT_DONE);
762
763                        if (ejLexGetToken(ep, state) != TOK_RBRACE) {
764                                goto error;
765                        }
766                        return STATE_STMT_DONE;
767
768                case TOK_RBRACE:
769                        if (state == STATE_STMT) {
770                                ejLexPutbackToken(ep, tid, ep->token);
771                                return STATE_STMT_BLOCK_DONE;
772                        }
773                        goto error;
774
775                case TOK_RETURN:
776                        if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) {
777                                goto error;
778                        }
779                        if (flags & FLAGS_EXE) {
780                                while ( ejLexGetToken(ep, state) != TOK_EOF );
781                                done++;
782                                return STATE_EOF;
783                        }
784                        break;
785                }
786        }
787
788        if (expectSemi) {
789                tid = ejLexGetToken(ep, state);
790                if (tid != TOK_SEMI && tid != TOK_NEWLINE) {
791                        goto error;
792                }
793
794/*
795 *              Skip newline after semi-colon
796 */
797                tid = ejLexGetToken(ep, state);
798                if (tid != TOK_NEWLINE) {
799                        ejLexPutbackToken(ep, tid, ep->token);
800                }
801        }
802
803/*
804 *      Free resources and return the correct status
805 */
806doneParse:
807        if (tid == TOK_FOR) {
808                ejLexFreeInputState(ep, &condScript);
809                ejLexFreeInputState(ep, &incrScript);
810                ejLexFreeInputState(ep, &endScript);
811                ejLexFreeInputState(ep, &bodyScript);
812        }
813        if (state == STATE_STMT) {
814                return STATE_STMT_DONE;
815        } else if (state == STATE_DEC) {
816                return STATE_DEC_DONE;
817        } else if (state == STATE_EXPR) {
818                return STATE_EXPR_DONE;
819        } else if (state == STATE_EOF) {
820                return state;
821        } else {
822                return STATE_ERR;
823        }
824
825/*
826 *      Common error exit
827 */
828error:
829        state = STATE_ERR;
830        goto doneParse;
831}
832
833/******************************************************************************/
834/*
835 *      Parse variable declaration list
836 */
837
838static int parseDeclaration(ej_t *ep, int state, int flags)
839{
840        int             tid;
841
842        a_assert(ep);
843
844/*
845 *      Declarations can be of the following forms:
846 *                      var x;
847 *                      var x, y, z;
848 *                      var x = 1 + 2 / 3, y = 2 + 4;
849 *
850 *      We set the variable to NULL if there is no associated assignment.
851 */
852
853        do {
854                if ((tid = ejLexGetToken(ep, state)) != TOK_ID) {
855                        return STATE_ERR;
856                }
857                ejLexPutbackToken(ep, tid, ep->token);
858
859/*
860 *              Parse the entire assignment or simple identifier declaration
861 */
862                if (parse(ep, STATE_DEC, flags) != STATE_DEC_DONE) {
863                        return STATE_ERR;
864                }
865
866/*
867 *              Peek at the next token, continue if comma seen
868 */
869                tid = ejLexGetToken(ep, state);
870                if (tid == TOK_SEMI) {
871                        return STATE_DEC_LIST_DONE;
872                } else if (tid != TOK_COMMA) {
873                        return STATE_ERR;
874                }
875        } while (tid == TOK_COMMA);
876
877        if (tid != TOK_SEMI) {
878                return STATE_ERR;
879        }
880        return STATE_DEC_LIST_DONE;
881}
882
883/******************************************************************************/
884/*
885 *      Parse function arguments
886 */
887
888static int parseArgs(ej_t *ep, int state, int flags)
889{
890        int             tid, aid;
891
892        a_assert(ep);
893
894        do {
895                state = parse(ep, STATE_RELEXP, flags);
896                if (state == STATE_EOF || state == STATE_ERR) {
897                        return state;
898                }
899                if (state == STATE_RELEXP_DONE) {
900                        aid = hAlloc((void***) &ep->func->args);
901                        ep->func->args[aid] = bstrdup(B_L, ep->result);
902                        ep->func->nArgs++;
903                }
904/*
905 *              Peek at the next token, continue if more args (ie. comma seen)
906 */
907                tid = ejLexGetToken(ep, state);
908                if (tid != TOK_COMMA) {
909                        ejLexPutbackToken(ep, tid, ep->token);
910                }
911        } while (tid == TOK_COMMA);
912
913        if (tid != TOK_RPAREN && state != STATE_RELEXP_DONE) {
914                return STATE_ERR;
915        }
916        return STATE_ARG_LIST_DONE;
917}
918
919/******************************************************************************/
920/*
921 *      Parse conditional expression (relational ops separated by ||, &&)
922 */
923
924static int parseCond(ej_t *ep, int state, int flags)
925{
926        char_t  *lhs, *rhs;
927        int             tid, operator;
928
929        a_assert(ep);
930
931        setString(&ep->result, T(""));
932        rhs = lhs = NULL;
933        operator = 0;
934
935        do {
936/*
937 *      Recurse to handle one side of a conditional. Accumulate the
938 *      left hand side and the final result in ep->result.
939 */
940                state = parse(ep, STATE_RELEXP, flags);
941                if (state != STATE_RELEXP_DONE) {
942                        state = STATE_ERR;
943                        break;
944                }
945                if (operator > 0) {
946                        setString(&rhs, ep->result);
947                        if (evalCond(ep, lhs, operator, rhs) < 0) {
948                                state = STATE_ERR;
949                                break;
950                        }
951                }
952                setString(&lhs, ep->result);
953
954                tid = ejLexGetToken(ep, state);
955                if (tid == TOK_LOGICAL) {
956                        operator = (int) *ep->token;
957
958                } else if (tid == TOK_RPAREN || tid == TOK_SEMI) {
959                        ejLexPutbackToken(ep, tid, ep->token);
960                        state = STATE_COND_DONE;
961                        break;
962
963                } else {
964                        ejLexPutbackToken(ep, tid, ep->token);
965                }
966
967        } while (state == STATE_RELEXP_DONE);
968
969        if (lhs) {
970                bfree(B_L, lhs);
971        }
972        if (rhs) {
973                bfree(B_L, rhs);
974        }
975        return state;
976}
977
978/******************************************************************************/
979/*
980 *      Parse expression (leftHandSide operator rightHandSide)
981 */
982
983static int parseExpr(ej_t *ep, int state, int flags)
984{
985        char_t  *lhs, *rhs;
986        int             rel, tid;
987
988        a_assert(ep);
989
990        setString(&ep->result, T(""));
991        rhs = lhs = NULL;
992        rel = 0;
993
994        do {
995/*
996 *      This loop will handle an entire expression list. We call parse
997 *      to evalutate each term which returns the result in ep->result.
998 */
999                state = parse(ep, STATE_EXPR, flags);
1000                if (state != STATE_EXPR_DONE) {
1001                        state = STATE_ERR;
1002                        break;
1003                }
1004                if (rel > 0) {
1005                        setString(&rhs, ep->result);
1006                        if (evalExpr(ep, lhs, rel, rhs) < 0) {
1007                                state = STATE_ERR;
1008                                break;
1009                        }
1010                }
1011                setString(&lhs, ep->result);
1012
1013                if ((tid = ejLexGetToken(ep, state)) == TOK_EXPR) {
1014                        rel = (int) *ep->token;
1015
1016                } else if (tid == TOK_INC_DEC) {
1017                        rel = (int) *ep->token;
1018
1019                } else {
1020                        ejLexPutbackToken(ep, tid, ep->token);
1021                        state = STATE_RELEXP_DONE;
1022                }
1023
1024        } while (state == STATE_EXPR_DONE);
1025
1026        if (rhs)
1027                bfree(B_L, rhs);
1028        if (lhs)
1029                bfree(B_L, lhs);
1030        return state;
1031}
1032
1033/******************************************************************************/
1034/*
1035 *      Evaluate a condition. Implements &&, ||, !
1036 */
1037
1038static int evalCond(ej_t *ep, char_t *lhs, int rel, char_t *rhs)
1039{
1040        char_t  buf[16];
1041        int             l, r, lval;
1042
1043        a_assert(lhs);
1044        a_assert(rhs);
1045        a_assert(rel > 0);
1046
1047        lval = 0;
1048        if (gisdigit(*lhs) && gisdigit(*rhs)) {
1049                l = gatoi(lhs);
1050                r = gatoi(rhs);
1051                switch (rel) {
1052                case COND_AND:
1053                        lval = l && r;
1054                        break;
1055                case COND_OR:
1056                        lval = l || r;
1057                        break;
1058                default:
1059                        ejError(ep, T("Bad operator %d"), rel);
1060                        return -1;
1061                }
1062        } else {
1063                if (!gisdigit(*lhs)) {
1064                        ejError(ep, T("Conditional must be numeric"), lhs);
1065                } else {
1066                        ejError(ep, T("Conditional must be numeric"), rhs);
1067                }
1068        }
1069               
1070        stritoa(lval, buf, sizeof(buf));
1071        setString(&ep->result, buf);
1072        return 0;
1073}
1074
1075/******************************************************************************/
1076/*
1077 *      Evaluate an operation
1078 */
1079
1080static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs)
1081{
1082        char_t  *cp, buf[16];
1083        int             numeric, l, r, lval;
1084
1085        a_assert(lhs);
1086        a_assert(rhs);
1087        a_assert(rel > 0);
1088
1089/*
1090 *      All of the characters in the lhs and rhs must be numeric
1091 */
1092        numeric = 1;
1093        for (cp = lhs; *cp; cp++) {
1094                if (!gisdigit(*cp)) {
1095                        numeric = 0;
1096                        break;
1097                }
1098        }
1099        if (numeric) {
1100                for (cp = rhs; *cp; cp++) {
1101                        if (!gisdigit(*cp)) {
1102                                numeric = 0;
1103                                break;
1104                        }
1105                }
1106        }
1107        if (numeric) {
1108                l = gatoi(lhs);
1109                r = gatoi(rhs);
1110                switch (rel) {
1111                case EXPR_PLUS:
1112                        lval = l + r;
1113                        break;
1114                case EXPR_INC:
1115                        lval = l + 1;
1116                        break;
1117                case EXPR_MINUS:
1118                        lval = l - r;
1119                        break;
1120                case EXPR_DEC:
1121                        lval = l - 1;
1122                        break;
1123                case EXPR_MUL:
1124                        lval = l * r;
1125                        break;
1126                case EXPR_DIV:
1127                        if (r != 0) {
1128                                lval = l / r;
1129                        } else {
1130                                lval = 0;
1131                        }
1132                        break;
1133                case EXPR_MOD:
1134                        if (r != 0) {
1135                                lval = l % r;
1136                        } else {
1137                                lval = 0;
1138                        }
1139                        break;
1140                case EXPR_LSHIFT:
1141                        lval = l << r;
1142                        break;
1143                case EXPR_RSHIFT:
1144                        lval = l >> r;
1145                        break;
1146                case EXPR_EQ:
1147                        lval = l == r;
1148                        break;
1149                case EXPR_NOTEQ:
1150                        lval = l != r;
1151                        break;
1152                case EXPR_LESS:
1153                        lval = (l < r) ? 1 : 0;
1154                        break;
1155                case EXPR_LESSEQ:
1156                        lval = (l <= r) ? 1 : 0;
1157                        break;
1158                case EXPR_GREATER:
1159                        lval = (l > r) ? 1 : 0;
1160                        break;
1161                case EXPR_GREATEREQ:
1162                        lval = (l >= r) ? 1 : 0;
1163                        break;
1164                default:
1165                        ejError(ep, T("Bad operator %d"), rel);
1166                        return -1;
1167                }
1168
1169        } else {
1170                switch (rel) {
1171                case EXPR_PLUS:
1172                        clearString(&ep->result);
1173                        appendString(&ep->result, lhs);
1174                        appendString(&ep->result, rhs);
1175                        return 0;
1176                case EXPR_LESS:
1177                        lval = gstrcmp(lhs, rhs) < 0;
1178                        break;
1179                case EXPR_LESSEQ:
1180                        lval = gstrcmp(lhs, rhs) <= 0;
1181                        break;
1182                case EXPR_GREATER:
1183                        lval = gstrcmp(lhs, rhs) > 0;
1184                        break;
1185                case EXPR_GREATEREQ:
1186                        lval = gstrcmp(lhs, rhs) >= 0;
1187                        break;
1188                case EXPR_EQ:
1189                        lval = gstrcmp(lhs, rhs) == 0;
1190                        break;
1191                case EXPR_NOTEQ:
1192                        lval = gstrcmp(lhs, rhs) != 0;
1193                        break;
1194                case EXPR_INC:
1195                case EXPR_DEC:
1196                case EXPR_MINUS:
1197                case EXPR_DIV:
1198                case EXPR_MOD:
1199                case EXPR_LSHIFT:
1200                case EXPR_RSHIFT:
1201                default:
1202                        ejError(ep, T("Bad operator"));
1203                        return -1;
1204                }
1205        }
1206
1207        stritoa(lval, buf, sizeof(buf));
1208        setString(&ep->result, buf);
1209        return 0;
1210}
1211
1212/******************************************************************************/
1213/*
1214 *      Evaluate a function
1215 */
1216
1217static int evalFunction(ej_t *ep)
1218{
1219        sym_t   *sp;
1220        int             (*fn)(int eid, void *handle, int argc, char_t **argv);
1221
1222        if ((sp = symLookup(ep->functions, ep->func->fname)) == NULL) {
1223                ejError(ep, T("Undefined procedure %s"), ep->func->fname);
1224                return -1;
1225        }
1226        fn = (int (*)(int, void*, int, char_t**)) sp->content.value.integer;
1227        if (fn == NULL) {
1228                ejError(ep, T("Undefined procedure %s"), ep->func->fname);
1229                return -1;
1230        }
1231
1232        return (*fn)(ep->eid, (void*) ep->userHandle, ep->func->nArgs,
1233                ep->func->args);
1234}
1235
1236/******************************************************************************/
1237/*
1238 *      Output a parse ej_error message
1239 */
1240
1241void ejError(ej_t* ep, char_t* fmt, ...)
1242{
1243        va_list         args;
1244        ejinput_t       *ip;
1245        char_t          *errbuf, *msgbuf;
1246
1247        a_assert(ep);
1248        a_assert(fmt);
1249        ip = ep->input;
1250
1251        va_start(args, fmt);
1252        msgbuf = NULL;
1253        gvsnprintf(&msgbuf, E_MAX_ERROR, fmt, args);
1254        va_end(args);
1255
1256        if (ep && ip) {
1257                errbuf = NULL;
1258                gsnprintf(&errbuf, E_MAX_ERROR, T("%s\n At line %d, line => \n\n%s\n"),
1259                        msgbuf, ip->lineNumber, ip->line);
1260                bfreeSafe(B_L, ep->error);
1261                ep->error = errbuf;
1262        }
1263        bfreeSafe(B_L, msgbuf);
1264}
1265
1266/******************************************************************************/
1267/*
1268 *      Clear a string value
1269 */
1270
1271static void clearString(char_t **ptr)
1272{
1273        a_assert(ptr);
1274
1275        if (*ptr) {
1276                bfree(B_L, *ptr);
1277        }
1278        *ptr = NULL;
1279}
1280
1281/******************************************************************************/
1282/*
1283 *      Set a string value
1284 */
1285
1286static void setString(char_t **ptr, char_t *s)
1287{
1288        a_assert(ptr);
1289
1290        if (*ptr) {
1291                bfree(B_L, *ptr);
1292        }
1293        *ptr = bstrdup(B_L, s);
1294}
1295
1296/******************************************************************************/
1297/*
1298 *      Append to the pointer value
1299 */
1300
1301static void appendString(char_t **ptr, char_t *s)
1302{
1303        int     len, oldlen;
1304
1305        a_assert(ptr);
1306
1307        if (*ptr) {
1308                len = gstrlen(s);
1309                oldlen = gstrlen(*ptr);
1310                *ptr = brealloc(B_L, *ptr, (len + oldlen + 1) * sizeof(char_t));
1311                gstrcpy(&(*ptr)[oldlen], s);
1312        } else {
1313                *ptr = bstrdup(B_L, s);
1314        }
1315}
1316
1317/******************************************************************************/
1318/*
1319 *      Define a function
1320 */
1321
1322int ejSetGlobalFunction(int eid, char_t *name,
1323        int (*fn)(int eid, void *handle, int argc, char_t **argv))
1324{
1325        ej_t    *ep;
1326
1327        if ((ep = ejPtr(eid)) == NULL) {
1328                return -1;
1329        }
1330        return ejSetGlobalFunctionDirect(ep->functions, name, fn);
1331}
1332
1333/******************************************************************************/
1334/*
1335 *      Define a function directly into the function symbol table.
1336 */
1337
1338int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name,
1339        int (*fn)(int eid, void *handle, int argc, char_t **argv))
1340{
1341        if (symEnter(functions, name, valueInteger((long) fn), 0) == NULL) {
1342                return -1;
1343        }
1344        return 0;
1345}
1346
1347/******************************************************************************/
1348/*
1349 *      Get a function definition
1350 */
1351
1352void *ejGetGlobalFunction(int eid, char_t *name)
1353{
1354        ej_t    *ep;
1355        sym_t   *sp;
1356        int             (*fn)(int eid, void *handle, int argc, char_t **argv);
1357
1358        if ((ep = ejPtr(eid)) == NULL) {
1359                return NULL;
1360        }
1361        if ((sp = symLookup(ep->functions, name)) != NULL) {
1362                fn = (int (*)(int, void*, int, char_t**)) sp->content.value.integer;
1363                return (void*) fn;
1364        }
1365        return NULL;
1366}
1367
1368/******************************************************************************/
1369/*
1370 *      Utility routine to crack Ejscript arguments. Return the number of args
1371 *      seen. This routine only supports %s and %d type args.
1372 *
1373 *      Typical usage:
1374 *
1375 *              if (ejArgs(argc, argv, "%s %d", &name, &age) < 2) {
1376 *                      error("Insufficient args\n");
1377 *                      return -1;
1378 *              }
1379 */
1380
1381int ejArgs(int argc, char_t **argv, char_t *fmt, ...)
1382{
1383        va_list vargs;
1384        char_t  *cp, **sp;
1385        int             *ip;
1386        int             argn;
1387
1388        va_start(vargs, fmt);
1389
1390        if (argv == NULL) {
1391                return 0;
1392        }
1393
1394        for (argn = 0, cp = fmt; cp && *cp && argv[argn]; ) {
1395                if (*cp++ != '%') {
1396                        continue;
1397                }
1398
1399                switch (*cp) {
1400                case 'd':
1401                        ip = va_arg(vargs, int*);
1402                        *ip = gatoi(argv[argn]);
1403                        break;
1404
1405                case 's':
1406                        sp = va_arg(vargs, char_t**);
1407                        *sp = argv[argn];
1408                        break;
1409
1410                default:
1411/*
1412 *                      Unsupported
1413 */
1414                        a_assert(0);
1415                }
1416                argn++;
1417        }
1418
1419        va_end(vargs);
1420        return argn;
1421}
1422
1423/******************************************************************************/
1424/*
1425 *      Define the user handle
1426 */
1427
1428void ejSetUserHandle(int eid, int handle)
1429{
1430        ej_t    *ep;
1431
1432        if ((ep = ejPtr(eid)) == NULL) {
1433                return;
1434        }
1435        ep->userHandle = handle;
1436}
1437
1438/******************************************************************************/
1439/*
1440 *      Get the user handle
1441 */
1442
1443int ejGetUserHandle(int eid)
1444{
1445        ej_t    *ep;
1446
1447        if ((ep = ejPtr(eid)) == NULL) {
1448                return -1;
1449        }
1450        return ep->userHandle;
1451}
1452
1453/******************************************************************************/
1454/*
1455 *      Get the current line number
1456 */
1457
1458int ejGetLineNumber(int eid)
1459{
1460        ej_t    *ep;
1461
1462        if ((ep = ejPtr(eid)) == NULL) {
1463                return -1;
1464        }
1465        return ep->input->lineNumber;
1466}
1467
1468/******************************************************************************/
1469/*
1470 *      Set the result
1471 */
1472
1473void ejSetResult(int eid, char_t *s)
1474{
1475        ej_t    *ep;
1476
1477        if ((ep = ejPtr(eid)) == NULL) {
1478                return;
1479        }
1480        setString(&ep->result, s);
1481}
1482
1483/******************************************************************************/
1484/*
1485 *      Get the result
1486 */
1487
1488char_t *ejGetResult(int eid)
1489{
1490        ej_t    *ep;
1491
1492        if ((ep = ejPtr(eid)) == NULL) {
1493                return NULL;
1494        }
1495        return ep->result;
1496}
1497
1498/******************************************************************************/
1499/*
1500 *      Set a variable. Note: a variable with a value of NULL means declared but
1501 *      undefined. The value is defined in the top-most variable frame.
1502 */
1503
1504void ejSetVar(int eid, char_t *var, char_t *value)
1505{
1506        ej_t    *ep;
1507        value_t v;
1508
1509        a_assert(var && *var);
1510
1511        if ((ep = ejPtr(eid)) == NULL) {
1512                return;
1513        }
1514        if (value == NULL) {
1515                v = valueString(value, 0);
1516        } else {
1517                v = valueString(value, VALUE_ALLOCATE);
1518        }
1519        symEnter(ep->variables[ep->variableMax - 1] - EJ_OFFSET, var, v, 0);
1520}
1521
1522/******************************************************************************/
1523/*
1524 *      Set a local variable. Note: a variable with a value of NULL means
1525 *      declared but undefined. The value is defined in the top-most variable frame.
1526 */
1527
1528void ejSetLocalVar(int eid, char_t *var, char_t *value)
1529{
1530        ej_t    *ep;
1531        value_t v;
1532
1533        a_assert(var && *var);
1534
1535        if ((ep = ejPtr(eid)) == NULL) {
1536                return;
1537        }
1538        if (value == NULL) {
1539                v = valueString(value, 0);
1540        } else {
1541                v = valueString(value, VALUE_ALLOCATE);
1542        }
1543        symEnter(ep->variables[ep->variableMax - 1] - EJ_OFFSET, var, v, 0);
1544}
1545
1546/******************************************************************************/
1547/*
1548 *      Set a global variable. Note: a variable with a value of NULL means
1549 *      declared but undefined. The value is defined in the global variable frame.
1550 */
1551
1552void ejSetGlobalVar(int eid, char_t *var, char_t *value)
1553{
1554        ej_t    *ep;
1555        value_t v;
1556
1557        a_assert(var && *var);
1558
1559        if ((ep = ejPtr(eid)) == NULL) {
1560                return;
1561        }
1562        if (value == NULL) {
1563                v = valueString(value, 0);
1564        } else {
1565                v = valueString(value, VALUE_ALLOCATE);
1566        }
1567        symEnter(ep->variables[0] - EJ_OFFSET, var, v, 0);
1568}
1569
1570/******************************************************************************/
1571/*
1572 *      Get a variable
1573 */
1574
1575int ejGetVar(int eid, char_t *var, char_t **value)
1576{
1577        ej_t    *ep;
1578        sym_t   *sp;
1579        int             i;
1580
1581        a_assert(var && *var);
1582        a_assert(value);
1583
1584        if ((ep = ejPtr(eid)) == NULL) {
1585                return -1;
1586        }
1587
1588        for (i = ep->variableMax - 1; i >= 0; i--) {
1589                if ((sp = symLookup(ep->variables[i] - EJ_OFFSET, var)) == NULL) {
1590                        continue;
1591                }
1592                a_assert(sp->content.type == string);
1593                *value = sp->content.value.string;
1594                return i;
1595        }
1596        return -1;
1597}
1598
1599/******************************************************************************/
1600#if UNUSED
1601/*
1602 *      Get the variable symbol table
1603 */
1604
1605sym_fd_t ejGetVariableTable(int eid)
1606{
1607        ej_t    *ep;
1608
1609        if ((ep = ejPtr(eid)) == NULL) {
1610                return -1;
1611        }
1612        return ep->variables;
1613}
1614#endif
1615/******************************************************************************/
1616/*
1617 *      Get the functions symbol table
1618 */
1619
1620sym_fd_t ejGetFunctionTable(int eid)
1621{
1622        ej_t    *ep;
1623
1624        if ((ep = ejPtr(eid)) == NULL) {
1625                return -1;
1626        }
1627        return ep->functions;
1628}
1629
1630/******************************************************************************/
1631/*
1632 *      Free an argument list
1633 */
1634
1635static void freeFunc(ejfunc_t *func)
1636{
1637        int     i;
1638
1639        for (i = func->nArgs - 1; i >= 0; i--) {
1640                bfree(B_L, func->args[i]);
1641                func->nArgs = hFree((void***) &func->args, i);
1642        }
1643        if (func->fname) {
1644                bfree(B_L, func->fname);
1645                func->fname = NULL;
1646        }
1647}
1648
1649/******************************************************************************/
1650/*
1651 *      Get Ejscript pointer
1652 */
1653
1654static ej_t *ejPtr(int eid)
1655{
1656        a_assert(0 <= eid && eid < ejMax);
1657
1658        if (eid < 0 || eid >= ejMax || ejHandles[eid] == NULL) {
1659                ejError(NULL, T("Bad handle %d"), eid);
1660                return NULL;
1661        }
1662        return ejHandles[eid];
1663}
1664
1665/******************************************************************************/
Note: See TracBrowser for help on using the repository browser.