OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rtems/] [c/] [src/] [libnetworking/] [rtems_webserver/] [ejparse.c] - Rev 1767

Go to most recent revision | Compare with Previous | Blame | View Log

/*
 * ejparse.c -- Ejscript(TM) Parser
 *
 * Copyright (c) Go Ahead Software, Inc., 1995-1999
 *
 * 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	"ej.h"
 
/********************************** Local Data ********************************/
 
ej_t			**ejHandles;							/* List of ej handles */
int				ejMax = -1;								/* Maximum size of	*/
 
/****************************** Forward Declarations **************************/
 
static ej_t		*ejPtr(int eid);
static void		clearString(char_t **ptr);
static void		setString(char_t **ptr, char_t *s);
static void		appendString(char_t **ptr, char_t *s);
static void		freeVar(sym_t* sp);
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);
 
/************************************* 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);
 
	if (ep->flags & FLAGS_VARIABLES) {
		for (i = ep->variableMax - 1; i >= 0; i--) {
			symClose(ep->variables[i] - EJ_OFFSET, freeVar);
			ep->variableMax = hFree((void***) &ep->variables, i);
		}
	}
	if (ep->flags & FLAGS_FUNCTIONS) {
		symClose(ep->functions, freeVar);
	}
 
	ejMax = hFree((void***) &ejHandles, ep->eid);
	bfree(B_L, ep);
}
 
/******************************************************************************/
/*
 *	Callback from symClose. Free the variable.
 */
 
static void freeVar(sym_t* sp)
{
	valueFree(&sp->content);
}
 
/******************************************************************************/
/*
 *	Evaluate a Ejscript file
 */
 
