📄 ejparse.c
字号:
/* * ejparse.c -- Ejscript(TM) Parser * * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * See the file "license.txt" for usage and redistribution license requirements *//******************************** Description *********************************//* * Ejscript parser. This implementes a subset of the JavaScript language. * Multiple Ejscript parsers can be opened at a time. *//********************************** Includes **********************************/#include "ejIntrn.h"#if CE #include "CE/wincompat.h"#endif/********************************** Local Data ********************************/ej_t **ejHandles; /* List of ej handles */int ejMax = -1; /* Maximum size of *//****************************** Forward Declarations **************************/#ifndef B_STATS#define setString(a,b,c) setstring(b,c)#endifstatic ej_t *ejPtr(int eid);static void clearString(char_t **ptr);static void setString(B_ARGS_DEC, char_t **ptr, char_t *s);static void appendString(char_t **ptr, char_t *s);static int parse(ej_t *ep, int state, int flags);static int parseStmt(ej_t *ep, int state, int flags);static int parseDeclaration(ej_t *ep, int state, int flags);static int parseArgs(ej_t *ep, int state, int flags);static int parseCond(ej_t *ep, int state, int flags);static int parseExpr(ej_t *ep, int state, int flags);static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs);static int evalCond(ej_t *ep, char_t *lhs, int rel, char_t *rhs);static int evalFunction(ej_t *ep);static void freeFunc(ejfunc_t *func);static void ejRemoveNewlines(ej_t *ep, int state);/************************************* Code ***********************************//* * Initialize a Ejscript engine */int ejOpenEngine(sym_fd_t variables, sym_fd_t functions){ ej_t *ep; int eid, vid; if ((eid = hAllocEntry((void***) &ejHandles, &ejMax, sizeof(ej_t))) < 0) { return -1; } ep = ejHandles[eid]; ep->eid = eid;/* * Create a top level symbol table if one is not provided for variables and * functions. Variables may create other symbol tables for block level * declarations so we use hAlloc to manage a list of variable tables. */ if ((vid = hAlloc((void***) &ep->variables)) < 0) { ejMax = hFree((void***) &ejHandles, ep->eid); return -1; } if (vid >= ep->variableMax) { ep->variableMax = vid + 1; } if (variables == -1) { ep->variables[vid] = symOpen(64) + EJ_OFFSET; ep->flags |= FLAGS_VARIABLES; } else { ep->variables[vid] = variables + EJ_OFFSET; } if (functions == -1) { ep->functions = symOpen(64); ep->flags |= FLAGS_FUNCTIONS; } else { ep->functions = functions; } ejLexOpen(ep);/* * Define standard constants */ ejSetGlobalVar(ep->eid, T("null"), NULL);#if EMF ejEmfOpen(ep->eid);#endif return ep->eid;}/******************************************************************************//* * Close */void ejCloseEngine(int eid){ ej_t *ep; int i; if ((ep = ejPtr(eid)) == NULL) { return; }#if EMF ejEmfClose(eid);#endif bfreeSafe(B_L, ep->error); ep->error = NULL; bfreeSafe(B_L, ep->result); ep->result = NULL; ejLexClose(ep); for (i = ep->variableMax - 1; i >= 0; i--) { if (ep->flags & FLAGS_VARIABLES) { symClose(ep->variables[i] - EJ_OFFSET); } ep->variableMax = hFree((void***) &ep->variables, i); } if (ep->flags & FLAGS_FUNCTIONS) { symClose(ep->functions); } ejMax = hFree((void***) &ejHandles, ep->eid); bfree(B_L, ep);}#ifndef __NO_EJ_FILE/******************************************************************************//* * Evaluate a Ejscript file */char_t *ejEvalFile(int eid, char_t *path, char_t **emsg){ gstat_t sbuf; ej_t *ep; char_t *script, *rs; char *fileBuf; int fd; a_assert(path && *path); if (emsg) { *emsg = NULL; } if ((ep = ejPtr(eid)) == NULL) { return NULL; } if ((fd = gopen(path, O_RDONLY | O_BINARY, 0666)) < 0) { ejError(ep, T("Bad handle %d"), eid); return NULL; } if (gstat(path, &sbuf) < 0) { gclose(fd); ejError(ep, T("Cant stat %s"), path); return NULL; } if ((fileBuf = balloc(B_L, sbuf.st_size + 1)) == NULL) { gclose(fd); ejError(ep, T("Cant malloc %d"), sbuf.st_size); return NULL; } if (gread(fd, fileBuf, sbuf.st_size) != (int)sbuf.st_size) { gclose(fd); bfree(B_L, fileBuf); ejError(ep, T("Error reading %s"), path); return NULL; } fileBuf[sbuf.st_size] = '\0'; gclose(fd); if ((script = ballocAscToUni(fileBuf, sbuf.st_size)) == NULL) { bfree(B_L, fileBuf); ejError(ep, T("Cant malloc %d"), sbuf.st_size + 1); return NULL; } bfree(B_L, fileBuf); rs = ejEvalBlock(eid, script, emsg); bfree(B_L, script); return rs;}#endif /* __NO_EJ_FILE *//******************************************************************************//* * Create a new variable scope block so that consecutive ejEval calls may * be made with the same varible scope. This space MUST be closed with * ejCloseBlock when the evaluations are complete. */int ejOpenBlock(int eid){ ej_t *ep; int vid; if((ep = ejPtr(eid)) == NULL) { return -1; } if ((vid = hAlloc((void***) &ep->variables)) < 0) { return -1; } if (vid >= ep->variableMax) { ep->variableMax = vid + 1; } ep->variables[vid] = symOpen(64) + EJ_OFFSET; return vid;}/******************************************************************************//* * Close a variable scope block. The vid parameter is the return value from * the call to ejOpenBlock */int ejCloseBlock(int eid, int vid){ ej_t *ep; if((ep = ejPtr(eid)) == NULL) { return -1; } symClose(ep->variables[vid] - EJ_OFFSET); ep->variableMax = hFree((void***) &ep->variables, vid); return 0;}/******************************************************************************//* * Create a new variable scope block and evaluate a script. All variables * created during this context will be automatically deleted when complete. */char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg){ char_t* returnVal; int vid; a_assert(script); vid = ejOpenBlock(eid); returnVal = ejEval(eid, script, emsg); ejCloseBlock(eid, vid); return returnVal;}/******************************************************************************//* * Parse and evaluate a Ejscript. The caller may provide a symbol table to * use for variables and function definitions. Return char_t pointer on * success otherwise NULL pointer is returned. */char_t *ejEval(int eid, char_t *script, char_t **emsg){ ej_t *ep; ejinput_t *oldBlock; int state; void *endlessLoopTest; int loopCounter; a_assert(script); if (emsg) { *emsg = NULL; } if ((ep = ejPtr(eid)) == NULL) { return NULL; } setString(B_L, &ep->result, T(""));/* * Allocate a new evaluation block, and save the old one */ oldBlock = ep->input; ejLexOpenScript(ep, script);/* * Do the actual parsing and evaluation */ loopCounter = 0; endlessLoopTest = NULL; do { state = parse(ep, STATE_BEGIN, FLAGS_EXE); if (state == STATE_RET) { state = STATE_EOF; }/* * prevent parser from going into infinite loop. If parsing the same * line 10 times then fail and report Syntax error. Most normal error * are caught in the parser itself. */ if (endlessLoopTest == ep->input->script.servp) { if (loopCounter++ > 10) { state = STATE_ERR; ejError(ep, T("Syntax error")); } } else { endlessLoopTest = ep->input->script.servp; loopCounter = 0; } } while (state != STATE_EOF && state != STATE_ERR); ejLexCloseScript(ep);/* * Return any error string to the user */ if (state == STATE_ERR && emsg) { *emsg = bstrdup(B_L, ep->error); }/* * Restore the old evaluation block */ ep->input = oldBlock; if (state == STATE_EOF) { return ep->result; } if (state == STATE_ERR) { return NULL; } return ep->result;}/******************************************************************************//* * Recursive descent parser for Ejscript */static int parse(ej_t *ep, int state, int flags){ a_assert(ep); switch (state) {/* * Any statement, function arguments or conditional expressions */ case STATE_STMT: if ((state = parseStmt(ep, state, flags)) != STATE_STMT_DONE && state != STATE_EOF && state != STATE_STMT_BLOCK_DONE && state != STATE_RET) { state = STATE_ERR; } break; case STATE_DEC: if ((state = parseStmt(ep, state, flags)) != STATE_DEC_DONE && state != STATE_EOF) { state = STATE_ERR; } break; case STATE_EXPR: if ((state = parseStmt(ep, state, flags)) != STATE_EXPR_DONE && state != STATE_EOF) { state = STATE_ERR; } break;/* * Variable declaration list */ case STATE_DEC_LIST: state = parseDeclaration(ep, state, flags); break;/* * Function argument string */ case STATE_ARG_LIST: state = parseArgs(ep, state, flags); break;/* * Logical condition list (relational operations separated by &&, ||) */ case STATE_COND: state = parseCond(ep, state, flags); break;/* * Expression list */ case STATE_RELEXP: state = parseExpr(ep, state, flags); break; } if (state == STATE_ERR && ep->error == NULL) { ejError(ep, T("Syntax error")); } return state;}/******************************************************************************//* * Parse any statement including functions and simple relational operations */static int parseStmt(ej_t *ep, int state, int flags){ ejfunc_t func; ejfunc_t *saveFunc; ejinput_t condScript, endScript, bodyScript, incrScript; char_t *value, *identifier; int done, expectSemi, thenFlags, elseFlags, tid, cond, forFlags; int ejVarType; a_assert(ep);/* * Set these to NULL, else we try to free them if an error occurs. */ endScript.putBackToken = NULL; bodyScript.putBackToken = NULL; incrScript.putBackToken = NULL; condScript.putBackToken = NULL; expectSemi = 0; saveFunc = NULL; for (done = 0; !done; ) { tid = ejLexGetToken(ep, state); switch (tid) { default: ejLexPutbackToken(ep, TOK_EXPR, ep->token); done++; break; case TOK_ERR: state = STATE_ERR; done++; break; case TOK_EOF: state = STATE_EOF; done++; break; case TOK_NEWLINE: break; case TOK_SEMI:/* * This case is when we discover no statement and just a lone ';' */ if (state != STATE_STMT) { ejLexPutbackToken(ep, tid, ep->token); } done++; break; case TOK_ID:/* * This could either be a reference to a variable or an assignment */ identifier = NULL; setString(B_L, &identifier, ep->token);/* * Peek ahead to see if this is an assignment */ tid = ejLexGetToken(ep, state); if (tid == TOK_ASSIGNMENT) { if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) { clearString(&identifier); goto error; } if (flags & FLAGS_EXE) { if ( state == STATE_DEC ) { ejSetLocalVar(ep->eid, identifier, ep->result); } else { ejVarType = ejGetVar(ep->eid, identifier, &value); if (ejVarType > 0) { ejSetLocalVar(ep->eid, identifier, ep->result); } else { ejSetGlobalVar(ep->eid, identifier, ep->result); } } } } else if (tid == TOK_INC_DEC ) { value = NULL; if (flags & FLAGS_EXE) { ejVarType = ejGetVar(ep->eid, identifier, &value); if (ejVarType < 0) { ejError(ep, T("Undefined variable %s\n"), identifier); goto error; } setString(B_L, &ep->result, value); if (evalExpr(ep, value, (int) *ep->token, T("1")) < 0) { state = STATE_ERR; break; } if (ejVarType > 0) { ejSetLocalVar(ep->eid, identifier, ep->result); } else { ejSetGlobalVar(ep->eid, identifier, ep->result); } } } else {/* * If we are processing a declaration, allow undefined vars */ value = NULL; if (state == STATE_DEC) { if (ejGetVar(ep->eid, identifier, &value) > 0) { ejError(ep, T("Variable already declared"), identifier); clearString(&identifier); goto error; } ejSetLocalVar(ep->eid, identifier, NULL); } else { if ( flags & FLAGS_EXE ) { if (ejGetVar(ep->eid, identifier, &value) < 0) { ejError(ep, T("Undefined variable %s\n"), identifier); clearString(&identifier); goto error; } } } setString(B_L, &ep->result, value); ejLexPutbackToken(ep, tid, ep->token); } clearString(&identifier); if (state == STATE_STMT) { expectSemi++; } done++; break; case TOK_LITERAL:/* * Set the result to the literal (number or string constant) */ setString(B_L, &ep->result, ep->token); if (state == STATE_STMT) { expectSemi++; } done++; break; case TOK_FUNCTION:/* * We must save any current ep->func value for the current stack frame */ if (ep->func) { saveFunc = ep->func; } memset(&func, 0, sizeof(ejfunc_t)); setString(B_L, &func.fname, ep->token); ep->func = &func; setString(B_L, &ep->result, T("")); if (ejLexGetToken(ep, state) != TOK_LPAREN) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -