Exemplo n.º 1
0
char_t *ejEvalFile(int eid, char_t *path, char_t **emsg)
{
	gstat_t sbuf;
	ej_t	*ep;
	char_t	*script, *rs;
	char	*fileBuf;
	int		fd;

	a_assert(path && *path);

	if (emsg) {
		*emsg = NULL;
	}

	if ((ep = ejPtr(eid)) == NULL) {
		return NULL;
	}

	if ((fd = gopen(path, O_RDONLY | O_BINARY, 0666)) < 0) {
		ejError(ep, T("Bad handle %d"), eid);
		return NULL;
	}
	
	if (gstat(path, &sbuf) < 0) {
		gclose(fd);
		ejError(ep, T("Cant stat %s"), path);
		return NULL;
	}
	
	if ((fileBuf = balloc(B_L, sbuf.st_size + 1)) == NULL) {
		gclose(fd);
		ejError(ep, T("Cant malloc %d"), sbuf.st_size);
		return NULL;
	}
	
	if (gread(fd, fileBuf, sbuf.st_size) != (int)sbuf.st_size) {
		gclose(fd);
		bfree(B_L, fileBuf);
		ejError(ep, T("Error reading %s"), path);
		return NULL;
	}
	
	fileBuf[sbuf.st_size] = '\0';
	gclose(fd);

	if ((script = ballocAscToUni(fileBuf, sbuf.st_size)) == NULL) {
		bfree(B_L, fileBuf);
		ejError(ep, T("Cant malloc %d"), sbuf.st_size + 1);
		return NULL;
	}
	bfree(B_L, fileBuf);

	rs = ejEvalBlock(eid, script, emsg);

	bfree(B_L, script);
	return rs;
}
Exemplo n.º 2
0
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((int)*lhs) && gisdigit((int)*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((int)*lhs)) {
			ejError(ep, T("Conditional must be numeric"), lhs);
		} else {
			ejError(ep, T("Conditional must be numeric"), rhs);
		}
	}

	stritoa(lval, buf, sizeof(buf));
	setString(B_L, &ep->result, buf);
	return 0;
}
Exemplo n.º 3
0
static int tokenAddChar(ej_t *ep, int c)
{
    ejinput_t*	ip;

    a_assert(ep);
    ip = ep->input;
    a_assert(ip);

    if (ringqPutc(&ip->tokbuf, (char_t) c) < 0) {
        ejError(ep, T("Token too big"));
        return -1;
    }
    * ((char_t*) ip->tokbuf.endp) = '\0';
    ep->token = (char_t*) ip->tokbuf.servp;

    return 0;
}
Exemplo n.º 4
0
static int parseStmt(ej_t *ep, int state, int flags)
{
	ejfunc_t	func;
	ejfunc_t	*saveFunc;
	ejinput_t	condScript, endScript, bodyScript, incrScript;
	char_t		*value,	*identifier;
	int			done, expectSemi, thenFlags, elseFlags, tid, cond, forFlags;
	int			ejVarType;

	a_assert(ep);

/*
 *	Set these to NULL, else we try to free them if an error occurs.
 */
	endScript.putBackToken = NULL;
	bodyScript.putBackToken = NULL;
	incrScript.putBackToken = NULL;
	condScript.putBackToken = NULL;

	expectSemi = 0;
	saveFunc = NULL;

	for (done = 0; !done; ) {
		tid = ejLexGetToken(ep, state);

		switch (tid) {
		default:
			ejLexPutbackToken(ep, TOK_EXPR, ep->token);
			done++;
			break;

		case TOK_ERR:
			state = STATE_ERR;
			done++;
			break;

		case TOK_EOF:
			state = STATE_EOF;
			done++;
			break;

		case TOK_NEWLINE:
			break;

		case TOK_SEMI:
/*
 *			This case is when we discover no statement and just a lone ';'
 */
			if (state != STATE_STMT) {
				ejLexPutbackToken(ep, tid, ep->token);
			}
			done++;
			break;

		case TOK_ID:
/*
 *			This could either be a reference to a variable or an assignment
 */
			identifier = NULL;
			setString(B_L, &identifier, ep->token);
/*
 *			Peek ahead to see if this is an assignment
 */
			tid = ejLexGetToken(ep, state);
			if (tid == TOK_ASSIGNMENT) {
				if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) {
					clearString(&identifier);
					goto error;
				}
				if (flags & FLAGS_EXE) {
					if ( state == STATE_DEC ) {
						ejSetLocalVar(ep->eid, identifier, ep->result);
					} else {
						ejVarType = ejGetVar(ep->eid, identifier, &value);
						if (ejVarType > 0) {
							ejSetLocalVar(ep->eid, identifier, ep->result);
						} else {
							ejSetGlobalVar(ep->eid, identifier, ep->result);
						}
					}
				}

			} else if (tid == TOK_INC_DEC ) {
				value = NULL;
				if (flags & FLAGS_EXE) {
					ejVarType = ejGetVar(ep->eid, identifier, &value);
					if (ejVarType < 0) {
						ejError(ep, T("Undefined variable %s\n"), identifier);
						goto error;
					}
					setString(B_L, &ep->result, value);
					if (evalExpr(ep, value, (int) *ep->token, T("1")) < 0) {
						state = STATE_ERR;
						break;
					}

					if (ejVarType > 0) {
						ejSetLocalVar(ep->eid, identifier, ep->result);
					} else {
						ejSetGlobalVar(ep->eid, identifier, ep->result);
					}
				}

			} else {
/*
 *				If we are processing a declaration, allow undefined vars
 */
				value = NULL;
				if (state == STATE_DEC) {
					if (ejGetVar(ep->eid, identifier, &value) > 0) {
						ejError(ep, T("Variable already declared"),
							identifier);
						clearString(&identifier);
						goto error;
					}
					ejSetLocalVar(ep->eid, identifier, NULL);
				} else {
					if ( flags & FLAGS_EXE ) {
						if (ejGetVar(ep->eid, identifier, &value) < 0) {
							ejError(ep, T("Undefined variable %s\n"),
								identifier);
							clearString(&identifier);
							goto error;
						}
					}
				}
				setString(B_L, &ep->result, value);
				ejLexPutbackToken(ep, tid, ep->token);
			}
			clearString(&identifier);

			if (state == STATE_STMT) {
				expectSemi++;
			}
			done++;
			break;

		case TOK_LITERAL:
/*
 *			Set the result to the literal (number or string constant)
 */
			setString(B_L, &ep->result, ep->token);
			if (state == STATE_STMT) {
				expectSemi++;
			}
			done++;
			break;

		case TOK_FUNCTION:
/*
 *			We must save any current ep->func value for the current stack frame
 */
			if (ep->func) {
				saveFunc = ep->func;
			}
			memset(&func, 0, sizeof(ejfunc_t));
			setString(B_L, &func.fname, ep->token);
			ep->func = &func;

			setString(B_L, &ep->result, T(""));
			if (ejLexGetToken(ep, state) != TOK_LPAREN) {
				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.  Allow for RETURN statement
 */
			switch (parse(ep, STATE_STMT, thenFlags)) {
			case STATE_RET:
				return STATE_RET;
			case STATE_STMT_DONE:
				break;
			default:
				goto error;
			}
/*
 *			check to see if there is an "else" case
 */
			ejRemoveNewlines(ep, state);
			tid = ejLexGetToken(ep, state);
			if (tid != TOK_ELSE) {
				ejLexPutbackToken(ep, tid, ep->token);
				done++;
				break;
			}
/*
 *			Process the "else" case.  Allow for return.
 */
			switch (parse(ep, STATE_STMT, elseFlags)) {
			case STATE_RET:
				return STATE_RET;
			case STATE_STMT_DONE:
				break;
			default:
				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);

				switch (parse(ep, STATE_STMT, flags)) {
				case STATE_RET:
					return STATE_RET;
				case STATE_STMT_DONE:
					break;
				default:
					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);

/*
 *			Allow return statement.
 */
			if (state == STATE_RET) {
				return state;
			}

			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_RET;
			}
			break;
		}
	}

	if (expectSemi) {
		tid = ejLexGetToken(ep, state);
		if (tid != TOK_SEMI && tid != TOK_NEWLINE) {
			goto error;
		}

/*
 *		Skip newline after semi-colon
 */
		ejRemoveNewlines(ep, state);
	}