#if DEV
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) {
		close(fd);
		ejError(ep, T("Cant stat %s"), path);
		return NULL;
	}
	if ((fileBuf = balloc(B_L, sbuf.st_size + 1)) == NULL) {
		close(fd);
		ejError(ep, T("Cant malloc %d"), sbuf.st_size);
		return NULL;
	}
	if (read(fd, fileBuf, sbuf.st_size) != (int)sbuf.st_size) {
		close(fd);
		bfree(B_L, fileBuf);
		ejError(ep, T("Error reading %s"), path);
		return NULL;
	}
	fileBuf[sbuf.st_size] = '\0';
	close(fd);
 
	if ((script = ballocAscToUni(fileBuf)) == 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
 
/******************************************************************************/
/*
 *	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, freeVar);
	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;
 
	a_assert(script);
 
	if (emsg) {
		*emsg = NULL;
	}
	if ((ep = ejPtr(eid)) == NULL) {
		return NULL;
	}
 
	setString(&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
 */
	do {
		state = parse(ep, STATE_BEGIN, FLAGS_EXE);
	} 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:
	case STATE_DEC:
		state = parseStmt(ep, state, flags);
		break;
 
	case STATE_EXPR:
		state = parseStmt(ep, state, flags);
		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;
	char_t		*identifier;
	int			done, expectSemi, thenFlags, elseFlags, tid, cond, forFlags;
 
	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(&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) {
					goto error;
				}
				if (flags & FLAGS_EXE) {
					if ( state == STATE_DEC ) {
						ejSetLocalVar(ep->eid, identifier, ep->result);
					}
					else {
						if (ejGetVar(ep->eid, identifier, &value) > 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 ) {
					if (ejGetVar(ep->eid, identifier, &value) < 0) {
						ejError(ep, T("Undefined variable %s\n"), identifier);
						goto error;
					}
					setString(&ep->result, value);
					if (evalExpr(ep, value, (int) *ep->token, T("1")) < 0) {
						state = STATE_ERR;
						break;
					}
					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(&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(&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(&func.fname, ep->token);
			ep->func = &func;
 
			setString(&ep->result, T(""));
			if (ejLexGetToken(ep, state) != TOK_LPAREN) {
				freeFunc(&func);
				goto error;
			}
 
			if (parse(ep, STATE_ARG_LIST, flags) != STATE_ARG_LIST_DONE) {
				freeFunc(&func);
				ep->func = saveFunc;
				goto error;
			}
/*
 *			Evaluate the function if required
 */
			if (flags & FLAGS_EXE && evalFunction(ep) < 0) {
				freeFunc(&func);
				ep->func = saveFunc;
				goto error;
			}
 
			freeFunc(&func);
			ep->func = saveFunc;
 
			if (ejLexGetToken(ep, state) != TOK_RPAREN) {
				goto error;
			}
			if (state == STATE_STMT) {
				expectSemi++;
			}
			done++;
			break;
 
		case TOK_IF:
			if (state != STATE_STMT) {
				goto error;
			}
			if (ejLexGetToken(ep, state) != TOK_LPAREN) {
				goto error;
			}
/*
 *			Evaluate the entire condition list "(condition)"
 */
			if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) {
				goto error;
			}
			if (ejLexGetToken(ep, state) != TOK_RPAREN) {
				goto error;
			}
/*
 *			This is the "then" case. We need to always parse both cases and
 *			execute only the relevant case.
 */
			if (*ep->result == '1') {
				thenFlags = flags;
				elseFlags = flags & ~FLAGS_EXE;
			} else {
				thenFlags = flags & ~FLAGS_EXE;
				elseFlags = flags;
			}
/*
 *			Process the "then" case
 */
			if (parse(ep, STATE_STMT, thenFlags) != STATE_STMT_DONE) {
				goto error;
			}
			tid = ejLexGetToken(ep, state);
			if (tid != TOK_ELSE) {
				ejLexPutbackToken(ep, tid, ep->token);
				done++;
				break;
			}
/*
 *			Process the "else" case
 */
			if (parse(ep, STATE_STMT, elseFlags) != STATE_STMT_DONE) {
				goto error;
			}
			done++;
			break;
 
		case TOK_FOR:
/*
 *			Format for the expression is:
 *
 *				for (initial; condition; incr) {
 *					body;
 *				}
 */
			if (state != STATE_STMT) {
				goto error;
			}
			if (ejLexGetToken(ep, state) != TOK_LPAREN) {
				goto error;
			}
 
/*
 *			Evaluate the for loop initialization statement
 */
			if (parse(ep, STATE_EXPR, flags) != STATE_EXPR_DONE) {
				goto error;
			}
			if (ejLexGetToken(ep, state) != TOK_SEMI) {
				goto error;
			}
 
/*
 *			The first time through, we save the current input context just 
 *			to each step: prior to the conditional, the loop increment and the
 *			loop body.
 */
			ejLexSaveInputState(ep, &condScript);
			if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) {
				goto error;
			}
			cond = (*ep->result != '0');
 
			if (ejLexGetToken(ep, state) != TOK_SEMI) {
				goto error;
			}
 
/*
 *			Don't execute the loop increment statement or the body first time
 */
			forFlags = flags & ~FLAGS_EXE;
			ejLexSaveInputState(ep, &incrScript);
			if (parse(ep, STATE_EXPR, forFlags) != STATE_EXPR_DONE) {
				goto error;
			}
			if (ejLexGetToken(ep, state) != TOK_RPAREN) {
				goto error;
			}
 
/*
 *			Parse the body and remember the end of the body script 
 */
			ejLexSaveInputState(ep, &bodyScript);
			if (parse(ep, STATE_STMT, forFlags) != STATE_STMT_DONE) {
				goto error;
			}
			ejLexSaveInputState(ep, &endScript);
 
/*
 *			Now actually do the for loop. Note loop has been rotated
 */
			while (cond && (flags & FLAGS_EXE) ) {
/*
 *				Evaluate the body
 */
				ejLexRestoreInputState(ep, &bodyScript);
				if (parse(ep, STATE_STMT, flags) != STATE_STMT_DONE) {
					goto error;
				}
/*
 *				Evaluate the increment script
 */
				ejLexRestoreInputState(ep, &incrScript);
				if (parse(ep, STATE_EXPR, flags) != STATE_EXPR_DONE) {
					goto error;
				}
/*
 *				Evaluate the condition
 */
				ejLexRestoreInputState(ep, &condScript);
				if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) {
					goto error;
				}
				cond = (*ep->result != '0');
			}
			ejLexRestoreInputState(ep, &endScript);
			done++;
			break;
 
		case TOK_VAR:
			if (parse(ep, STATE_DEC_LIST, flags) != STATE_DEC_LIST_DONE) {
				goto error;
			}
			done++;
			break;
 
		case TOK_COMMA:
			ejLexPutbackToken(ep, TOK_EXPR, ep->token);
			done++;
			break;
 
		case TOK_LPAREN:
			if (state == STATE_EXPR) {
				if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) {
					goto error;
				}
				if (ejLexGetToken(ep, state) != TOK_RPAREN) {
					goto error;
				}
				return STATE_EXPR_DONE;
			}
			done++;
			break;
 
		case TOK_RPAREN:
			ejLexPutbackToken(ep, tid, ep->token);
			return STATE_EXPR_DONE;
 
		case TOK_LBRACE:
/*
 *			This handles any code in braces except "if () {} else {}"
 */
			if (state != STATE_STMT) {
				goto error;
			}
 
/*
 *			Parse will return STATE_STMT_BLOCK_DONE when the RBRACE is seen
 */
			do {
				state = parse(ep, STATE_STMT, flags);
			} while (state == STATE_STMT_DONE);
 
			if (ejLexGetToken(ep, state) != TOK_RBRACE) {
				goto error;
			}
			return STATE_STMT_DONE;
 
		case TOK_RBRACE:
			if (state == STATE_STMT) {
				ejLexPutbackToken(ep, tid, ep->token);
				return STATE_STMT_BLOCK_DONE;
			}
			goto error;
 
		case TOK_RETURN:
			if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) {
				goto error;
			}
			if (flags & FLAGS_EXE) {
				while ( ejLexGetToken(ep, state) != TOK_EOF );
				done++;
				return STATE_EOF;
			}
			break;
		}
	}
 
	if (expectSemi) {
		tid = ejLexGetToken(ep, state);
		if (tid != TOK_SEMI && tid != TOK_NEWLINE) {
			goto error;
		}
 
/*
 *		Skip newline after semi-colon
 */
		tid = ejLexGetToken(ep, state);
		if (tid != TOK_NEWLINE) {
			ejLexPutbackToken(ep, tid, ep->token);
		}
	}
 
