/*
|
/*
|
* ejparse.c -- Ejscript(TM) Parser
|
* ejparse.c -- Ejscript(TM) Parser
|
*
|
*
|
* Copyright (c) Go Ahead Software, Inc., 1995-1999
|
* Copyright (c) Go Ahead Software, Inc., 1995-1999
|
*
|
*
|
* See the file "license.txt" for usage and redistribution license requirements
|
* See the file "license.txt" for usage and redistribution license requirements
|
*/
|
*/
|
|
|
/******************************** Description *********************************/
|
/******************************** Description *********************************/
|
|
|
/*
|
/*
|
* Ejscript parser. This implementes a subset of the JavaScript language.
|
* Ejscript parser. This implementes a subset of the JavaScript language.
|
* Multiple Ejscript parsers can be opened at a time.
|
* Multiple Ejscript parsers can be opened at a time.
|
*/
|
*/
|
|
|
/********************************** Includes **********************************/
|
/********************************** Includes **********************************/
|
|
|
#include "ej.h"
|
#include "ej.h"
|
|
|
/********************************** Local Data ********************************/
|
/********************************** Local Data ********************************/
|
|
|
ej_t **ejHandles; /* List of ej handles */
|
ej_t **ejHandles; /* List of ej handles */
|
int ejMax = -1; /* Maximum size of */
|
int ejMax = -1; /* Maximum size of */
|
|
|
/****************************** Forward Declarations **************************/
|
/****************************** Forward Declarations **************************/
|
|
|
static ej_t *ejPtr(int eid);
|
static ej_t *ejPtr(int eid);
|
static void clearString(char_t **ptr);
|
static void clearString(char_t **ptr);
|
static void setString(char_t **ptr, char_t *s);
|
static void setString(char_t **ptr, char_t *s);
|
static void appendString(char_t **ptr, char_t *s);
|
static void appendString(char_t **ptr, char_t *s);
|
static void freeVar(sym_t* sp);
|
static void freeVar(sym_t* sp);
|
static int parse(ej_t *ep, int state, int flags);
|
static int parse(ej_t *ep, int state, int flags);
|
static int parseStmt(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 parseDeclaration(ej_t *ep, int state, int flags);
|
static int parseArgs(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 parseCond(ej_t *ep, int state, int flags);
|
static int parseExpr(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 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 evalCond(ej_t *ep, char_t *lhs, int rel, char_t *rhs);
|
static int evalFunction(ej_t *ep);
|
static int evalFunction(ej_t *ep);
|
static void freeFunc(ejfunc_t *func);
|
static void freeFunc(ejfunc_t *func);
|
|
|
/************************************* Code ***********************************/
|
/************************************* Code ***********************************/
|
/*
|
/*
|
* Initialize a Ejscript engine
|
* Initialize a Ejscript engine
|
*/
|
*/
|
|
|
int ejOpenEngine(sym_fd_t variables, sym_fd_t functions)
|
int ejOpenEngine(sym_fd_t variables, sym_fd_t functions)
|
{
|
{
|
ej_t *ep;
|
ej_t *ep;
|
int eid, vid;
|
int eid, vid;
|
|
|
if ((eid = hAllocEntry((void***) &ejHandles, &ejMax, sizeof(ej_t))) < 0) {
|
if ((eid = hAllocEntry((void***) &ejHandles, &ejMax, sizeof(ej_t))) < 0) {
|
return -1;
|
return -1;
|
}
|
}
|
ep = ejHandles[eid];
|
ep = ejHandles[eid];
|
ep->eid = eid;
|
ep->eid = eid;
|
|
|
/*
|
/*
|
* Create a top level symbol table if one is not provided for variables and
|
* Create a top level symbol table if one is not provided for variables and
|
* functions. Variables may create other symbol tables for block level
|
* functions. Variables may create other symbol tables for block level
|
* declarations so we use hAlloc to manage a list of variable tables.
|
* declarations so we use hAlloc to manage a list of variable tables.
|
*/
|
*/
|
if ((vid = hAlloc((void***) &ep->variables)) < 0) {
|
if ((vid = hAlloc((void***) &ep->variables)) < 0) {
|
ejMax = hFree((void***) &ejHandles, ep->eid);
|
ejMax = hFree((void***) &ejHandles, ep->eid);
|
return -1;
|
return -1;
|
}
|
}
|
if (vid >= ep->variableMax) {
|
if (vid >= ep->variableMax) {
|
ep->variableMax = vid + 1;
|
ep->variableMax = vid + 1;
|
}
|
}
|
|
|
if (variables == -1) {
|
if (variables == -1) {
|
ep->variables[vid] = symOpen(64) + EJ_OFFSET;
|
ep->variables[vid] = symOpen(64) + EJ_OFFSET;
|
ep->flags |= FLAGS_VARIABLES;
|
ep->flags |= FLAGS_VARIABLES;
|
|
|
} else {
|
} else {
|
ep->variables[vid] = variables + EJ_OFFSET;
|
ep->variables[vid] = variables + EJ_OFFSET;
|
}
|
}
|
|
|
if (functions == -1) {
|
if (functions == -1) {
|
ep->functions = symOpen(64);
|
ep->functions = symOpen(64);
|
ep->flags |= FLAGS_FUNCTIONS;
|
ep->flags |= FLAGS_FUNCTIONS;
|
} else {
|
} else {
|
ep->functions = functions;
|
ep->functions = functions;
|
}
|
}
|
|
|
ejLexOpen(ep);
|
ejLexOpen(ep);
|
|
|
/*
|
/*
|
* Define standard constants
|
* Define standard constants
|
*/
|
*/
|
ejSetGlobalVar(ep->eid, T("null"), NULL);
|
ejSetGlobalVar(ep->eid, T("null"), NULL);
|
|
|
#if EMF
|
#if EMF
|
ejEmfOpen(ep->eid);
|
ejEmfOpen(ep->eid);
|
#endif
|
#endif
|
return ep->eid;
|
return ep->eid;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Close
|
* Close
|
*/
|
*/
|
|
|
void ejCloseEngine(int eid)
|
void ejCloseEngine(int eid)
|
{
|
{
|
ej_t *ep;
|
ej_t *ep;
|
int i;
|
int i;
|
|
|
if ((ep = ejPtr(eid)) == NULL) {
|
if ((ep = ejPtr(eid)) == NULL) {
|
return;
|
return;
|
}
|
}
|
|
|
#if EMF
|
#if EMF
|
ejEmfClose(eid);
|
ejEmfClose(eid);
|
#endif
|
#endif
|
|
|
bfreeSafe(B_L, ep->error);
|
bfreeSafe(B_L, ep->error);
|
ep->error = NULL;
|
ep->error = NULL;
|
bfreeSafe(B_L, ep->result);
|
bfreeSafe(B_L, ep->result);
|
ep->result = NULL;
|
ep->result = NULL;
|
|
|
ejLexClose(ep);
|
ejLexClose(ep);
|
|
|
if (ep->flags & FLAGS_VARIABLES) {
|
if (ep->flags & FLAGS_VARIABLES) {
|
for (i = ep->variableMax - 1; i >= 0; i--) {
|
for (i = ep->variableMax - 1; i >= 0; i--) {
|
symClose(ep->variables[i] - EJ_OFFSET, freeVar);
|
symClose(ep->variables[i] - EJ_OFFSET, freeVar);
|
ep->variableMax = hFree((void***) &ep->variables, i);
|
ep->variableMax = hFree((void***) &ep->variables, i);
|
}
|
}
|
}
|
}
|
if (ep->flags & FLAGS_FUNCTIONS) {
|
if (ep->flags & FLAGS_FUNCTIONS) {
|
symClose(ep->functions, freeVar);
|
symClose(ep->functions, freeVar);
|
}
|
}
|
|
|
ejMax = hFree((void***) &ejHandles, ep->eid);
|
ejMax = hFree((void***) &ejHandles, ep->eid);
|
bfree(B_L, ep);
|
bfree(B_L, ep);
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Callback from symClose. Free the variable.
|
* Callback from symClose. Free the variable.
|
*/
|
*/
|
|
|
static void freeVar(sym_t* sp)
|
static void freeVar(sym_t* sp)
|
{
|
{
|
valueFree(&sp->content);
|
valueFree(&sp->content);
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Evaluate a Ejscript file
|
* Evaluate a Ejscript file
|
*/
|
*/
|
|
|
#if DEV
|
#if DEV
|
char_t *ejEvalFile(int eid, char_t *path, char_t **emsg)
|
char_t *ejEvalFile(int eid, char_t *path, char_t **emsg)
|
{
|
{
|
gstat_t sbuf;
|
gstat_t sbuf;
|
ej_t *ep;
|
ej_t *ep;
|
char_t *script, *rs;
|
char_t *script, *rs;
|
char *fileBuf;
|
char *fileBuf;
|
int fd;
|
int fd;
|
|
|
a_assert(path && *path);
|
a_assert(path && *path);
|
|
|
if (emsg) {
|
if (emsg) {
|
*emsg = NULL;
|
*emsg = NULL;
|
}
|
}
|
if ((ep = ejPtr(eid)) == NULL) {
|
if ((ep = ejPtr(eid)) == NULL) {
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
if ((fd = gopen(path, O_RDONLY | O_BINARY, 0666)) < 0) {
|
if ((fd = gopen(path, O_RDONLY | O_BINARY, 0666)) < 0) {
|
ejError(ep, T("Bad handle %d"), eid);
|
ejError(ep, T("Bad handle %d"), eid);
|
return NULL;
|
return NULL;
|
}
|
}
|
if (gstat(path, &sbuf) < 0) {
|
if (gstat(path, &sbuf) < 0) {
|
close(fd);
|
close(fd);
|
ejError(ep, T("Cant stat %s"), path);
|
ejError(ep, T("Cant stat %s"), path);
|
return NULL;
|
return NULL;
|
}
|
}
|
if ((fileBuf = balloc(B_L, sbuf.st_size + 1)) == NULL) {
|
if ((fileBuf = balloc(B_L, sbuf.st_size + 1)) == NULL) {
|
close(fd);
|
close(fd);
|
ejError(ep, T("Cant malloc %d"), sbuf.st_size);
|
ejError(ep, T("Cant malloc %d"), sbuf.st_size);
|
return NULL;
|
return NULL;
|
}
|
}
|
if (read(fd, fileBuf, sbuf.st_size) != (int)sbuf.st_size) {
|
if (read(fd, fileBuf, sbuf.st_size) != (int)sbuf.st_size) {
|
close(fd);
|
close(fd);
|
bfree(B_L, fileBuf);
|
bfree(B_L, fileBuf);
|
ejError(ep, T("Error reading %s"), path);
|
ejError(ep, T("Error reading %s"), path);
|
return NULL;
|
return NULL;
|
}
|
}
|
fileBuf[sbuf.st_size] = '\0';
|
fileBuf[sbuf.st_size] = '\0';
|
close(fd);
|
close(fd);
|
|
|
if ((script = ballocAscToUni(fileBuf)) == NULL) {
|
if ((script = ballocAscToUni(fileBuf)) == NULL) {
|
bfree(B_L, fileBuf);
|
bfree(B_L, fileBuf);
|
ejError(ep, T("Cant malloc %d"), sbuf.st_size + 1);
|
ejError(ep, T("Cant malloc %d"), sbuf.st_size + 1);
|
return NULL;
|
return NULL;
|
}
|
}
|
bfree(B_L, fileBuf);
|
bfree(B_L, fileBuf);
|
|
|
rs = ejEvalBlock(eid, script, emsg);
|
rs = ejEvalBlock(eid, script, emsg);
|
|
|
bfree(B_L, script);
|
bfree(B_L, script);
|
return rs;
|
return rs;
|
}
|
}
|
#endif
|
#endif
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Create a new variable scope block so that consecutive ejEval calls may
|
* 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
|
* be made with the same varible scope. This space MUST be closed with
|
* ejCloseBlock when the evaluations are complete.
|
* ejCloseBlock when the evaluations are complete.
|
*/
|
*/
|
|
|
int ejOpenBlock(int eid)
|
int ejOpenBlock(int eid)
|
{
|
{
|
ej_t *ep;
|
ej_t *ep;
|
int vid;
|
int vid;
|
|
|
if((ep = ejPtr(eid)) == NULL) {
|
if((ep = ejPtr(eid)) == NULL) {
|
return -1;
|
return -1;
|
}
|
}
|
if ((vid = hAlloc((void***) &ep->variables)) < 0) {
|
if ((vid = hAlloc((void***) &ep->variables)) < 0) {
|
return -1;
|
return -1;
|
}
|
}
|
if (vid >= ep->variableMax) {
|
if (vid >= ep->variableMax) {
|
ep->variableMax = vid + 1;
|
ep->variableMax = vid + 1;
|
}
|
}
|
ep->variables[vid] = symOpen(64) + EJ_OFFSET;
|
ep->variables[vid] = symOpen(64) + EJ_OFFSET;
|
return vid;
|
return vid;
|
|
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Close a variable scope block. The vid parameter is the return value from
|
* Close a variable scope block. The vid parameter is the return value from
|
* the call to ejOpenBlock
|
* the call to ejOpenBlock
|
*/
|
*/
|
|
|
int ejCloseBlock(int eid, int vid)
|
int ejCloseBlock(int eid, int vid)
|
{
|
{
|
ej_t *ep;
|
ej_t *ep;
|
|
|
if((ep = ejPtr(eid)) == NULL) {
|
if((ep = ejPtr(eid)) == NULL) {
|
return -1;
|
return -1;
|
}
|
}
|
symClose(ep->variables[vid] - EJ_OFFSET, freeVar);
|
symClose(ep->variables[vid] - EJ_OFFSET, freeVar);
|
ep->variableMax = hFree((void***) &ep->variables, vid);
|
ep->variableMax = hFree((void***) &ep->variables, vid);
|
return 0;
|
return 0;
|
|
|
}
|
}
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Create a new variable scope block and evaluate a script. All variables
|
* Create a new variable scope block and evaluate a script. All variables
|
* created during this context will be automatically deleted when complete.
|
* created during this context will be automatically deleted when complete.
|
*/
|
*/
|
|
|
char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg)
|
char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg)
|
{
|
{
|
char_t* returnVal;
|
char_t* returnVal;
|
int vid;
|
int vid;
|
|
|
a_assert(script);
|
a_assert(script);
|
|
|
vid = ejOpenBlock(eid);
|
vid = ejOpenBlock(eid);
|
returnVal = ejEval(eid, script, emsg);
|
returnVal = ejEval(eid, script, emsg);
|
ejCloseBlock(eid, vid);
|
ejCloseBlock(eid, vid);
|
|
|
return returnVal;
|
return returnVal;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Parse and evaluate a Ejscript. The caller may provide a symbol table to
|
* Parse and evaluate a Ejscript. The caller may provide a symbol table to
|
* use for variables and function definitions. Return char_t pointer on
|
* use for variables and function definitions. Return char_t pointer on
|
* success otherwise NULL pointer is returned.
|
* success otherwise NULL pointer is returned.
|
*/
|
*/
|
|
|
char_t *ejEval(int eid, char_t *script, char_t **emsg)
|
char_t *ejEval(int eid, char_t *script, char_t **emsg)
|
{
|
{
|
ej_t *ep;
|
ej_t *ep;
|
ejinput_t *oldBlock;
|
ejinput_t *oldBlock;
|
int state;
|
int state;
|
|
|
a_assert(script);
|
a_assert(script);
|
|
|
if (emsg) {
|
if (emsg) {
|
*emsg = NULL;
|
*emsg = NULL;
|
}
|
}
|
if ((ep = ejPtr(eid)) == NULL) {
|
if ((ep = ejPtr(eid)) == NULL) {
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
setString(&ep->result, T(""));
|
setString(&ep->result, T(""));
|
|
|
/*
|
/*
|
* Allocate a new evaluation block, and save the old one
|
* Allocate a new evaluation block, and save the old one
|
*/
|
*/
|
oldBlock = ep->input;
|
oldBlock = ep->input;
|
ejLexOpenScript(ep, script);
|
ejLexOpenScript(ep, script);
|
|
|
/*
|
/*
|
* Do the actual parsing and evaluation
|
* Do the actual parsing and evaluation
|
*/
|
*/
|
do {
|
do {
|
state = parse(ep, STATE_BEGIN, FLAGS_EXE);
|
state = parse(ep, STATE_BEGIN, FLAGS_EXE);
|
} while (state != STATE_EOF && state != STATE_ERR);
|
} while (state != STATE_EOF && state != STATE_ERR);
|
|
|
ejLexCloseScript(ep);
|
ejLexCloseScript(ep);
|
|
|
/*
|
/*
|
* Return any error string to the user
|
* Return any error string to the user
|
*/
|
*/
|
if (state == STATE_ERR && emsg) {
|
if (state == STATE_ERR && emsg) {
|
*emsg = bstrdup(B_L, ep->error);
|
*emsg = bstrdup(B_L, ep->error);
|
}
|
}
|
|
|
/*
|
/*
|
* Restore the old evaluation block
|
* Restore the old evaluation block
|
*/
|
*/
|
ep->input = oldBlock;
|
ep->input = oldBlock;
|
|
|
if (state == STATE_EOF) {
|
if (state == STATE_EOF) {
|
return ep->result;
|
return ep->result;
|
}
|
}
|
if (state == STATE_ERR) {
|
if (state == STATE_ERR) {
|
return NULL;
|
return NULL;
|
}
|
}
|
return ep->result;
|
return ep->result;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Recursive descent parser for Ejscript
|
* Recursive descent parser for Ejscript
|
*/
|
*/
|
|
|
static int parse(ej_t *ep, int state, int flags)
|
static int parse(ej_t *ep, int state, int flags)
|
{
|
{
|
a_assert(ep);
|
a_assert(ep);
|
|
|
switch (state) {
|
switch (state) {
|
/*
|
/*
|
* Any statement, function arguments or conditional expressions
|
* Any statement, function arguments or conditional expressions
|
*/
|
*/
|
case STATE_STMT:
|
case STATE_STMT:
|
case STATE_DEC:
|
case STATE_DEC:
|
state = parseStmt(ep, state, flags);
|
state = parseStmt(ep, state, flags);
|
break;
|
break;
|
|
|
case STATE_EXPR:
|
case STATE_EXPR:
|
state = parseStmt(ep, state, flags);
|
state = parseStmt(ep, state, flags);
|
break;
|
break;
|
|
|
/*
|
/*
|
* Variable declaration list
|
* Variable declaration list
|
*/
|
*/
|
case STATE_DEC_LIST:
|
case STATE_DEC_LIST:
|
state = parseDeclaration(ep, state, flags);
|
state = parseDeclaration(ep, state, flags);
|
break;
|
break;
|
|
|
/*
|
/*
|
* Function argument string
|
* Function argument string
|
*/
|
*/
|
case STATE_ARG_LIST:
|
case STATE_ARG_LIST:
|
state = parseArgs(ep, state, flags);
|
state = parseArgs(ep, state, flags);
|
break;
|
break;
|
|
|
/*
|
/*
|
* Logical condition list (relational operations separated by &&, ||)
|
* Logical condition list (relational operations separated by &&, ||)
|
*/
|
*/
|
case STATE_COND:
|
case STATE_COND:
|
state = parseCond(ep, state, flags);
|
state = parseCond(ep, state, flags);
|
break;
|
break;
|
|
|
/*
|
/*
|
* Expression list
|
* Expression list
|
*/
|
*/
|
case STATE_RELEXP:
|
case STATE_RELEXP:
|
state = parseExpr(ep, state, flags);
|
state = parseExpr(ep, state, flags);
|
break;
|
break;
|
}
|
}
|
|
|
if (state == STATE_ERR && ep->error == NULL) {
|
if (state == STATE_ERR && ep->error == NULL) {
|
ejError(ep, T("Syntax error"));
|
ejError(ep, T("Syntax error"));
|
}
|
}
|
return state;
|
return state;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Parse any statement including functions and simple relational operations
|
* Parse any statement including functions and simple relational operations
|
*/
|
*/
|
|
|
static int parseStmt(ej_t *ep, int state, int flags)
|
static int parseStmt(ej_t *ep, int state, int flags)
|
{
|
{
|
ejfunc_t func;
|
ejfunc_t func;
|
ejfunc_t *saveFunc;
|
ejfunc_t *saveFunc;
|
ejinput_t condScript, endScript, bodyScript, incrScript;
|
ejinput_t condScript, endScript, bodyScript, incrScript;
|
char_t *value;
|
char_t *value;
|
char_t *identifier;
|
char_t *identifier;
|
int done, expectSemi, thenFlags, elseFlags, tid, cond, forFlags;
|
int done, expectSemi, thenFlags, elseFlags, tid, cond, forFlags;
|
|
|
a_assert(ep);
|
a_assert(ep);
|
|
|
/*
|
/*
|
* Set these to NULL, else we try to free them if an error occurs.
|
* Set these to NULL, else we try to free them if an error occurs.
|
*/
|
*/
|
endScript.putBackToken = NULL;
|
endScript.putBackToken = NULL;
|
bodyScript.putBackToken = NULL;
|
bodyScript.putBackToken = NULL;
|
incrScript.putBackToken = NULL;
|
incrScript.putBackToken = NULL;
|
condScript.putBackToken = NULL;
|
condScript.putBackToken = NULL;
|
|
|
expectSemi = 0;
|
expectSemi = 0;
|
saveFunc = NULL;
|
saveFunc = NULL;
|
|
|
for (done = 0; !done; ) {
|
for (done = 0; !done; ) {
|
tid = ejLexGetToken(ep, state);
|
tid = ejLexGetToken(ep, state);
|
|
|
switch (tid) {
|
switch (tid) {
|
default:
|
default:
|
ejLexPutbackToken(ep, TOK_EXPR, ep->token);
|
ejLexPutbackToken(ep, TOK_EXPR, ep->token);
|
done++;
|
done++;
|
break;
|
break;
|
|
|
case TOK_ERR:
|
case TOK_ERR:
|
state = STATE_ERR;
|
state = STATE_ERR;
|
done++;
|
done++;
|
break;
|
break;
|
|
|
case TOK_EOF:
|
case TOK_EOF:
|
state = STATE_EOF;
|
state = STATE_EOF;
|
done++;
|
done++;
|
break;
|
break;
|
|
|
case TOK_NEWLINE:
|
case TOK_NEWLINE:
|
break;
|
break;
|
|
|
case TOK_SEMI:
|
case TOK_SEMI:
|
/*
|
/*
|
* This case is when we discover no statement and just a lone ';'
|
* This case is when we discover no statement and just a lone ';'
|
*/
|
*/
|
if (state != STATE_STMT) {
|
if (state != STATE_STMT) {
|
ejLexPutbackToken(ep, tid, ep->token);
|
ejLexPutbackToken(ep, tid, ep->token);
|
}
|
}
|
done++;
|
done++;
|
break;
|
break;
|
|
|
case TOK_ID:
|
case TOK_ID:
|
/*
|
/*
|
* This could either be a reference to a variable or an assignment
|
* This could either be a reference to a variable or an assignment
|
*/
|
*/
|
identifier = NULL;
|
identifier = NULL;
|
setString(&identifier, ep->token);
|
setString(&identifier, ep->token);
|
/*
|
/*
|
* Peek ahead to see if this is an assignment
|
* Peek ahead to see if this is an assignment
|
*/
|
*/
|
tid = ejLexGetToken(ep, state);
|
tid = ejLexGetToken(ep, state);
|
if (tid == TOK_ASSIGNMENT) {
|
if (tid == TOK_ASSIGNMENT) {
|
if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) {
|
if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) {
|
goto error;
|
goto error;
|
}
|
}
|
if (flags & FLAGS_EXE) {
|
if (flags & FLAGS_EXE) {
|
if ( state == STATE_DEC ) {
|
if ( state == STATE_DEC ) {
|
ejSetLocalVar(ep->eid, identifier, ep->result);
|
ejSetLocalVar(ep->eid, identifier, ep->result);
|
}
|
}
|
else {
|
else {
|
if (ejGetVar(ep->eid, identifier, &value) > 0) {
|
if (ejGetVar(ep->eid, identifier, &value) > 0) {
|
ejSetLocalVar(ep->eid, identifier, ep->result);
|
ejSetLocalVar(ep->eid, identifier, ep->result);
|
} else {
|
} else {
|
ejSetGlobalVar(ep->eid, identifier, ep->result);
|
ejSetGlobalVar(ep->eid, identifier, ep->result);
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
} else if (tid == TOK_INC_DEC ) {
|
} else if (tid == TOK_INC_DEC ) {
|
value = NULL;
|
value = NULL;
|
if ( flags & FLAGS_EXE ) {
|
if ( flags & FLAGS_EXE ) {
|
if (ejGetVar(ep->eid, identifier, &value) < 0) {
|
if (ejGetVar(ep->eid, identifier, &value) < 0) {
|
ejError(ep, T("Undefined variable %s\n"), identifier);
|
ejError(ep, T("Undefined variable %s\n"), identifier);
|
goto error;
|
goto error;
|
}
|
}
|
setString(&ep->result, value);
|
setString(&ep->result, value);
|
if (evalExpr(ep, value, (int) *ep->token, T("1")) < 0) {
|
if (evalExpr(ep, value, (int) *ep->token, T("1")) < 0) {
|
state = STATE_ERR;
|
state = STATE_ERR;
|
break;
|
break;
|
}
|
}
|
ejSetGlobalVar(ep->eid, identifier, ep->result);
|
ejSetGlobalVar(ep->eid, identifier, ep->result);
|
}
|
}
|
|
|
} else {
|
} else {
|
/*
|
/*
|
* If we are processing a declaration, allow undefined vars
|
* If we are processing a declaration, allow undefined vars
|
*/
|
*/
|
value = NULL;
|
value = NULL;
|
if (state == STATE_DEC) {
|
if (state == STATE_DEC) {
|
if (ejGetVar(ep->eid, identifier, &value) > 0) {
|
if (ejGetVar(ep->eid, identifier, &value) > 0) {
|
ejError(ep, T("Variable already declared"),
|
ejError(ep, T("Variable already declared"),
|
identifier);
|
identifier);
|
clearString(&identifier);
|
clearString(&identifier);
|
goto error;
|
goto error;
|
}
|
}
|
ejSetLocalVar(ep->eid, identifier, NULL);
|
ejSetLocalVar(ep->eid, identifier, NULL);
|
} else {
|
} else {
|
if ( flags & FLAGS_EXE ) {
|
if ( flags & FLAGS_EXE ) {
|
if (ejGetVar(ep->eid, identifier, &value) < 0) {
|
if (ejGetVar(ep->eid, identifier, &value) < 0) {
|
ejError(ep, T("Undefined variable %s\n"),
|
ejError(ep, T("Undefined variable %s\n"),
|
identifier);
|
identifier);
|
clearString(&identifier);
|
clearString(&identifier);
|
goto error;
|
goto error;
|
}
|
}
|
}
|
}
|
}
|
}
|
setString(&ep->result, value);
|
setString(&ep->result, value);
|
ejLexPutbackToken(ep, tid, ep->token);
|
ejLexPutbackToken(ep, tid, ep->token);
|
}
|
}
|
clearString(&identifier);
|
clearString(&identifier);
|
|
|
if (state == STATE_STMT) {
|
if (state == STATE_STMT) {
|
expectSemi++;
|
expectSemi++;
|
}
|
}
|
done++;
|
done++;
|
break;
|
break;
|
|
|
case TOK_LITERAL:
|
case TOK_LITERAL:
|
/*
|
/*
|
* Set the result to the literal (number or string constant)
|
* Set the result to the literal (number or string constant)
|
*/
|
*/
|
setString(&ep->result, ep->token);
|
setString(&ep->result, ep->token);
|
if (state == STATE_STMT) {
|
if (state == STATE_STMT) {
|
expectSemi++;
|
expectSemi++;
|
}
|
}
|
done++;
|
done++;
|
break;
|
break;
|
|
|
case TOK_FUNCTION:
|
case TOK_FUNCTION:
|
/*
|
/*
|
* We must save any current ep->func value for the current stack frame
|
* We must save any current ep->func value for the current stack frame
|
*/
|
*/
|
if (ep->func) {
|
if (ep->func) {
|
saveFunc = ep->func;
|
saveFunc = ep->func;
|
}
|
}
|
memset(&func, 0, sizeof(ejfunc_t));
|
memset(&func, 0, sizeof(ejfunc_t));
|
setString(&func.fname, ep->token);
|
setString(&func.fname, ep->token);
|
ep->func = &func;
|
ep->func = &func;
|
|
|
setString(&ep->result, T(""));
|
setString(&ep->result, T(""));
|
if (ejLexGetToken(ep, state) != TOK_LPAREN) {
|
if (ejLexGetToken(ep, state) != TOK_LPAREN) {
|
freeFunc(&func);
|
freeFunc(&func);
|
goto error;
|
goto error;
|
}
|
}
|
|
|
if (parse(ep, STATE_ARG_LIST, flags) != STATE_ARG_LIST_DONE) {
|
if (parse(ep, STATE_ARG_LIST, flags) != STATE_ARG_LIST_DONE) {
|
freeFunc(&func);
|
freeFunc(&func);
|
ep->func = saveFunc;
|
ep->func = saveFunc;
|
goto error;
|
goto error;
|
}
|
}
|
/*
|
/*
|
* Evaluate the function if required
|
* Evaluate the function if required
|
*/
|
*/
|
if (flags & FLAGS_EXE && evalFunction(ep) < 0) {
|
if (flags & FLAGS_EXE && evalFunction(ep) < 0) {
|
freeFunc(&func);
|
freeFunc(&func);
|
ep->func = saveFunc;
|
ep->func = saveFunc;
|
goto error;
|
goto error;
|
}
|
}
|
|
|
freeFunc(&func);
|
freeFunc(&func);
|
ep->func = saveFunc;
|
ep->func = saveFunc;
|
|
|
if (ejLexGetToken(ep, state) != TOK_RPAREN) {
|
if (ejLexGetToken(ep, state) != TOK_RPAREN) {
|
goto error;
|
goto error;
|
}
|
}
|
if (state == STATE_STMT) {
|
if (state == STATE_STMT) {
|
expectSemi++;
|
expectSemi++;
|
}
|
}
|
done++;
|
done++;
|
break;
|
break;
|
|
|
case TOK_IF:
|
case TOK_IF:
|
if (state != STATE_STMT) {
|
if (state != STATE_STMT) {
|
goto error;
|
goto error;
|
}
|
}
|
if (ejLexGetToken(ep, state) != TOK_LPAREN) {
|
if (ejLexGetToken(ep, state) != TOK_LPAREN) {
|
goto error;
|
goto error;
|
}
|
}
|
/*
|
/*
|
* Evaluate the entire condition list "(condition)"
|
* Evaluate the entire condition list "(condition)"
|
*/
|
*/
|
if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) {
|
if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) {
|
goto error;
|
goto error;
|
}
|
}
|
if (ejLexGetToken(ep, state) != TOK_RPAREN) {
|
if (ejLexGetToken(ep, state) != TOK_RPAREN) {
|
goto error;
|
goto error;
|
}
|
}
|
/*
|
/*
|
* This is the "then" case. We need to always parse both cases and
|
* This is the "then" case. We need to always parse both cases and
|
* execute only the relevant case.
|
* execute only the relevant case.
|
*/
|
*/
|
if (*ep->result == '1') {
|
if (*ep->result == '1') {
|
thenFlags = flags;
|
thenFlags = flags;
|
elseFlags = flags & ~FLAGS_EXE;
|
elseFlags = flags & ~FLAGS_EXE;
|
} else {
|
} else {
|
thenFlags = flags & ~FLAGS_EXE;
|
thenFlags = flags & ~FLAGS_EXE;
|
elseFlags = flags;
|
elseFlags = flags;
|
}
|
}
|
/*
|
/*
|
* Process the "then" case
|
* Process the "then" case
|
*/
|
*/
|
if (parse(ep, STATE_STMT, thenFlags) != STATE_STMT_DONE) {
|
if (parse(ep, STATE_STMT, thenFlags) != STATE_STMT_DONE) {
|
goto error;
|
goto error;
|
}
|
}
|
tid = ejLexGetToken(ep, state);
|
tid = ejLexGetToken(ep, state);
|
if (tid != TOK_ELSE) {
|
if (tid != TOK_ELSE) {
|
ejLexPutbackToken(ep, tid, ep->token);
|
ejLexPutbackToken(ep, tid, ep->token);
|
done++;
|
done++;
|
break;
|
break;
|
}
|
}
|
/*
|
/*
|
* Process the "else" case
|
* Process the "else" case
|
*/
|
*/
|
if (parse(ep, STATE_STMT, elseFlags) != STATE_STMT_DONE) {
|
if (parse(ep, STATE_STMT, elseFlags) != STATE_STMT_DONE) {
|
goto error;
|
goto error;
|
}
|
}
|
done++;
|
done++;
|
break;
|
break;
|
|
|
case TOK_FOR:
|
case TOK_FOR:
|
/*
|
/*
|
* Format for the expression is:
|
* Format for the expression is:
|
*
|
*
|
* for (initial; condition; incr) {
|
* for (initial; condition; incr) {
|
* body;
|
* body;
|
* }
|
* }
|
*/
|
*/
|
if (state != STATE_STMT) {
|
if (state != STATE_STMT) {
|
goto error;
|
goto error;
|
}
|
}
|
if (ejLexGetToken(ep, state) != TOK_LPAREN) {
|
if (ejLexGetToken(ep, state) != TOK_LPAREN) {
|
goto error;
|
goto error;
|
}
|
}
|
|
|
/*
|
/*
|
* Evaluate the for loop initialization statement
|
* Evaluate the for loop initialization statement
|
*/
|
*/
|
if (parse(ep, STATE_EXPR, flags) != STATE_EXPR_DONE) {
|
if (parse(ep, STATE_EXPR, flags) != STATE_EXPR_DONE) {
|
goto error;
|
goto error;
|
}
|
}
|
if (ejLexGetToken(ep, state) != TOK_SEMI) {
|
if (ejLexGetToken(ep, state) != TOK_SEMI) {
|
goto error;
|
goto error;
|
}
|
}
|
|
|
/*
|
/*
|
* The first time through, we save the current input context just
|
* The first time through, we save the current input context just
|
* to each step: prior to the conditional, the loop increment and the
|
* to each step: prior to the conditional, the loop increment and the
|
* loop body.
|
* loop body.
|
*/
|
*/
|
ejLexSaveInputState(ep, &condScript);
|
ejLexSaveInputState(ep, &condScript);
|
if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) {
|
if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) {
|
goto error;
|
goto error;
|
}
|
}
|
cond = (*ep->result != '0');
|
cond = (*ep->result != '0');
|
|
|
if (ejLexGetToken(ep, state) != TOK_SEMI) {
|
if (ejLexGetToken(ep, state) != TOK_SEMI) {
|
goto error;
|
goto error;
|
}
|
}
|
|
|
/*
|
/*
|
* Don't execute the loop increment statement or the body first time
|
* Don't execute the loop increment statement or the body first time
|
*/
|
*/
|
forFlags = flags & ~FLAGS_EXE;
|
forFlags = flags & ~FLAGS_EXE;
|
ejLexSaveInputState(ep, &incrScript);
|
ejLexSaveInputState(ep, &incrScript);
|
if (parse(ep, STATE_EXPR, forFlags) != STATE_EXPR_DONE) {
|
if (parse(ep, STATE_EXPR, forFlags) != STATE_EXPR_DONE) {
|
goto error;
|
goto error;
|
}
|
}
|
if (ejLexGetToken(ep, state) != TOK_RPAREN) {
|
if (ejLexGetToken(ep, state) != TOK_RPAREN) {
|
goto error;
|
goto error;
|
}
|
}
|
|
|
/*
|
/*
|
* Parse the body and remember the end of the body script
|
* Parse the body and remember the end of the body script
|
*/
|
*/
|
ejLexSaveInputState(ep, &bodyScript);
|
ejLexSaveInputState(ep, &bodyScript);
|
if (parse(ep, STATE_STMT, forFlags) != STATE_STMT_DONE) {
|
if (parse(ep, STATE_STMT, forFlags) != STATE_STMT_DONE) {
|
goto error;
|
goto error;
|
}
|
}
|
ejLexSaveInputState(ep, &endScript);
|
ejLexSaveInputState(ep, &endScript);
|
|
|
/*
|
/*
|
* Now actually do the for loop. Note loop has been rotated
|
* Now actually do the for loop. Note loop has been rotated
|
*/
|
*/
|
while (cond && (flags & FLAGS_EXE) ) {
|
while (cond && (flags & FLAGS_EXE) ) {
|
/*
|
/*
|
* Evaluate the body
|
* Evaluate the body
|
*/
|
*/
|
ejLexRestoreInputState(ep, &bodyScript);
|
ejLexRestoreInputState(ep, &bodyScript);
|
if (parse(ep, STATE_STMT, flags) != STATE_STMT_DONE) {
|
if (parse(ep, STATE_STMT, flags) != STATE_STMT_DONE) {
|
goto error;
|
goto error;
|
}
|
}
|
/*
|
/*
|
* Evaluate the increment script
|
* Evaluate the increment script
|
*/
|
*/
|
ejLexRestoreInputState(ep, &incrScript);
|
ejLexRestoreInputState(ep, &incrScript);
|
if (parse(ep, STATE_EXPR, flags) != STATE_EXPR_DONE) {
|
if (parse(ep, STATE_EXPR, flags) != STATE_EXPR_DONE) {
|
goto error;
|
goto error;
|
}
|
}
|
/*
|
/*
|
* Evaluate the condition
|
* Evaluate the condition
|
*/
|
*/
|
ejLexRestoreInputState(ep, &condScript);
|
ejLexRestoreInputState(ep, &condScript);
|
if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) {
|
if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) {
|
goto error;
|
goto error;
|
}
|
}
|
cond = (*ep->result != '0');
|
cond = (*ep->result != '0');
|
}
|
}
|
ejLexRestoreInputState(ep, &endScript);
|
ejLexRestoreInputState(ep, &endScript);
|
done++;
|
done++;
|
break;
|
break;
|
|
|
case TOK_VAR:
|
case TOK_VAR:
|
if (parse(ep, STATE_DEC_LIST, flags) != STATE_DEC_LIST_DONE) {
|
if (parse(ep, STATE_DEC_LIST, flags) != STATE_DEC_LIST_DONE) {
|
goto error;
|
goto error;
|
}
|
}
|
done++;
|
done++;
|
break;
|
break;
|
|
|
case TOK_COMMA:
|
case TOK_COMMA:
|
ejLexPutbackToken(ep, TOK_EXPR, ep->token);
|
ejLexPutbackToken(ep, TOK_EXPR, ep->token);
|
done++;
|
done++;
|
break;
|
break;
|
|
|
case TOK_LPAREN:
|
case TOK_LPAREN:
|
if (state == STATE_EXPR) {
|
if (state == STATE_EXPR) {
|
if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) {
|
if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) {
|
goto error;
|
goto error;
|
}
|
}
|
if (ejLexGetToken(ep, state) != TOK_RPAREN) {
|
if (ejLexGetToken(ep, state) != TOK_RPAREN) {
|
goto error;
|
goto error;
|
}
|
}
|
return STATE_EXPR_DONE;
|
return STATE_EXPR_DONE;
|
}
|
}
|
done++;
|
done++;
|
break;
|
break;
|
|
|
case TOK_RPAREN:
|
case TOK_RPAREN:
|
ejLexPutbackToken(ep, tid, ep->token);
|
ejLexPutbackToken(ep, tid, ep->token);
|
return STATE_EXPR_DONE;
|
return STATE_EXPR_DONE;
|
|
|
case TOK_LBRACE:
|
case TOK_LBRACE:
|
/*
|
/*
|
* This handles any code in braces except "if () {} else {}"
|
* This handles any code in braces except "if () {} else {}"
|
*/
|
*/
|
if (state != STATE_STMT) {
|
if (state != STATE_STMT) {
|
goto error;
|
goto error;
|
}
|
}
|
|
|
/*
|
/*
|
* Parse will return STATE_STMT_BLOCK_DONE when the RBRACE is seen
|
* Parse will return STATE_STMT_BLOCK_DONE when the RBRACE is seen
|
*/
|
*/
|
do {
|
do {
|
state = parse(ep, STATE_STMT, flags);
|
state = parse(ep, STATE_STMT, flags);
|
} while (state == STATE_STMT_DONE);
|
} while (state == STATE_STMT_DONE);
|
|
|
if (ejLexGetToken(ep, state) != TOK_RBRACE) {
|
if (ejLexGetToken(ep, state) != TOK_RBRACE) {
|
goto error;
|
goto error;
|
}
|
}
|
return STATE_STMT_DONE;
|
return STATE_STMT_DONE;
|
|
|
case TOK_RBRACE:
|
case TOK_RBRACE:
|
if (state == STATE_STMT) {
|
if (state == STATE_STMT) {
|
ejLexPutbackToken(ep, tid, ep->token);
|
ejLexPutbackToken(ep, tid, ep->token);
|
return STATE_STMT_BLOCK_DONE;
|
return STATE_STMT_BLOCK_DONE;
|
}
|
}
|
goto error;
|
goto error;
|
|
|
case TOK_RETURN:
|
case TOK_RETURN:
|
if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) {
|
if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) {
|
goto error;
|
goto error;
|
}
|
}
|
if (flags & FLAGS_EXE) {
|
if (flags & FLAGS_EXE) {
|
while ( ejLexGetToken(ep, state) != TOK_EOF );
|
while ( ejLexGetToken(ep, state) != TOK_EOF );
|
done++;
|
done++;
|
return STATE_EOF;
|
return STATE_EOF;
|
}
|
}
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|
if (expectSemi) {
|
if (expectSemi) {
|
tid = ejLexGetToken(ep, state);
|
tid = ejLexGetToken(ep, state);
|
if (tid != TOK_SEMI && tid != TOK_NEWLINE) {
|
if (tid != TOK_SEMI && tid != TOK_NEWLINE) {
|
goto error;
|
goto error;
|
}
|
}
|
|
|
/*
|
/*
|
* Skip newline after semi-colon
|
* Skip newline after semi-colon
|
*/
|
*/
|
tid = ejLexGetToken(ep, state);
|
tid = ejLexGetToken(ep, state);
|
if (tid != TOK_NEWLINE) {
|
if (tid != TOK_NEWLINE) {
|
ejLexPutbackToken(ep, tid, ep->token);
|
ejLexPutbackToken(ep, tid, ep->token);
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
* Free resources and return the correct status
|
* Free resources and return the correct status
|
*/
|
*/
|
doneParse:
|
doneParse:
|
if (tid == TOK_FOR) {
|
if (tid == TOK_FOR) {
|
ejLexFreeInputState(ep, &condScript);
|
ejLexFreeInputState(ep, &condScript);
|
ejLexFreeInputState(ep, &incrScript);
|
ejLexFreeInputState(ep, &incrScript);
|
ejLexFreeInputState(ep, &endScript);
|
ejLexFreeInputState(ep, &endScript);
|
ejLexFreeInputState(ep, &bodyScript);
|
ejLexFreeInputState(ep, &bodyScript);
|
}
|
}
|
if (state == STATE_STMT) {
|
if (state == STATE_STMT) {
|
return STATE_STMT_DONE;
|
return STATE_STMT_DONE;
|
} else if (state == STATE_DEC) {
|
} else if (state == STATE_DEC) {
|
return STATE_DEC_DONE;
|
return STATE_DEC_DONE;
|
} else if (state == STATE_EXPR) {
|
} else if (state == STATE_EXPR) {
|
return STATE_EXPR_DONE;
|
return STATE_EXPR_DONE;
|
} else if (state == STATE_EOF) {
|
} else if (state == STATE_EOF) {
|
return state;
|
return state;
|
} else {
|
} else {
|
return STATE_ERR;
|
return STATE_ERR;
|
}
|
}
|
|
|
/*
|
/*
|
* Common error exit
|
* Common error exit
|
*/
|
*/
|
error:
|
error:
|
state = STATE_ERR;
|
state = STATE_ERR;
|
goto doneParse;
|
goto doneParse;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Parse variable declaration list
|
* Parse variable declaration list
|
*/
|
*/
|
|
|
static int parseDeclaration(ej_t *ep, int state, int flags)
|
static int parseDeclaration(ej_t *ep, int state, int flags)
|
{
|
{
|
int tid;
|
int tid;
|
|
|
a_assert(ep);
|
a_assert(ep);
|
|
|
/*
|
/*
|
* Declarations can be of the following forms:
|
* Declarations can be of the following forms:
|
* var x;
|
* var x;
|
* var x, y, z;
|
* var x, y, z;
|
* var x = 1 + 2 / 3, y = 2 + 4;
|
* var x = 1 + 2 / 3, y = 2 + 4;
|
*
|
*
|
* We set the variable to NULL if there is no associated assignment.
|
* We set the variable to NULL if there is no associated assignment.
|
*/
|
*/
|
|
|
do {
|
do {
|
if ((tid = ejLexGetToken(ep, state)) != TOK_ID) {
|
if ((tid = ejLexGetToken(ep, state)) != TOK_ID) {
|
return STATE_ERR;
|
return STATE_ERR;
|
}
|
}
|
ejLexPutbackToken(ep, tid, ep->token);
|
ejLexPutbackToken(ep, tid, ep->token);
|
|
|
/*
|
/*
|
* Parse the entire assignment or simple identifier declaration
|
* Parse the entire assignment or simple identifier declaration
|
*/
|
*/
|
if (parse(ep, STATE_DEC, flags) != STATE_DEC_DONE) {
|
if (parse(ep, STATE_DEC, flags) != STATE_DEC_DONE) {
|
return STATE_ERR;
|
return STATE_ERR;
|
}
|
}
|
|
|
/*
|
/*
|
* Peek at the next token, continue if comma seen
|
* Peek at the next token, continue if comma seen
|
*/
|
*/
|
tid = ejLexGetToken(ep, state);
|
tid = ejLexGetToken(ep, state);
|
if (tid == TOK_SEMI) {
|
if (tid == TOK_SEMI) {
|
return STATE_DEC_LIST_DONE;
|
return STATE_DEC_LIST_DONE;
|
} else if (tid != TOK_COMMA) {
|
} else if (tid != TOK_COMMA) {
|
return STATE_ERR;
|
return STATE_ERR;
|
}
|
}
|
} while (tid == TOK_COMMA);
|
} while (tid == TOK_COMMA);
|
|
|
if (tid != TOK_SEMI) {
|
if (tid != TOK_SEMI) {
|
return STATE_ERR;
|
return STATE_ERR;
|
}
|
}
|
return STATE_DEC_LIST_DONE;
|
return STATE_DEC_LIST_DONE;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Parse function arguments
|
* Parse function arguments
|
*/
|
*/
|
|
|
static int parseArgs(ej_t *ep, int state, int flags)
|
static int parseArgs(ej_t *ep, int state, int flags)
|
{
|
{
|
int tid, aid;
|
int tid, aid;
|
|
|
a_assert(ep);
|
a_assert(ep);
|
|
|
do {
|
do {
|
state = parse(ep, STATE_RELEXP, flags);
|
state = parse(ep, STATE_RELEXP, flags);
|
if (state == STATE_EOF || state == STATE_ERR) {
|
if (state == STATE_EOF || state == STATE_ERR) {
|
return state;
|
return state;
|
}
|
}
|
if (state == STATE_RELEXP_DONE) {
|
if (state == STATE_RELEXP_DONE) {
|
aid = hAlloc((void***) &ep->func->args);
|
aid = hAlloc((void***) &ep->func->args);
|
ep->func->args[aid] = bstrdup(B_L, ep->result);
|
ep->func->args[aid] = bstrdup(B_L, ep->result);
|
ep->func->nArgs++;
|
ep->func->nArgs++;
|
}
|
}
|
/*
|
/*
|
* Peek at the next token, continue if more args (ie. comma seen)
|
* Peek at the next token, continue if more args (ie. comma seen)
|
*/
|
*/
|
tid = ejLexGetToken(ep, state);
|
tid = ejLexGetToken(ep, state);
|
if (tid != TOK_COMMA) {
|
if (tid != TOK_COMMA) {
|
ejLexPutbackToken(ep, tid, ep->token);
|
ejLexPutbackToken(ep, tid, ep->token);
|
}
|
}
|
} while (tid == TOK_COMMA);
|
} while (tid == TOK_COMMA);
|
|
|
if (tid != TOK_RPAREN && state != STATE_RELEXP_DONE) {
|
if (tid != TOK_RPAREN && state != STATE_RELEXP_DONE) {
|
return STATE_ERR;
|
return STATE_ERR;
|
}
|
}
|
return STATE_ARG_LIST_DONE;
|
return STATE_ARG_LIST_DONE;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Parse conditional expression (relational ops separated by ||, &&)
|
* Parse conditional expression (relational ops separated by ||, &&)
|
*/
|
*/
|
|
|
static int parseCond(ej_t *ep, int state, int flags)
|
static int parseCond(ej_t *ep, int state, int flags)
|
{
|
{
|
char_t *lhs, *rhs;
|
char_t *lhs, *rhs;
|
int tid, operator;
|
int tid, operator;
|
|
|
a_assert(ep);
|
a_assert(ep);
|
|
|
setString(&ep->result, T(""));
|
setString(&ep->result, T(""));
|
rhs = lhs = NULL;
|
rhs = lhs = NULL;
|
operator = 0;
|
operator = 0;
|
|
|
do {
|
do {
|
/*
|
/*
|
* Recurse to handle one side of a conditional. Accumulate the
|
* Recurse to handle one side of a conditional. Accumulate the
|
* left hand side and the final result in ep->result.
|
* left hand side and the final result in ep->result.
|
*/
|
*/
|
state = parse(ep, STATE_RELEXP, flags);
|
state = parse(ep, STATE_RELEXP, flags);
|
if (state != STATE_RELEXP_DONE) {
|
if (state != STATE_RELEXP_DONE) {
|
state = STATE_ERR;
|
state = STATE_ERR;
|
break;
|
break;
|
}
|
}
|
if (operator > 0) {
|
if (operator > 0) {
|
setString(&rhs, ep->result);
|
setString(&rhs, ep->result);
|
if (evalCond(ep, lhs, operator, rhs) < 0) {
|
if (evalCond(ep, lhs, operator, rhs) < 0) {
|
state = STATE_ERR;
|
state = STATE_ERR;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
setString(&lhs, ep->result);
|
setString(&lhs, ep->result);
|
|
|
tid = ejLexGetToken(ep, state);
|
tid = ejLexGetToken(ep, state);
|
if (tid == TOK_LOGICAL) {
|
if (tid == TOK_LOGICAL) {
|
operator = (int) *ep->token;
|
operator = (int) *ep->token;
|
|
|
} else if (tid == TOK_RPAREN || tid == TOK_SEMI) {
|
} else if (tid == TOK_RPAREN || tid == TOK_SEMI) {
|
ejLexPutbackToken(ep, tid, ep->token);
|
ejLexPutbackToken(ep, tid, ep->token);
|
state = STATE_COND_DONE;
|
state = STATE_COND_DONE;
|
break;
|
break;
|
|
|
} else {
|
} else {
|
ejLexPutbackToken(ep, tid, ep->token);
|
ejLexPutbackToken(ep, tid, ep->token);
|
}
|
}
|
|
|
} while (state == STATE_RELEXP_DONE);
|
} while (state == STATE_RELEXP_DONE);
|
|
|
if (lhs) {
|
if (lhs) {
|
bfree(B_L, lhs);
|
bfree(B_L, lhs);
|
}
|
}
|
if (rhs) {
|
if (rhs) {
|
bfree(B_L, rhs);
|
bfree(B_L, rhs);
|
}
|
}
|
return state;
|
return state;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Parse expression (leftHandSide operator rightHandSide)
|
* Parse expression (leftHandSide operator rightHandSide)
|
*/
|
*/
|
|
|
static int parseExpr(ej_t *ep, int state, int flags)
|
static int parseExpr(ej_t *ep, int state, int flags)
|
{
|
{
|
char_t *lhs, *rhs;
|
char_t *lhs, *rhs;
|
int rel, tid;
|
int rel, tid;
|
|
|
a_assert(ep);
|
a_assert(ep);
|
|
|
setString(&ep->result, T(""));
|
setString(&ep->result, T(""));
|
rhs = lhs = NULL;
|
rhs = lhs = NULL;
|
rel = 0;
|
rel = 0;
|
|
|
do {
|
do {
|
/*
|
/*
|
* This loop will handle an entire expression list. We call parse
|
* This loop will handle an entire expression list. We call parse
|
* to evalutate each term which returns the result in ep->result.
|
* to evalutate each term which returns the result in ep->result.
|
*/
|
*/
|
state = parse(ep, STATE_EXPR, flags);
|
state = parse(ep, STATE_EXPR, flags);
|
if (state != STATE_EXPR_DONE) {
|
if (state != STATE_EXPR_DONE) {
|
state = STATE_ERR;
|
state = STATE_ERR;
|
break;
|
break;
|
}
|
}
|
if (rel > 0) {
|
if (rel > 0) {
|
setString(&rhs, ep->result);
|
setString(&rhs, ep->result);
|
if (evalExpr(ep, lhs, rel, rhs) < 0) {
|
if (evalExpr(ep, lhs, rel, rhs) < 0) {
|
state = STATE_ERR;
|
state = STATE_ERR;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
setString(&lhs, ep->result);
|
setString(&lhs, ep->result);
|
|
|
if ((tid = ejLexGetToken(ep, state)) == TOK_EXPR) {
|
if ((tid = ejLexGetToken(ep, state)) == TOK_EXPR) {
|
rel = (int) *ep->token;
|
rel = (int) *ep->token;
|
|
|
} else if (tid == TOK_INC_DEC) {
|
} else if (tid == TOK_INC_DEC) {
|
rel = (int) *ep->token;
|
rel = (int) *ep->token;
|
|
|
} else {
|
} else {
|
ejLexPutbackToken(ep, tid, ep->token);
|
ejLexPutbackToken(ep, tid, ep->token);
|
state = STATE_RELEXP_DONE;
|
state = STATE_RELEXP_DONE;
|
}
|
}
|
|
|
} while (state == STATE_EXPR_DONE);
|
} while (state == STATE_EXPR_DONE);
|
|
|
if (rhs)
|
if (rhs)
|
bfree(B_L, rhs);
|
bfree(B_L, rhs);
|
if (lhs)
|
if (lhs)
|
bfree(B_L, lhs);
|
bfree(B_L, lhs);
|
return state;
|
return state;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Evaluate a condition. Implements &&, ||, !
|
* Evaluate a condition. Implements &&, ||, !
|
*/
|
*/
|
|
|
static int evalCond(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)
|
{
|
{
|
char_t buf[16];
|
char_t buf[16];
|
int l, r, lval;
|
int l, r, lval;
|
|
|
a_assert(lhs);
|
a_assert(lhs);
|
a_assert(rhs);
|
a_assert(rhs);
|
a_assert(rel > 0);
|
a_assert(rel > 0);
|
|
|
lval = 0;
|
lval = 0;
|
if (gisdigit(*lhs) && gisdigit(*rhs)) {
|
if (gisdigit(*lhs) && gisdigit(*rhs)) {
|
l = gatoi(lhs);
|
l = gatoi(lhs);
|
r = gatoi(rhs);
|
r = gatoi(rhs);
|
switch (rel) {
|
switch (rel) {
|
case COND_AND:
|
case COND_AND:
|
lval = l && r;
|
lval = l && r;
|
break;
|
break;
|
case COND_OR:
|
case COND_OR:
|
lval = l || r;
|
lval = l || r;
|
break;
|
break;
|
default:
|
default:
|
ejError(ep, T("Bad operator %d"), rel);
|
ejError(ep, T("Bad operator %d"), rel);
|
return -1;
|
return -1;
|
}
|
}
|
} else {
|
} else {
|
if (!gisdigit(*lhs)) {
|
if (!gisdigit(*lhs)) {
|
ejError(ep, T("Conditional must be numeric"), lhs);
|
ejError(ep, T("Conditional must be numeric"), lhs);
|
} else {
|
} else {
|
ejError(ep, T("Conditional must be numeric"), rhs);
|
ejError(ep, T("Conditional must be numeric"), rhs);
|
}
|
}
|
}
|
}
|
|
|
stritoa(lval, buf, sizeof(buf));
|
stritoa(lval, buf, sizeof(buf));
|
setString(&ep->result, buf);
|
setString(&ep->result, buf);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Evaluate an operation
|
* Evaluate an operation
|
*/
|
*/
|
|
|
static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs)
|
static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs)
|
{
|
{
|
char_t *cp, buf[16];
|
char_t *cp, buf[16];
|
int numeric, l, r, lval;
|
int numeric, l, r, lval;
|
|
|
a_assert(lhs);
|
a_assert(lhs);
|
a_assert(rhs);
|
a_assert(rhs);
|
a_assert(rel > 0);
|
a_assert(rel > 0);
|
|
|
/*
|
/*
|
* All of the characters in the lhs and rhs must be numeric
|
* All of the characters in the lhs and rhs must be numeric
|
*/
|
*/
|
numeric = 1;
|
numeric = 1;
|
for (cp = lhs; *cp; cp++) {
|
for (cp = lhs; *cp; cp++) {
|
if (!gisdigit(*cp)) {
|
if (!gisdigit(*cp)) {
|
numeric = 0;
|
numeric = 0;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
if (numeric) {
|
if (numeric) {
|
for (cp = rhs; *cp; cp++) {
|
for (cp = rhs; *cp; cp++) {
|
if (!gisdigit(*cp)) {
|
if (!gisdigit(*cp)) {
|
numeric = 0;
|
numeric = 0;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
}
|
}
|
if (numeric) {
|
if (numeric) {
|
l = gatoi(lhs);
|
l = gatoi(lhs);
|
r = gatoi(rhs);
|
r = gatoi(rhs);
|
switch (rel) {
|
switch (rel) {
|
case EXPR_PLUS:
|
case EXPR_PLUS:
|
lval = l + r;
|
lval = l + r;
|
break;
|
break;
|
case EXPR_INC:
|
case EXPR_INC:
|
lval = l + 1;
|
lval = l + 1;
|
break;
|
break;
|
case EXPR_MINUS:
|
case EXPR_MINUS:
|
lval = l - r;
|
lval = l - r;
|
break;
|
break;
|
case EXPR_DEC:
|
case EXPR_DEC:
|
lval = l - 1;
|
lval = l - 1;
|
break;
|
break;
|
case EXPR_MUL:
|
case EXPR_MUL:
|
lval = l * r;
|
lval = l * r;
|
break;
|
break;
|
case EXPR_DIV:
|
case EXPR_DIV:
|
if (r != 0) {
|
if (r != 0) {
|
lval = l / r;
|
lval = l / r;
|
} else {
|
} else {
|
lval = 0;
|
lval = 0;
|
}
|
}
|
break;
|
break;
|
case EXPR_MOD:
|
case EXPR_MOD:
|
if (r != 0) {
|
if (r != 0) {
|
lval = l % r;
|
lval = l % r;
|
} else {
|
} else {
|
lval = 0;
|
lval = 0;
|
}
|
}
|
break;
|
break;
|
case EXPR_LSHIFT:
|
case EXPR_LSHIFT:
|
lval = l << r;
|
lval = l << r;
|
break;
|
break;
|
case EXPR_RSHIFT:
|
case EXPR_RSHIFT:
|
lval = l >> r;
|
lval = l >> r;
|
break;
|
break;
|
case EXPR_EQ:
|
case EXPR_EQ:
|
lval = l == r;
|
lval = l == r;
|
break;
|
break;
|
case EXPR_NOTEQ:
|
case EXPR_NOTEQ:
|
lval = l != r;
|
lval = l != r;
|
break;
|
break;
|
case EXPR_LESS:
|
case EXPR_LESS:
|
lval = (l < r) ? 1 : 0;
|
lval = (l < r) ? 1 : 0;
|
break;
|
break;
|
case EXPR_LESSEQ:
|
case EXPR_LESSEQ:
|
lval = (l <= r) ? 1 : 0;
|
lval = (l <= r) ? 1 : 0;
|
break;
|
break;
|
case EXPR_GREATER:
|
case EXPR_GREATER:
|
lval = (l > r) ? 1 : 0;
|
lval = (l > r) ? 1 : 0;
|
break;
|
break;
|
case EXPR_GREATEREQ:
|
case EXPR_GREATEREQ:
|
lval = (l >= r) ? 1 : 0;
|
lval = (l >= r) ? 1 : 0;
|
break;
|
break;
|
default:
|
default:
|
ejError(ep, T("Bad operator %d"), rel);
|
ejError(ep, T("Bad operator %d"), rel);
|
return -1;
|
return -1;
|
}
|
}
|
|
|
} else {
|
} else {
|
switch (rel) {
|
switch (rel) {
|
case EXPR_PLUS:
|
case EXPR_PLUS:
|
clearString(&ep->result);
|
clearString(&ep->result);
|
appendString(&ep->result, lhs);
|
appendString(&ep->result, lhs);
|
appendString(&ep->result, rhs);
|
appendString(&ep->result, rhs);
|
return 0;
|
return 0;
|
case EXPR_LESS:
|
case EXPR_LESS:
|
lval = gstrcmp(lhs, rhs) < 0;
|
lval = gstrcmp(lhs, rhs) < 0;
|
break;
|
break;
|
case EXPR_LESSEQ:
|
case EXPR_LESSEQ:
|
lval = gstrcmp(lhs, rhs) <= 0;
|
lval = gstrcmp(lhs, rhs) <= 0;
|
break;
|
break;
|
case EXPR_GREATER:
|
case EXPR_GREATER:
|
lval = gstrcmp(lhs, rhs) > 0;
|
lval = gstrcmp(lhs, rhs) > 0;
|
break;
|
break;
|
case EXPR_GREATEREQ:
|
case EXPR_GREATEREQ:
|
lval = gstrcmp(lhs, rhs) >= 0;
|
lval = gstrcmp(lhs, rhs) >= 0;
|
break;
|
break;
|
case EXPR_EQ:
|
case EXPR_EQ:
|
lval = gstrcmp(lhs, rhs) == 0;
|
lval = gstrcmp(lhs, rhs) == 0;
|
break;
|
break;
|
case EXPR_NOTEQ:
|
case EXPR_NOTEQ:
|
lval = gstrcmp(lhs, rhs) != 0;
|
lval = gstrcmp(lhs, rhs) != 0;
|
break;
|
break;
|
case EXPR_INC:
|
case EXPR_INC:
|
case EXPR_DEC:
|
case EXPR_DEC:
|
case EXPR_MINUS:
|
case EXPR_MINUS:
|
case EXPR_DIV:
|
case EXPR_DIV:
|
case EXPR_MOD:
|
case EXPR_MOD:
|
case EXPR_LSHIFT:
|
case EXPR_LSHIFT:
|
case EXPR_RSHIFT:
|
case EXPR_RSHIFT:
|
default:
|
default:
|
ejError(ep, T("Bad operator"));
|
ejError(ep, T("Bad operator"));
|
return -1;
|
return -1;
|
}
|
}
|
}
|
}
|
|
|
stritoa(lval, buf, sizeof(buf));
|
stritoa(lval, buf, sizeof(buf));
|
setString(&ep->result, buf);
|
setString(&ep->result, buf);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Evaluate a function
|
* Evaluate a function
|
*/
|
*/
|
|
|
static int evalFunction(ej_t *ep)
|
static int evalFunction(ej_t *ep)
|
{
|
{
|
sym_t *sp;
|
sym_t *sp;
|
int (*fn)(int eid, void *handle, int argc, char_t **argv);
|
int (*fn)(int eid, void *handle, int argc, char_t **argv);
|
|
|
if ((sp = symLookup(ep->functions, ep->func->fname)) == NULL) {
|
if ((sp = symLookup(ep->functions, ep->func->fname)) == NULL) {
|
ejError(ep, T("Undefined procedure %s"), ep->func->fname);
|
ejError(ep, T("Undefined procedure %s"), ep->func->fname);
|
return -1;
|
return -1;
|
}
|
}
|
fn = (int (*)(int, void*, int, char_t**)) sp->content.value.integer;
|
fn = (int (*)(int, void*, int, char_t**)) sp->content.value.integer;
|
if (fn == NULL) {
|
if (fn == NULL) {
|
ejError(ep, T("Undefined procedure %s"), ep->func->fname);
|
ejError(ep, T("Undefined procedure %s"), ep->func->fname);
|
return -1;
|
return -1;
|
}
|
}
|
|
|
return (*fn)(ep->eid, (void*) ep->userHandle, ep->func->nArgs,
|
return (*fn)(ep->eid, (void*) ep->userHandle, ep->func->nArgs,
|
ep->func->args);
|
ep->func->args);
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Output a parse ej_error message
|
* Output a parse ej_error message
|
*/
|
*/
|
|
|
void ejError(ej_t* ep, char_t* fmt, ...)
|
void ejError(ej_t* ep, char_t* fmt, ...)
|
{
|
{
|
va_list args;
|
va_list args;
|
ejinput_t *ip;
|
ejinput_t *ip;
|
char_t *errbuf, *msgbuf;
|
char_t *errbuf, *msgbuf;
|
|
|
a_assert(ep);
|
a_assert(ep);
|
a_assert(fmt);
|
a_assert(fmt);
|
ip = ep->input;
|
ip = ep->input;
|
|
|
va_start(args, fmt);
|
va_start(args, fmt);
|
msgbuf = NULL;
|
msgbuf = NULL;
|
gvsnprintf(&msgbuf, E_MAX_ERROR, fmt, args);
|
gvsnprintf(&msgbuf, E_MAX_ERROR, fmt, args);
|
va_end(args);
|
va_end(args);
|
|
|
if (ep && ip) {
|
if (ep && ip) {
|
errbuf = NULL;
|
errbuf = NULL;
|
gsnprintf(&errbuf, E_MAX_ERROR, T("%s\n At line %d, line => \n\n%s\n"),
|
gsnprintf(&errbuf, E_MAX_ERROR, T("%s\n At line %d, line => \n\n%s\n"),
|
msgbuf, ip->lineNumber, ip->line);
|
msgbuf, ip->lineNumber, ip->line);
|
bfreeSafe(B_L, ep->error);
|
bfreeSafe(B_L, ep->error);
|
ep->error = errbuf;
|
ep->error = errbuf;
|
}
|
}
|
bfreeSafe(B_L, msgbuf);
|
bfreeSafe(B_L, msgbuf);
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Clear a string value
|
* Clear a string value
|
*/
|
*/
|
|
|
static void clearString(char_t **ptr)
|
static void clearString(char_t **ptr)
|
{
|
{
|
a_assert(ptr);
|
a_assert(ptr);
|
|
|
if (*ptr) {
|
if (*ptr) {
|
bfree(B_L, *ptr);
|
bfree(B_L, *ptr);
|
}
|
}
|
*ptr = NULL;
|
*ptr = NULL;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Set a string value
|
* Set a string value
|
*/
|
*/
|
|
|
static void setString(char_t **ptr, char_t *s)
|
static void setString(char_t **ptr, char_t *s)
|
{
|
{
|
a_assert(ptr);
|
a_assert(ptr);
|
|
|
if (*ptr) {
|
if (*ptr) {
|
bfree(B_L, *ptr);
|
bfree(B_L, *ptr);
|
}
|
}
|
*ptr = bstrdup(B_L, s);
|
*ptr = bstrdup(B_L, s);
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Append to the pointer value
|
* Append to the pointer value
|
*/
|
*/
|
|
|
static void appendString(char_t **ptr, char_t *s)
|
static void appendString(char_t **ptr, char_t *s)
|
{
|
{
|
int len, oldlen;
|
int len, oldlen;
|
|
|
a_assert(ptr);
|
a_assert(ptr);
|
|
|
if (*ptr) {
|
if (*ptr) {
|
len = gstrlen(s);
|
len = gstrlen(s);
|
oldlen = gstrlen(*ptr);
|
oldlen = gstrlen(*ptr);
|
*ptr = brealloc(B_L, *ptr, (len + oldlen + 1) * sizeof(char_t));
|
*ptr = brealloc(B_L, *ptr, (len + oldlen + 1) * sizeof(char_t));
|
gstrcpy(&(*ptr)[oldlen], s);
|
gstrcpy(&(*ptr)[oldlen], s);
|
} else {
|
} else {
|
*ptr = bstrdup(B_L, s);
|
*ptr = bstrdup(B_L, s);
|
}
|
}
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Define a function
|
* Define a function
|
*/
|
*/
|
|
|
int ejSetGlobalFunction(int eid, char_t *name,
|
int ejSetGlobalFunction(int eid, char_t *name,
|
int (*fn)(int eid, void *handle, int argc, char_t **argv))
|
int (*fn)(int eid, void *handle, int argc, char_t **argv))
|
{
|
{
|
ej_t *ep;
|
ej_t *ep;
|
|
|
if ((ep = ejPtr(eid)) == NULL) {
|
if ((ep = ejPtr(eid)) == NULL) {
|
return -1;
|
return -1;
|
}
|
}
|
return ejSetGlobalFunctionDirect(ep->functions, name, fn);
|
return ejSetGlobalFunctionDirect(ep->functions, name, fn);
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Define a function directly into the function symbol table.
|
* Define a function directly into the function symbol table.
|
*/
|
*/
|
|
|
int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name,
|
int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name,
|
int (*fn)(int eid, void *handle, int argc, char_t **argv))
|
int (*fn)(int eid, void *handle, int argc, char_t **argv))
|
{
|
{
|
if (symEnter(functions, name, valueInteger((long) fn), 0) == NULL) {
|
if (symEnter(functions, name, valueInteger((long) fn), 0) == NULL) {
|
return -1;
|
return -1;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Get a function definition
|
* Get a function definition
|
*/
|
*/
|
|
|
void *ejGetGlobalFunction(int eid, char_t *name)
|
void *ejGetGlobalFunction(int eid, char_t *name)
|
{
|
{
|
ej_t *ep;
|
ej_t *ep;
|
sym_t *sp;
|
sym_t *sp;
|
int (*fn)(int eid, void *handle, int argc, char_t **argv);
|
int (*fn)(int eid, void *handle, int argc, char_t **argv);
|
|
|
if ((ep = ejPtr(eid)) == NULL) {
|
if ((ep = ejPtr(eid)) == NULL) {
|
return NULL;
|
return NULL;
|
}
|
}
|
if ((sp = symLookup(ep->functions, name)) != NULL) {
|
if ((sp = symLookup(ep->functions, name)) != NULL) {
|
fn = (int (*)(int, void*, int, char_t**)) sp->content.value.integer;
|
fn = (int (*)(int, void*, int, char_t**)) sp->content.value.integer;
|
return (void*) fn;
|
return (void*) fn;
|
}
|
}
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Utility routine to crack Ejscript arguments. Return the number of args
|
* Utility routine to crack Ejscript arguments. Return the number of args
|
* seen. This routine only supports %s and %d type args.
|
* seen. This routine only supports %s and %d type args.
|
*
|
*
|
* Typical usage:
|
* Typical usage:
|
*
|
*
|
* if (ejArgs(argc, argv, "%s %d", &name, &age) < 2) {
|
* if (ejArgs(argc, argv, "%s %d", &name, &age) < 2) {
|
* error("Insufficient args\n");
|
* error("Insufficient args\n");
|
* return -1;
|
* return -1;
|
* }
|
* }
|
*/
|
*/
|
|
|
int ejArgs(int argc, char_t **argv, char_t *fmt, ...)
|
int ejArgs(int argc, char_t **argv, char_t *fmt, ...)
|
{
|
{
|
va_list vargs;
|
va_list vargs;
|
char_t *cp, **sp;
|
char_t *cp, **sp;
|
int *ip;
|
int *ip;
|
int argn;
|
int argn;
|
|
|
va_start(vargs, fmt);
|
va_start(vargs, fmt);
|
|
|
if (argv == NULL) {
|
if (argv == NULL) {
|
return 0;
|
return 0;
|
}
|
}
|
|
|
for (argn = 0, cp = fmt; cp && *cp && argv[argn]; ) {
|
for (argn = 0, cp = fmt; cp && *cp && argv[argn]; ) {
|
if (*cp++ != '%') {
|
if (*cp++ != '%') {
|
continue;
|
continue;
|
}
|
}
|
|
|
switch (*cp) {
|
switch (*cp) {
|
case 'd':
|
case 'd':
|
ip = va_arg(vargs, int*);
|
ip = va_arg(vargs, int*);
|
*ip = gatoi(argv[argn]);
|
*ip = gatoi(argv[argn]);
|
break;
|
break;
|
|
|
case 's':
|
case 's':
|
sp = va_arg(vargs, char_t**);
|
sp = va_arg(vargs, char_t**);
|
*sp = argv[argn];
|
*sp = argv[argn];
|
break;
|
break;
|
|
|
default:
|
default:
|
/*
|
/*
|
* Unsupported
|
* Unsupported
|
*/
|
*/
|
a_assert(0);
|
a_assert(0);
|
}
|
}
|
argn++;
|
argn++;
|
}
|
}
|
|
|
va_end(vargs);
|
va_end(vargs);
|
return argn;
|
return argn;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Define the user handle
|
* Define the user handle
|
*/
|
*/
|
|
|
void ejSetUserHandle(int eid, int handle)
|
void ejSetUserHandle(int eid, int handle)
|
{
|
{
|
ej_t *ep;
|
ej_t *ep;
|
|
|
if ((ep = ejPtr(eid)) == NULL) {
|
if ((ep = ejPtr(eid)) == NULL) {
|
return;
|
return;
|
}
|
}
|
ep->userHandle = handle;
|
ep->userHandle = handle;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Get the user handle
|
* Get the user handle
|
*/
|
*/
|
|
|
int ejGetUserHandle(int eid)
|
int ejGetUserHandle(int eid)
|
{
|
{
|
ej_t *ep;
|
ej_t *ep;
|
|
|
if ((ep = ejPtr(eid)) == NULL) {
|
if ((ep = ejPtr(eid)) == NULL) {
|
return -1;
|
return -1;
|
}
|
}
|
return ep->userHandle;
|
return ep->userHandle;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Get the current line number
|
* Get the current line number
|
*/
|
*/
|
|
|
int ejGetLineNumber(int eid)
|
int ejGetLineNumber(int eid)
|
{
|
{
|
ej_t *ep;
|
ej_t *ep;
|
|
|
if ((ep = ejPtr(eid)) == NULL) {
|
if ((ep = ejPtr(eid)) == NULL) {
|
return -1;
|
return -1;
|
}
|
}
|
return ep->input->lineNumber;
|
return ep->input->lineNumber;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Set the result
|
* Set the result
|
*/
|
*/
|
|
|
void ejSetResult(int eid, char_t *s)
|
void ejSetResult(int eid, char_t *s)
|
{
|
{
|
ej_t *ep;
|
ej_t *ep;
|
|
|
if ((ep = ejPtr(eid)) == NULL) {
|
if ((ep = ejPtr(eid)) == NULL) {
|
return;
|
return;
|
}
|
}
|
setString(&ep->result, s);
|
setString(&ep->result, s);
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Get the result
|
* Get the result
|
*/
|
*/
|
|
|
char_t *ejGetResult(int eid)
|
char_t *ejGetResult(int eid)
|
{
|
{
|
ej_t *ep;
|
ej_t *ep;
|
|
|
if ((ep = ejPtr(eid)) == NULL) {
|
if ((ep = ejPtr(eid)) == NULL) {
|
return NULL;
|
return NULL;
|
}
|
}
|
return ep->result;
|
return ep->result;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Set a variable. Note: a variable with a value of NULL means declared but
|
* Set a variable. Note: a variable with a value of NULL means declared but
|
* undefined. The value is defined in the top-most variable frame.
|
* undefined. The value is defined in the top-most variable frame.
|
*/
|
*/
|
|
|
void ejSetVar(int eid, char_t *var, char_t *value)
|
void ejSetVar(int eid, char_t *var, char_t *value)
|
{
|
{
|
ej_t *ep;
|
ej_t *ep;
|
value_t v;
|
value_t v;
|
|
|
a_assert(var && *var);
|
a_assert(var && *var);
|
|
|
if ((ep = ejPtr(eid)) == NULL) {
|
if ((ep = ejPtr(eid)) == NULL) {
|
return;
|
return;
|
}
|
}
|
if (value == NULL) {
|
if (value == NULL) {
|
v = valueString(value, 0);
|
v = valueString(value, 0);
|
} else {
|
} else {
|
v = valueString(value, VALUE_ALLOCATE);
|
v = valueString(value, VALUE_ALLOCATE);
|
}
|
}
|
symEnter(ep->variables[ep->variableMax - 1] - EJ_OFFSET, var, v, 0);
|
symEnter(ep->variables[ep->variableMax - 1] - EJ_OFFSET, var, v, 0);
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Set a local variable. Note: a variable with a value of NULL means
|
* Set a local variable. Note: a variable with a value of NULL means
|
* declared but undefined. The value is defined in the top-most variable frame.
|
* declared but undefined. The value is defined in the top-most variable frame.
|
*/
|
*/
|
|
|
void ejSetLocalVar(int eid, char_t *var, char_t *value)
|
void ejSetLocalVar(int eid, char_t *var, char_t *value)
|
{
|
{
|
ej_t *ep;
|
ej_t *ep;
|
value_t v;
|
value_t v;
|
|
|
a_assert(var && *var);
|
a_assert(var && *var);
|
|
|
if ((ep = ejPtr(eid)) == NULL) {
|
if ((ep = ejPtr(eid)) == NULL) {
|
return;
|
return;
|
}
|
}
|
if (value == NULL) {
|
if (value == NULL) {
|
v = valueString(value, 0);
|
v = valueString(value, 0);
|
} else {
|
} else {
|
v = valueString(value, VALUE_ALLOCATE);
|
v = valueString(value, VALUE_ALLOCATE);
|
}
|
}
|
symEnter(ep->variables[ep->variableMax - 1] - EJ_OFFSET, var, v, 0);
|
symEnter(ep->variables[ep->variableMax - 1] - EJ_OFFSET, var, v, 0);
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Set a global variable. Note: a variable with a value of NULL means
|
* Set a global variable. Note: a variable with a value of NULL means
|
* declared but undefined. The value is defined in the global variable frame.
|
* declared but undefined. The value is defined in the global variable frame.
|
*/
|
*/
|
|
|
void ejSetGlobalVar(int eid, char_t *var, char_t *value)
|
void ejSetGlobalVar(int eid, char_t *var, char_t *value)
|
{
|
{
|
ej_t *ep;
|
ej_t *ep;
|
value_t v;
|
value_t v;
|
|
|
a_assert(var && *var);
|
a_assert(var && *var);
|
|
|
if ((ep = ejPtr(eid)) == NULL) {
|
if ((ep = ejPtr(eid)) == NULL) {
|
return;
|
return;
|
}
|
}
|
if (value == NULL) {
|
if (value == NULL) {
|
v = valueString(value, 0);
|
v = valueString(value, 0);
|
} else {
|
} else {
|
v = valueString(value, VALUE_ALLOCATE);
|
v = valueString(value, VALUE_ALLOCATE);
|
}
|
}
|
symEnter(ep->variables[0] - EJ_OFFSET, var, v, 0);
|
symEnter(ep->variables[0] - EJ_OFFSET, var, v, 0);
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Get a variable
|
* Get a variable
|
*/
|
*/
|
|
|
int ejGetVar(int eid, char_t *var, char_t **value)
|
int ejGetVar(int eid, char_t *var, char_t **value)
|
{
|
{
|
ej_t *ep;
|
ej_t *ep;
|
sym_t *sp;
|
sym_t *sp;
|
int i;
|
int i;
|
|
|
a_assert(var && *var);
|
a_assert(var && *var);
|
a_assert(value);
|
a_assert(value);
|
|
|
if ((ep = ejPtr(eid)) == NULL) {
|
if ((ep = ejPtr(eid)) == NULL) {
|
return -1;
|
return -1;
|
}
|
}
|
|
|
for (i = ep->variableMax - 1; i >= 0; i--) {
|
for (i = ep->variableMax - 1; i >= 0; i--) {
|
if ((sp = symLookup(ep->variables[i] - EJ_OFFSET, var)) == NULL) {
|
if ((sp = symLookup(ep->variables[i] - EJ_OFFSET, var)) == NULL) {
|
continue;
|
continue;
|
}
|
}
|
a_assert(sp->content.type == string);
|
a_assert(sp->content.type == string);
|
*value = sp->content.value.string;
|
*value = sp->content.value.string;
|
return i;
|
return i;
|
}
|
}
|
return -1;
|
return -1;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
#if UNUSED
|
#if UNUSED
|
/*
|
/*
|
* Get the variable symbol table
|
* Get the variable symbol table
|
*/
|
*/
|
|
|
sym_fd_t ejGetVariableTable(int eid)
|
sym_fd_t ejGetVariableTable(int eid)
|
{
|
{
|
ej_t *ep;
|
ej_t *ep;
|
|
|
if ((ep = ejPtr(eid)) == NULL) {
|
if ((ep = ejPtr(eid)) == NULL) {
|
return -1;
|
return -1;
|
}
|
}
|
return ep->variables;
|
return ep->variables;
|
}
|
}
|
#endif
|
#endif
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Get the functions symbol table
|
* Get the functions symbol table
|
*/
|
*/
|
|
|
sym_fd_t ejGetFunctionTable(int eid)
|
sym_fd_t ejGetFunctionTable(int eid)
|
{
|
{
|
ej_t *ep;
|
ej_t *ep;
|
|
|
if ((ep = ejPtr(eid)) == NULL) {
|
if ((ep = ejPtr(eid)) == NULL) {
|
return -1;
|
return -1;
|
}
|
}
|
return ep->functions;
|
return ep->functions;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Free an argument list
|
* Free an argument list
|
*/
|
*/
|
|
|
static void freeFunc(ejfunc_t *func)
|
static void freeFunc(ejfunc_t *func)
|
{
|
{
|
int i;
|
int i;
|
|
|
for (i = func->nArgs - 1; i >= 0; i--) {
|
for (i = func->nArgs - 1; i >= 0; i--) {
|
bfree(B_L, func->args[i]);
|
bfree(B_L, func->args[i]);
|
func->nArgs = hFree((void***) &func->args, i);
|
func->nArgs = hFree((void***) &func->args, i);
|
}
|
}
|
if (func->fname) {
|
if (func->fname) {
|
bfree(B_L, func->fname);
|
bfree(B_L, func->fname);
|
func->fname = NULL;
|
func->fname = NULL;
|
}
|
}
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Get Ejscript pointer
|
* Get Ejscript pointer
|
*/
|
*/
|
|
|
static ej_t *ejPtr(int eid)
|
static ej_t *ejPtr(int eid)
|
{
|
{
|
a_assert(0 <= eid && eid < ejMax);
|
a_assert(0 <= eid && eid < ejMax);
|
|
|
if (eid < 0 || eid >= ejMax || ejHandles[eid] == NULL) {
|
if (eid < 0 || eid >= ejMax || ejHandles[eid] == NULL) {
|
ejError(NULL, T("Bad handle %d"), eid);
|
ejError(NULL, T("Bad handle %d"), eid);
|
return NULL;
|
return NULL;
|
}
|
}
|
return ejHandles[eid];
|
return ejHandles[eid];
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
|
|