/*
 *	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;
}
Exemplo n.º 5
0
static int parse(ej_t *ep, int state, int flags)
{
	a_assert(ep);

	switch (state) {
/*
 *	Any statement, function arguments or conditional expressions
 */
	case STATE_STMT:
		if ((state = parseStmt(ep, state, flags)) != STATE_STMT_DONE &&
			state != STATE_EOF && state != STATE_STMT_BLOCK_DONE &&
			state != STATE_RET) {
			state = STATE_ERR;
		}
		break;

	case STATE_DEC:
		if ((state = parseStmt(ep, state, flags)) != STATE_DEC_DONE &&
			state != STATE_EOF) {
			state = STATE_ERR;
		}
		break;

	case STATE_EXPR:
		if ((state = parseStmt(ep, state, flags)) != STATE_EXPR_DONE &&
			state != STATE_EOF) {
			state = STATE_ERR;
		}
		break;

/*
 *	Variable declaration list
 */
	case STATE_DEC_LIST:
		state = parseDeclaration(ep, state, flags);
		break;

/*
 *	Function argument string
 */
	case STATE_ARG_LIST:
		state = parseArgs(ep, state, flags);
		break;

/*
 *	Logical condition list (relational operations separated by &&, ||)
 */
	case STATE_COND:
		state = parseCond(ep, state, flags);
		break;