/*
 *	Free resources and return the correct status
 */
doneParse:
	if (tid == TOK_FOR) {
		ejLexFreeInputState(ep, &condScript);
		ejLexFreeInputState(ep, &incrScript);
		ejLexFreeInputState(ep, &endScript);
		ejLexFreeInputState(ep, &bodyScript);
	}
	if (state == STATE_STMT) {
		return STATE_STMT_DONE;
	} else if (state == STATE_DEC) {
		return STATE_DEC_DONE;
	} else if (state == STATE_EXPR) {
		return STATE_EXPR_DONE;
	} else if (state == STATE_EOF) {
		return state;
	} else {
		return STATE_ERR;
	}
 
/*
 *	Common error exit
 */
error:
	state = STATE_ERR;
	goto doneParse;
}
 
/******************************************************************************/
/*
 *	Parse variable declaration list
 */
 
static int parseDeclaration(ej_t *ep, int state, int flags)
{
	int		tid;
 
	a_assert(ep);
 
/*
 *	Declarations can be of the following forms:
 *			var x;
 *			var x, y, z;
 *			var x = 1 + 2 / 3, y = 2 + 4;
 *
 *	We set the variable to NULL if there is no associated assignment.
 */
 
	do {
		if ((tid = ejLexGetToken(ep, state)) != TOK_ID) {
			return STATE_ERR;
		}
		ejLexPutbackToken(ep, tid, ep->token);
 
/*
 *		Parse the entire assignment or simple identifier declaration
 */
		if (parse(ep, STATE_DEC, flags) != STATE_DEC_DONE) {
			return STATE_ERR;
		}
 
/*
 *		Peek at the next token, continue if comma seen
 */
		tid = ejLexGetToken(ep, state);
		if (tid == TOK_SEMI) {
			return STATE_DEC_LIST_DONE;
		} else if (tid != TOK_COMMA) {
			return STATE_ERR;
		}
	} while (tid == TOK_COMMA);
 
	if (tid != TOK_SEMI) {
		return STATE_ERR;
	}
	return STATE_DEC_LIST_DONE;
}
 
/******************************************************************************/
/*
 *	Parse function arguments
 */
 
static int parseArgs(ej_t *ep, int state, int flags)
{
	int		tid, aid;
 
	a_assert(ep);
 
	do {
		state = parse(ep, STATE_RELEXP, flags);
		if (state == STATE_EOF || state == STATE_ERR) {
			return state;
		}
		if (state == STATE_RELEXP_DONE) {
			aid = hAlloc((void***) &ep->func->args);
			ep->func->args[aid] = bstrdup(B_L, ep->result);
			ep->func->nArgs++;
		}
/*
 *		Peek at the next token, continue if more args (ie. comma seen)
 */
		tid = ejLexGetToken(ep, state);
		if (tid != TOK_COMMA) {
			ejLexPutbackToken(ep, tid, ep->token);
		}
	} while (tid == TOK_COMMA);
 
	if (tid != TOK_RPAREN && state != STATE_RELEXP_DONE) {
		return STATE_ERR;
	}
	return STATE_ARG_LIST_DONE;
}
 
/******************************************************************************/
/*
 *	Parse conditional expression (relational ops separated by ||, &&)
 */
 
static int parseCond(ej_t *ep, int state, int flags)
{
	char_t	*lhs, *rhs;
	int		tid, operator;
 
	a_assert(ep);
 
	setString(&ep->result, T(""));
	rhs = lhs = NULL;
	operator = 0;
 
	do {
/*
 *	Recurse to handle one side of a conditional. Accumulate the
 *	left hand side and the final result in ep->result.
 */
		state = parse(ep, STATE_RELEXP, flags);
		if (state != STATE_RELEXP_DONE) {
			state = STATE_ERR;
			break;
		}
		if (operator > 0) {
			setString(&rhs, ep->result);
			if (evalCond(ep, lhs, operator, rhs) < 0) {
				state = STATE_ERR;
				break;
			}
		}
		setString(&lhs, ep->result);
 
		tid = ejLexGetToken(ep, state);
		if (tid == TOK_LOGICAL) {
			operator = (int) *ep->token;
 
		} else if (tid == TOK_RPAREN || tid == TOK_SEMI) {
			ejLexPutbackToken(ep, tid, ep->token);
			state = STATE_COND_DONE;
			break;
 
		} else {
			ejLexPutbackToken(ep, tid, ep->token);
		}
 
	} while (state == STATE_RELEXP_DONE);
 
	if (lhs) {
		bfree(B_L, lhs);
	}
	if (rhs) {
		bfree(B_L, rhs);
	}
	return state;
}
 
/******************************************************************************/
/*
 *	Parse expression (leftHandSide operator rightHandSide)
 */
 