/*
 *	Expression list
 */
	case STATE_RELEXP:
		state = parseExpr(ep, state, flags);
		break;
	}

	if (state == STATE_ERR && ep->error == NULL) {
		ejError(ep, T("Syntax error"));
	}
	return state;
}
Exemplo n.º 6
0
char_t *ejEval(int eid, char_t *script, char_t **emsg)
{
	ej_t	*ep;
	ejinput_t	*oldBlock;
	int		state;
	void	*endlessLoopTest;
	int		loopCounter;
	
	
	a_assert(script);

	if (emsg) {
		*emsg = NULL;
	} 

	if ((ep = ejPtr(eid)) == NULL) {
		return NULL;
	}

	setString(B_L, &ep->result, T(""));

/*
 *	Allocate a new evaluation block, and save the old one
 */
	oldBlock = ep->input;
	ejLexOpenScript(ep, script);

/*
 *	Do the actual parsing and evaluation
 */
	loopCounter = 0;
	endlessLoopTest = NULL;

	do {
		state = parse(ep, STATE_BEGIN, FLAGS_EXE);

		if (state == STATE_RET) {
			state = STATE_EOF;
		}
/*
 *		prevent parser from going into infinite loop.  If parsing the same
 *		line 10 times then fail and report Syntax error.  Most normal error
 *		are caught in the parser itself.
 */
		if (endlessLoopTest == ep->input->script.servp) {
			if (loopCounter++ > 10) {
				state = STATE_ERR;
				ejError(ep, T("Syntax error"));
			}
		} else {
			endlessLoopTest = ep->input->script.servp;
			loopCounter = 0;
		}
	} while (state != STATE_EOF && state != STATE_ERR);

	ejLexCloseScript(ep);

/*
 *	Return any error string to the user
 */
	if (state == STATE_ERR && emsg) {
		*emsg = bstrdup(B_L, ep->error);
	}

/*
 *	Restore the old evaluation block
 */
	ep->input = oldBlock;

	if (state == STATE_EOF) {
		return ep->result;
	}

	if (state == STATE_ERR) {
		return NULL;
	}

	return ep->result;
}
Exemplo n.º 7
0
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((int)*cp)) {
			numeric = 0;
			break;
		}
	}

	if (numeric) {
		for (cp = rhs; *cp; cp++) {
			if (!gisdigit((int)*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;
		case EXPR_BOOL_COMP:
			lval = (r == 0) ? 1 : 0;
			break;
		default:
			ejError(ep, T("Bad operator %d"), rel);
			return -1;
		}

	} else {
		switch (rel) {
Exemplo n.º 8
0
static int getLexicalToken(ej_t* ep, int state)
{
    ringq_t		*inq, *tokq;
    ejinput_t*	ip;
    int			done, tid, c, quote, style;

    a_assert(ep);
    ip = ep->input;
    a_assert(ip);

    inq = &ip->script;
    tokq = &ip->tokbuf;

    ep->tid = -1;
    tid = -1;
    ep->token = T("");

    ringqFlush(tokq);

    if (ip->putBackTokenId > 0) {
        ringqPutStr(tokq, ip->putBackToken);
        tid = ip->putBackTokenId;
        ip->putBackTokenId = 0;
        ep->token = (char_t*) tokq->servp;
        return tid;
    }

    if ((c = inputGetc(ep)) < 0) {
        return TOK_EOF;
    }

    for (done = 0; !done; ) {
        switch (c) {
        case -1:
            return TOK_EOF;

        case ' ':
        case '\t':
        case '\r':
            do {
                if ((c = inputGetc(ep)) < 0)
                    break;
            } while (c == ' ' || c == '\t' || c == '\r');
            break;

        case '\n':
            return TOK_NEWLINE;

        case '(':
            tokenAddChar(ep, c);
            return TOK_LPAREN;

        case ')':
            tokenAddChar(ep, c);
            return TOK_RPAREN;

        case '{':
            tokenAddChar(ep, c);
            return TOK_LBRACE;

        case '}':
            tokenAddChar(ep, c);
            return TOK_RBRACE;

        case '+':
            if ((c = inputGetc(ep)) < 0) {
                ejError(ep, T("Syntax Error"));
                return TOK_ERR;
            }
            if (c != '+' ) {
                inputPutback(ep, c);
                tokenAddChar(ep, EXPR_PLUS);
                return TOK_EXPR;
            }
            tokenAddChar(ep, EXPR_INC);
            return TOK_INC_DEC;

        case '-':
            if ((c = inputGetc(ep)) < 0) {
                ejError(ep, T("Syntax Error"));
                return TOK_ERR;
            }
            if (c != '-' ) {
                inputPutback(ep, c);
                tokenAddChar(ep, EXPR_MINUS);
                return TOK_EXPR;
            }
            tokenAddChar(ep, EXPR_DEC);
            return TOK_INC_DEC;

        case '*':
            tokenAddChar(ep, EXPR_MUL);
            return TOK_EXPR;

        case '%':
            tokenAddChar(ep, EXPR_MOD);
            return TOK_EXPR;

        case '/':
            /*
             *			Handle the division operator and comments
             */
            if ((c = inputGetc(ep)) < 0) {
                ejError(ep, T("Syntax Error"));
                return TOK_ERR;
            }
            if (c != '*' && c != '/') {
                inputPutback(ep, c);
                tokenAddChar(ep, EXPR_DIV);
                return TOK_EXPR;
            }
            style = c;
            /*
             *			Eat comments. Both C and C++ comment styles are supported.
             */
            while (1) {
                if ((c = inputGetc(ep)) < 0) {
                    ejError(ep, T("Syntax Error"));
                    return TOK_ERR;
                }
                if (c == '\n' && style == '/') {
                    break;
                } else if (c == '*') {
                    c = inputGetc(ep);
                    if (style == '/') {
                        if (c == '\n') {
                            break;
                        }
                    } else {
                        if (c == '/') {
                            break;
                        }
                    }
                }
            }
            /*
             *			Continue looking for a token, so get the next character
             */
            if ((c = inputGetc(ep)) < 0) {
                return TOK_EOF;
            }
            break;

        case '<':									/* < and <= */
            if ((c = inputGetc(ep)) < 0) {
                ejError(ep, T("Syntax Error"));
                return TOK_ERR;
            }
            if (c == '<') {
                tokenAddChar(ep, EXPR_LSHIFT);
                return TOK_EXPR;
            } else if (c == '=') {
                tokenAddChar(ep, EXPR_LESSEQ);
                return TOK_EXPR;
            }
            tokenAddChar(ep, EXPR_LESS);
            inputPutback(ep, c);
            return TOK_EXPR;

        case '>':									/* > and >= */
            if ((c = inputGetc(ep)) < 0) {
                ejError(ep, T("Syntax Error"));
                return TOK_ERR;
            }
            if (c == '>') {
                tokenAddChar(ep, EXPR_RSHIFT);
                return TOK_EXPR;
            } else if (c == '=') {
                tokenAddChar(ep, EXPR_GREATEREQ);
                return TOK_EXPR;
            }
            tokenAddChar(ep, EXPR_GREATER);
            inputPutback(ep, c);
            return TOK_EXPR;

        case '=':									/* "==" */
            if ((c = inputGetc(ep)) < 0) {
                ejError(ep, T("Syntax Error"));
                return TOK_ERR;
            }
            if (c == '=') {
                tokenAddChar(ep, EXPR_EQ);
                return TOK_EXPR;
            }
            inputPutback(ep, c);
            return TOK_ASSIGNMENT;

        case '!':									/* "!=" or "!"*/
            if ((c = inputGetc(ep)) < 0) {
                ejError(ep, T("Syntax Error"));
                return TOK_ERR;
            }
            if (c == '=') {
                tokenAddChar(ep, EXPR_NOTEQ);
                return TOK_EXPR;
            }
            inputPutback(ep, c);
            tokenAddChar(ep, EXPR_BOOL_COMP);
            return TOK_EXPR;

        case ';':
            tokenAddChar(ep, c);
            return TOK_SEMI;

        case ',':
            tokenAddChar(ep, c);
            return TOK_COMMA;

        case '|':									/* "||" */
            if ((c = inputGetc(ep)) < 0 || c != '|') {
                ejError(ep, T("Syntax Error"));
                return TOK_ERR;
            }
            tokenAddChar(ep, COND_OR);
            return TOK_LOGICAL;

        case '&':									/* "&&" */
            if ((c = inputGetc(ep)) < 0 || c != '&') {
                ejError(ep, T("Syntax Error"));
                return TOK_ERR;
            }
            tokenAddChar(ep, COND_AND);
            return TOK_LOGICAL;

        case '\"':									/* String quote */
        case '\'':
            quote = c;
            if ((c = inputGetc(ep)) < 0) {
                ejError(ep, T("Syntax Error"));
                return TOK_ERR;
            }

            while (c != quote) {
                /*
                 *				check for escape sequence characters
                 */
                if (c == '\\') {
                    c = inputGetc(ep);

                    if (gisdigit(c)) {
                        /*
                         *						octal support, \101 maps to 65 = 'A'. put first char
                         *						back so converter will work properly.
                         */
                        inputPutback(ep, c);
                        c = charConvert(ep, OCTAL, 3);

                    } else {
                        switch (c) {
                        case 'n':
                            c = '\n';
                            break;
                        case 'b':
                            c = '\b';
                            break;
                        case 'f':
                            c = '\f';
                            break;
                        case 'r':
                            c = '\r';
                            break;
                        case 't':
                            c = '\t';
                            break;
                        case 'x':
                            /*
                             *							hex support, \x41 maps to 65 = 'A'
                             */
                            c = charConvert(ep, HEX, 2);
                            break;
                        case 'u':
                            /*
                             *							unicode support, \x0401 maps to 65 = 'A'
                             */
                            c = charConvert(ep, HEX, 2);
                            c = c*16 + charConvert(ep, HEX, 2);

                            break;
                        case '\'':
                        case '\"':
                        case '\\':
                            break;
                        default:
                            ejError(ep, T("Invalid Escape Sequence"));
                            return TOK_ERR;
                        }
                    }
                    if (tokenAddChar(ep, c) < 0) {
                        return TOK_ERR;
                    }
                } else {
                    if (tokenAddChar(ep, c) < 0) {
                        return TOK_ERR;
                    }
                }
                if ((c = inputGetc(ep)) < 0) {
                    ejError(ep, T("Unmatched Quote"));
                    return TOK_ERR;
                }
            }
            return TOK_LITERAL;

        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            do {
                if (tokenAddChar(ep, c) < 0) {
                    return TOK_ERR;
                }
                if ((c = inputGetc(ep)) < 0)
                    break;
            } while (gisdigit(c));
            inputPutback(ep, c);
            return TOK_LITERAL;

        default:
            /*
             *			Identifiers or a function names
             */
            while (1) {
                if (c == '\\') {
                    /*
                     *					just ignore any \ characters.
                     */
                } else if (tokenAddChar(ep, c) < 0) {
                    break;
                }
                if ((c = inputGetc(ep)) < 0) {
                    break;
                }
                if (!gisalnum(c) && c != '$' && c != '_' &&
                    c != '\\') {
                    break;
                }
            }
            if (! gisalpha(*tokq->servp) && *tokq->servp != '$' &&
                *tokq->servp != '_') {
                ejError(ep, T("Invalid identifier %s"), tokq->servp);
                return TOK_ERR;
            }
            /*
             *			Check for reserved words (only "if", "else", "var", "for"
             *			and "return" at the moment)
             */
            if (state == STATE_STMT) {
                if (gstrcmp(ep->token, T("if")) == 0) {
                    return TOK_IF;
                } else if (gstrcmp(ep->token, T("else")) == 0) {
                    return TOK_ELSE;
                } else if (gstrcmp(ep->token, T("var")) == 0) {
                    return TOK_VAR;
                } else if (gstrcmp(ep->token, T("for")) == 0) {
                    return TOK_FOR;
                } else if (gstrcmp(ep->token, T("return")) == 0) {
                    if ((c == ';') || (c == '(')) {
                        inputPutback(ep, c);
                    }
                    return TOK_RETURN;
                }
            }

            /*
             * 			Skip white space after token to find out whether this is
             * 			a function or not.
             */
            while (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
                if ((c = inputGetc(ep)) < 0)
                    break;
            }

            tid = (c == '(') ? TOK_FUNCTION : TOK_ID;
            done++;
        }
    }

    /*
     *	Putback the last extra character for next time
     */
    inputPutback(ep, c);
    return tid;
}