static int parseExpr(ej_t *ep, int state, int flags)
{
	char_t	*lhs, *rhs;
	int		rel, tid;
 
	a_assert(ep);
 
	setString(&ep->result, T(""));
	rhs = lhs = NULL;
	rel = 0;
 
	do {
/*
 *	This loop will handle an entire expression list. We call parse
 *	to evalutate each term which returns the result in ep->result.
 */
		state = parse(ep, STATE_EXPR, flags);
		if (state != STATE_EXPR_DONE) {
			state = STATE_ERR;
			break;
		}
		if (rel > 0) {
			setString(&rhs, ep->result);
			if (evalExpr(ep, lhs, rel, rhs) < 0) {
				state = STATE_ERR;
				break;
			}
		}
		setString(&lhs, ep->result);
 
		if ((tid = ejLexGetToken(ep, state)) == TOK_EXPR) {
			rel = (int) *ep->token;
 
		} else if (tid == TOK_INC_DEC) {
			rel = (int) *ep->token;
 
		} else {
			ejLexPutbackToken(ep, tid, ep->token);
			state = STATE_RELEXP_DONE;
		}
 
	} while (state == STATE_EXPR_DONE);
 
	if (rhs)
		bfree(B_L, rhs);
	if (lhs)
		bfree(B_L, lhs);
	return state;
}
 
/******************************************************************************/
/*
 *	Evaluate a condition. Implements &&, ||, !
 */
 
static int evalCond(ej_t *ep, char_t *lhs, int rel, char_t *rhs)
{
	char_t	buf[16];
	int		l, r, lval;
 
	a_assert(lhs);
	a_assert(rhs);
	a_assert(rel > 0);
 
	lval = 0;
	if (gisdigit(*lhs) && gisdigit(*rhs)) {
		l = gatoi(lhs);
		r = gatoi(rhs);
		switch (rel) {
		case COND_AND:
			lval = l && r;
			break;
		case COND_OR:
			lval = l || r;
			break;
		default:
			ejError(ep, T("Bad operator %d"), rel);
			return -1;
		}
	} else {
		if (!gisdigit(*lhs)) {
			ejError(ep, T("Conditional must be numeric"), lhs);
		} else {
			ejError(ep, T("Conditional must be numeric"), rhs);
		}
	}
 
	stritoa(lval, buf, sizeof(buf));
	setString(&ep->result, buf);
	return 0;
}
 
/******************************************************************************/
/*
 *	Evaluate an operation
 */
 
static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs)
{
	char_t	*cp, buf[16];
	int		numeric, l, r, lval;
 
	a_assert(lhs);
	a_assert(rhs);
	a_assert(rel > 0);
 
/*
 *	All of the characters in the lhs and rhs must be numeric
 */
	numeric = 1;
	for (cp = lhs; *cp; cp++) {
		if (!gisdigit(*cp)) {
			numeric = 0;
			break;
		}
	}
	if (numeric) {
		for (cp = rhs; *cp; cp++) {
			if (!gisdigit(*cp)) {
				numeric = 0;
				break;
			}
		}
	}
	if (numeric) {
		l = gatoi(lhs);
		r = gatoi(rhs);
		switch (rel) {
		case EXPR_PLUS:
			lval = l + r;
			break;
		case EXPR_INC:
			lval = l + 1;
			break;
		case EXPR_MINUS:
			lval = l - r;
			break;
		case EXPR_DEC:
			lval = l - 1;
			break;
		case EXPR_MUL:
			lval = l * r;
			break;
		case EXPR_DIV:
			if (r != 0) {
				lval = l / r;
			} else {
				lval = 0;
			}
			break;
		case EXPR_MOD:
			if (r != 0) {
				lval = l % r;
			} else {
				lval = 0;
			}
			break;
		case EXPR_LSHIFT:
			lval = l << r;
			break;
		case EXPR_RSHIFT:
			lval = l >> r;
			break;
		case EXPR_EQ:
			lval = l == r;
			break;
		case EXPR_NOTEQ:
			lval = l != r;
			break;
		case EXPR_LESS:
			lval = (l < r) ? 1 : 0;
			break;
		case EXPR_LESSEQ:
			lval = (l <= r) ? 1 : 0;
			break;
		case EXPR_GREATER:
			lval = (l > r) ? 1 : 0;
			break;
		case EXPR_GREATEREQ:
			lval = (l >= r) ? 1 : 0;
			break;
		default:
			ejError(ep, T("Bad operator %d"), rel);
			return -1;
		}
 
	} else {
		switch (rel) {
		case EXPR_PLUS:
			clearString(&ep->result);
			appendString(&ep->result, lhs);
			appendString(&ep->result, rhs);
			return 0;
		case EXPR_LESS:
			lval = gstrcmp(lhs, rhs) < 0;
			break;
		case EXPR_LESSEQ:
			lval = gstrcmp(lhs, rhs) <= 0;
			break;
		case EXPR_GREATER:
			lval = gstrcmp(lhs, rhs) > 0;
			break;
		case EXPR_GREATEREQ:
			lval = gstrcmp(lhs, rhs) >= 0;
			break;
		case EXPR_EQ:
			lval = gstrcmp(lhs, rhs) == 0;
			break;
		case EXPR_NOTEQ:
			lval = gstrcmp(lhs, rhs) != 0;
			break;
		case EXPR_INC:
		case EXPR_DEC:
		case EXPR_MINUS:
		case EXPR_DIV:
		case EXPR_MOD:
		case EXPR_LSHIFT:
		case EXPR_RSHIFT:
		default:
			ejError(ep, T("Bad operator"));
			return -1;
		}
	}
 
	stritoa(lval, buf, sizeof(buf));
	setString(&ep->result, buf);
	return 0;
}
 
/******************************************************************************/
/*
 *	Evaluate a function
 */
 
static int evalFunction(ej_t *ep)
{
	sym_t	*sp;
	int		(*fn)(int eid, void *handle, int argc, char_t **argv);
 
	if ((sp = symLookup(ep->functions, ep->func->fname)) == NULL) {
		ejError(ep, T("Undefined procedure %s"), ep->func->fname);
		return -1;
	}
	fn = (int (*)(int, void*, int, char_t**)) sp->content.value.integer;
	if (fn == NULL) {
		ejError(ep, T("Undefined procedure %s"), ep->func->fname);
		return -1;
	}
 
	return (*fn)(ep->eid, (void*) ep->userHandle, ep->func->nArgs, 
		ep->func->args);
}
 
/******************************************************************************/
/*
 *	Output a parse ej_error message
 */
 
void ejError(ej_t* ep, char_t* fmt, ...)
{
	va_list		args;
	ejinput_t	*ip;
	char_t		*errbuf, *msgbuf;
 
	a_assert(ep);
	a_assert(fmt);
	ip = ep->input;
 
	va_start(args, fmt);
	msgbuf = NULL;
	gvsnprintf(&msgbuf, E_MAX_ERROR, fmt, args);
	va_end(args);
 
	if (ep && ip) {
		errbuf = NULL;
		gsnprintf(&errbuf, E_MAX_ERROR, T("%s\n At line %d, line => \n\n%s\n"),
			msgbuf, ip->lineNumber, ip->line);
		bfreeSafe(B_L, ep->error);
		ep->error = errbuf;
	}
	bfreeSafe(B_L, msgbuf);
}
 
/******************************************************************************/
/*
 *	Clear a string value
 */
 
static void clearString(char_t **ptr)
{
	a_assert(ptr);
 
	if (*ptr) {
		bfree(B_L, *ptr);
	}
	*ptr = NULL;
}
 
/******************************************************************************/
/*
 *	Set a string value
 */
 
static void setString(char_t **ptr, char_t *s)
{
	a_assert(ptr);
 
	if (*ptr) {
		bfree(B_L, *ptr);
	}
	*ptr = bstrdup(B_L, s);
}
 
/******************************************************************************/
/*
 *	Append to the pointer value
 */
 
static void appendString(char_t **ptr, char_t *s)
{
	int	len, oldlen;
 
	a_assert(ptr);
 
	if (*ptr) {
		len = gstrlen(s);
		oldlen = gstrlen(*ptr);
		*ptr = brealloc(B_L, *ptr, (len + oldlen + 1) * sizeof(char_t));
		gstrcpy(&(*ptr)[oldlen], s);
	} else {
		*ptr = bstrdup(B_L, s);
	}
}
 
/******************************************************************************/
/*
 *	Define a function
 */
 
int ejSetGlobalFunction(int eid, char_t *name, 
	int (*fn)(int eid, void *handle, int argc, char_t **argv))
{
	ej_t	*ep;
 
	if ((ep = ejPtr(eid)) == NULL) {
		return -1;
	}
	return ejSetGlobalFunctionDirect(ep->functions, name, fn);
}
 
/******************************************************************************/
/*
 *	Define a function directly into the function symbol table.
 */
 
int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name, 
	int (*fn)(int eid, void *handle, int argc, char_t **argv))
{
	if (symEnter(functions, name, valueInteger((long) fn), 0) == NULL) {
		return -1;
	}
	return 0;
}
 
/******************************************************************************/
/*
 *	Get a function definition
 */
 
void *ejGetGlobalFunction(int eid, char_t *name)
{
	ej_t	*ep;
	sym_t	*sp;
	int		(*fn)(int eid, void *handle, int argc, char_t **argv);
 
	if ((ep = ejPtr(eid)) == NULL) {
		return NULL;
	}
	if ((sp = symLookup(ep->functions, name)) != NULL) {
		fn = (int (*)(int, void*, int, char_t**)) sp->content.value.integer;
		return (void*) fn;
	}
	return NULL;
}
 
/******************************************************************************/
/*
 *	Utility routine to crack Ejscript arguments. Return the number of args 
 *	seen. This routine only supports %s and %d type args.
 *
 *	Typical usage:
 *
 *		if (ejArgs(argc, argv, "%s %d", &name, &age) < 2) {
 *			error("Insufficient args\n");
 *			return -1;
 *		}
 */
 
int ejArgs(int argc, char_t **argv, char_t *fmt, ...)
{
	va_list	vargs;
	char_t	*cp, **sp;
	int		*ip;
	int		argn;
 
	va_start(vargs, fmt);
 
	if (argv == NULL) {
		return 0;
	}
 
	for (argn = 0, cp = fmt; cp && *cp && argv[argn]; ) {
		if (*cp++ != '%') {
			continue;
		}
 
		switch (*cp) {
		case 'd':
			ip = va_arg(vargs, int*);
			*ip = gatoi(argv[argn]);
			break;
 
		case 's':
			sp = va_arg(vargs, char_t**);
			*sp = argv[argn];
			break;
 
		default:
/*
 *			Unsupported
 */
			a_assert(0);
		}
		argn++;
	}
 
	va_end(vargs);
	return argn;
}
 
/******************************************************************************/
/*
 *	Define the user handle
 */
 
void ejSetUserHandle(int eid, int handle)
{
	ej_t	*ep;
 
	if ((ep = ejPtr(eid)) == NULL) {
		return;
	}
	ep->userHandle = handle;
}
 
/******************************************************************************/
/*
 *	Get the user handle
 */
 
int ejGetUserHandle(int eid)
{
	ej_t	*ep;
 
	if ((ep = ejPtr(eid)) == NULL) {
		return -1;
	}
	return ep->userHandle;
}
 
/******************************************************************************/
/*
 *	Get the current line number
 */
 
int ejGetLineNumber(int eid)
{
	ej_t	*ep;
 
	if ((ep = ejPtr(eid)) == NULL) {
		return -1;
	}
	return ep->input->lineNumber;
}
 
/******************************************************************************/
/*
 *	Set the result
 */
 
void ejSetResult(int eid, char_t *s)
{
	ej_t	*ep;
 
	if ((ep = ejPtr(eid)) == NULL) {
		return;
	}
	setString(&ep->result, s);
}
 
/******************************************************************************/
/*
 *	Get the result
 */
 
char_t *ejGetResult(int eid)
{
	ej_t	*ep;
 
	if ((ep = ejPtr(eid)) == NULL) {
		return NULL;
	}
	return ep->result;
}
 
/******************************************************************************/
/*
 *	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.
 */
 
void ejSetVar(int eid, char_t *var, char_t *value)
{
	ej_t	*ep;
	value_t	v;
 
	a_assert(var && *var);
 
	if ((ep = ejPtr(eid)) == NULL) {
		return;
	}
	if (value == NULL) {
		v = valueString(value, 0);
	} else {
		v = valueString(value, VALUE_ALLOCATE);
	}
	symEnter(ep->variables[ep->variableMax - 1] - EJ_OFFSET, var, v, 0);
}
 
/******************************************************************************/
/*
 *	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.
 */
 
void ejSetLocalVar(int eid, char_t *var, char_t *value)
{
	ej_t	*ep;
	value_t	v;
 
	a_assert(var && *var);
 
	if ((ep = ejPtr(eid)) == NULL) {
		return;
	}
	if (value == NULL) {
		v = valueString(value, 0);
	} else {
		v = valueString(value, VALUE_ALLOCATE);
	}
	symEnter(ep->variables[ep->variableMax - 1] - EJ_OFFSET, var, v, 0);
}
 
/******************************************************************************/
/*
 *	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.
 */
 
void ejSetGlobalVar(int eid, char_t *var, char_t *value)
{
	ej_t	*ep;
	value_t v;
 
	a_assert(var && *var);
 
	if ((ep = ejPtr(eid)) == NULL) {
		return;
	}
	if (value == NULL) {
		v = valueString(value, 0);
	} else {
		v = valueString(value, VALUE_ALLOCATE);
	}
	symEnter(ep->variables[0] - EJ_OFFSET, var, v, 0);
}
 
/******************************************************************************/
/*
 *	Get a variable
 */
 
int ejGetVar(int eid, char_t *var, char_t **value)
{
	ej_t	*ep;
	sym_t	*sp;
	int		i;
 
	a_assert(var && *var);
	a_assert(value);
 
	if ((ep = ejPtr(eid)) == NULL) {
		return -1;
	}
 
	for (i = ep->variableMax - 1; i >= 0; i--) {
		if ((sp = symLookup(ep->variables[i] - EJ_OFFSET, var)) == NULL) {
			continue;
		}
		a_assert(sp->content.type == string);
		*value = sp->content.value.string;
		return i;
	}
	return -1;
}
 
/******************************************************************************/
#if UNUSED
/*
 *	Get the variable symbol table
 */
 
sym_fd_t ejGetVariableTable(int eid)
{
	ej_t	*ep;
 
	if ((ep = ejPtr(eid)) == NULL) {
		return -1;
	}
	return ep->variables;
}
#endif
/******************************************************************************/
/*
 *	Get the functions symbol table
 */
 
sym_fd_t ejGetFunctionTable(int eid)
{
	ej_t	*ep;
 
	if ((ep = ejPtr(eid)) == NULL) {
		return -1;
	}
	return ep->functions;
}
 
/******************************************************************************/
/*
 *	Free an argument list
 */
 
static void freeFunc(ejfunc_t *func)
{
	int	i;
 
	for (i = func->nArgs - 1; i >= 0; i--) {
		bfree(B_L, func->args[i]);
		func->nArgs = hFree((void***) &func->args, i);
	}
	if (func->fname) {
		bfree(B_L, func->fname);
		func->fname = NULL;
	}
}
 
/******************************************************************************/
/* 
 *	Get Ejscript pointer
 */
 
static ej_t *ejPtr(int eid)
{
	a_assert(0 <= eid && eid < ejMax);
 
	if (eid < 0 || eid >= ejMax || ejHandles[eid] == NULL) {
		ejError(NULL, T("Bad handle %d"), eid);
		return NULL;
	}
	return ejHandles[eid];
}
 
/******************************************************************************/
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.