/*
|
/*
|
* ejlex.c -- Ejscript(TM) Lexical Analyser
|
* ejlex.c -- Ejscript(TM) Lexical Analyser
|
*
|
*
|
* 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 lexical analyser. This implementes a lexical analyser for a
|
* Ejscript lexical analyser. This implementes a lexical analyser for a
|
* a subset of the JavaScript language.
|
* a subset of the JavaScript language.
|
*/
|
*/
|
|
|
/********************************** Includes **********************************/
|
/********************************** Includes **********************************/
|
|
|
#include "ej.h"
|
#include "ej.h"
|
|
|
#if UEMF
|
#if UEMF
|
#include "uemf.h"
|
#include "uemf.h"
|
#else
|
#else
|
#include "basic/basicInternal.h"
|
#include "basic/basicInternal.h"
|
#endif
|
#endif
|
|
|
/****************************** Forward Declarations **************************/
|
/****************************** Forward Declarations **************************/
|
|
|
static int getLexicalToken(ej_t* ep, int state);
|
static int getLexicalToken(ej_t* ep, int state);
|
static int tokenAddChar(ej_t *ep, int c);
|
static int tokenAddChar(ej_t *ep, int c);
|
static int inputGetc(ej_t* ep);
|
static int inputGetc(ej_t* ep);
|
static void inputPutback(ej_t* ep, int c);
|
static void inputPutback(ej_t* ep, int c);
|
|
|
/************************************* Code ***********************************/
|
/************************************* Code ***********************************/
|
/*
|
/*
|
* Setup the lexical analyser
|
* Setup the lexical analyser
|
*/
|
*/
|
|
|
int ejLexOpen(ej_t* ep)
|
int ejLexOpen(ej_t* ep)
|
{
|
{
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Close the lexicial analyser
|
* Close the lexicial analyser
|
*/
|
*/
|
|
|
void ejLexClose(ej_t* ep)
|
void ejLexClose(ej_t* ep)
|
{
|
{
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Open a new input script
|
* Open a new input script
|
*/
|
*/
|
|
|
int ejLexOpenScript(ej_t* ep, char_t *script)
|
int ejLexOpenScript(ej_t* ep, char_t *script)
|
{
|
{
|
ejinput_t *ip;
|
ejinput_t *ip;
|
|
|
a_assert(ep);
|
a_assert(ep);
|
a_assert(script);
|
a_assert(script);
|
|
|
if ((ep->input = balloc(B_L, sizeof(ejinput_t))) == NULL) {
|
if ((ep->input = balloc(B_L, sizeof(ejinput_t))) == NULL) {
|
return -1;
|
return -1;
|
}
|
}
|
ip = ep->input;
|
ip = ep->input;
|
memset(ip, 0, sizeof(*ip));
|
memset(ip, 0, sizeof(*ip));
|
|
|
a_assert(ip);
|
a_assert(ip);
|
a_assert(ip->putBackToken == NULL);
|
a_assert(ip->putBackToken == NULL);
|
a_assert(ip->putBackTokenId == 0);
|
a_assert(ip->putBackTokenId == 0);
|
|
|
/*
|
/*
|
* Create the parse token buffer and script buffer
|
* Create the parse token buffer and script buffer
|
*/
|
*/
|
if (ringqOpen(&ip->tokbuf, EJ_INC, -1) < 0) {
|
if (ringqOpen(&ip->tokbuf, EJ_INC, -1) < 0) {
|
return -1;
|
return -1;
|
}
|
}
|
if (ringqOpen(&ip->script, EJ_INC, -1) < 0) {
|
if (ringqOpen(&ip->script, EJ_INC, -1) < 0) {
|
return -1;
|
return -1;
|
}
|
}
|
/*
|
/*
|
* Put the Ejscript into a ring queue for easy parsing
|
* Put the Ejscript into a ring queue for easy parsing
|
*/
|
*/
|
ringqPutstr(&ip->script, script);
|
ringqPutstr(&ip->script, script);
|
|
|
ip->lineNumber = 1;
|
ip->lineNumber = 1;
|
ip->lineLength = 0;
|
ip->lineLength = 0;
|
ip->lineColumn = 0;
|
ip->lineColumn = 0;
|
ip->line = NULL;
|
ip->line = NULL;
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Close the input script
|
* Close the input script
|
*/
|
*/
|
|
|
void ejLexCloseScript(ej_t* ep)
|
void ejLexCloseScript(ej_t* ep)
|
{
|
{
|
ejinput_t *ip;
|
ejinput_t *ip;
|
|
|
a_assert(ep);
|
a_assert(ep);
|
|
|
ip = ep->input;
|
ip = ep->input;
|
a_assert(ip);
|
a_assert(ip);
|
|
|
if (ip->putBackToken) {
|
if (ip->putBackToken) {
|
bfree(B_L, ip->putBackToken);
|
bfree(B_L, ip->putBackToken);
|
ip->putBackToken = NULL;
|
ip->putBackToken = NULL;
|
}
|
}
|
ip->putBackTokenId = 0;
|
ip->putBackTokenId = 0;
|
|
|
if (ip->line) {
|
if (ip->line) {
|
bfree(B_L, ip->line);
|
bfree(B_L, ip->line);
|
ip->line = NULL;
|
ip->line = NULL;
|
}
|
}
|
|
|
ringqClose(&ip->tokbuf);
|
ringqClose(&ip->tokbuf);
|
ringqClose(&ip->script);
|
ringqClose(&ip->script);
|
|
|
bfree(B_L, ip);
|
bfree(B_L, ip);
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Save the input state
|
* Save the input state
|
*/
|
*/
|
|
|
void ejLexSaveInputState(ej_t* ep, ejinput_t* state)
|
void ejLexSaveInputState(ej_t* ep, ejinput_t* state)
|
{
|
{
|
ejinput_t *ip;
|
ejinput_t *ip;
|
|
|
a_assert(ep);
|
a_assert(ep);
|
|
|
ip = ep->input;
|
ip = ep->input;
|
a_assert(ip);
|
a_assert(ip);
|
|
|
*state = *ip;
|
*state = *ip;
|
if (ip->putBackToken) {
|
if (ip->putBackToken) {
|
state->putBackToken = bstrdup(B_L, ip->putBackToken);
|
state->putBackToken = bstrdup(B_L, ip->putBackToken);
|
}
|
}
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Restore the input state
|
* Restore the input state
|
*/
|
*/
|
|
|
void ejLexRestoreInputState(ej_t* ep, ejinput_t* state)
|
void ejLexRestoreInputState(ej_t* ep, ejinput_t* state)
|
{
|
{
|
ejinput_t *ip;
|
ejinput_t *ip;
|
|
|
a_assert(ep);
|
a_assert(ep);
|
|
|
ip = ep->input;
|
ip = ep->input;
|
a_assert(ip);
|
a_assert(ip);
|
|
|
ip->tokbuf = state->tokbuf;
|
ip->tokbuf = state->tokbuf;
|
ip->script = state->script;
|
ip->script = state->script;
|
ip->putBackTokenId = state->putBackTokenId;
|
ip->putBackTokenId = state->putBackTokenId;
|
if (ip->putBackToken) {
|
if (ip->putBackToken) {
|
bfree(B_L, ip->putBackToken);
|
bfree(B_L, ip->putBackToken);
|
}
|
}
|
if (state->putBackToken) {
|
if (state->putBackToken) {
|
ip->putBackToken = bstrdup(B_L, state->putBackToken);
|
ip->putBackToken = bstrdup(B_L, state->putBackToken);
|
}
|
}
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Free a saved input state
|
* Free a saved input state
|
*/
|
*/
|
|
|
void ejLexFreeInputState(ej_t* ep, ejinput_t* state)
|
void ejLexFreeInputState(ej_t* ep, ejinput_t* state)
|
{
|
{
|
if (state->putBackToken) {
|
if (state->putBackToken) {
|
bfree(B_L, state->putBackToken);
|
bfree(B_L, state->putBackToken);
|
}
|
}
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Get the next Ejscript token
|
* Get the next Ejscript token
|
*/
|
*/
|
|
|
int ejLexGetToken(ej_t* ep, int state)
|
int ejLexGetToken(ej_t* ep, int state)
|
{
|
{
|
ep->tid = getLexicalToken(ep, state);
|
ep->tid = getLexicalToken(ep, state);
|
goahead_trace(7, T("ejGetToken: %d, \"%s\"\n"), ep->tid, ep->token);
|
goahead_trace(7, T("ejGetToken: %d, \"%s\"\n"), ep->tid, ep->token);
|
return ep->tid;
|
return ep->tid;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Get the next Ejscript token
|
* Get the next Ejscript token
|
*/
|
*/
|
|
|
static int getLexicalToken(ej_t* ep, int state)
|
static int getLexicalToken(ej_t* ep, int state)
|
{
|
{
|
ringq_t *inq, *tokq;
|
ringq_t *inq, *tokq;
|
ejinput_t* ip;
|
ejinput_t* ip;
|
int done, tid, c, quote, style, back_quoted, lval, i;
|
int done, tid, c, quote, style, back_quoted, lval, i;
|
|
|
a_assert(ep);
|
a_assert(ep);
|
ip = ep->input;
|
ip = ep->input;
|
a_assert(ip);
|
a_assert(ip);
|
|
|
inq = &ip->script;
|
inq = &ip->script;
|
tokq = &ip->tokbuf;
|
tokq = &ip->tokbuf;
|
|
|
ep->tid = -1;
|
ep->tid = -1;
|
tid = -1;
|
tid = -1;
|
ep->token = T("");
|
ep->token = T("");
|
|
|
ringqFlush(tokq);
|
ringqFlush(tokq);
|
|
|
if (ip->putBackTokenId > 0) {
|
if (ip->putBackTokenId > 0) {
|
ringqPutstr(tokq, ip->putBackToken);
|
ringqPutstr(tokq, ip->putBackToken);
|
tid = ip->putBackTokenId;
|
tid = ip->putBackTokenId;
|
ip->putBackTokenId = 0;
|
ip->putBackTokenId = 0;
|
ep->token = (char_t*) tokq->servp;
|
ep->token = (char_t*) tokq->servp;
|
return tid;
|
return tid;
|
}
|
}
|
|
|
if ((c = inputGetc(ep)) < 0) {
|
if ((c = inputGetc(ep)) < 0) {
|
return TOK_EOF;
|
return TOK_EOF;
|
}
|
}
|
|
|
for (done = 0; !done; ) {
|
for (done = 0; !done; ) {
|
switch (c) {
|
switch (c) {
|
case -1:
|
case -1:
|
return TOK_EOF;
|
return TOK_EOF;
|
|
|
case ' ':
|
case ' ':
|
case '\t':
|
case '\t':
|
case '\r':
|
case '\r':
|
do {
|
do {
|
if ((c = inputGetc(ep)) < 0)
|
if ((c = inputGetc(ep)) < 0)
|
break;
|
break;
|
} while (c == ' ' || c == '\t' || c == '\r');
|
} while (c == ' ' || c == '\t' || c == '\r');
|
break;
|
break;
|
|
|
case '\n':
|
case '\n':
|
return TOK_NEWLINE;
|
return TOK_NEWLINE;
|
|
|
case '(':
|
case '(':
|
tokenAddChar(ep, c);
|
tokenAddChar(ep, c);
|
return TOK_LPAREN;
|
return TOK_LPAREN;
|
|
|
case ')':
|
case ')':
|
tokenAddChar(ep, c);
|
tokenAddChar(ep, c);
|
return TOK_RPAREN;
|
return TOK_RPAREN;
|
|
|
case '{':
|
case '{':
|
tokenAddChar(ep, c);
|
tokenAddChar(ep, c);
|
return TOK_LBRACE;
|
return TOK_LBRACE;
|
|
|
case '}':
|
case '}':
|
tokenAddChar(ep, c);
|
tokenAddChar(ep, c);
|
return TOK_RBRACE;
|
return TOK_RBRACE;
|
|
|
case '+':
|
case '+':
|
if ((c = inputGetc(ep)) < 0) {
|
if ((c = inputGetc(ep)) < 0) {
|
ejError(ep, T("Syntax Error"));
|
ejError(ep, T("Syntax Error"));
|
return TOK_ERR;
|
return TOK_ERR;
|
}
|
}
|
if (c != '+' ) {
|
if (c != '+' ) {
|
inputPutback(ep, c);
|
inputPutback(ep, c);
|
tokenAddChar(ep, EXPR_PLUS);
|
tokenAddChar(ep, EXPR_PLUS);
|
return TOK_EXPR;
|
return TOK_EXPR;
|
}
|
}
|
tokenAddChar(ep, EXPR_INC);
|
tokenAddChar(ep, EXPR_INC);
|
return TOK_INC_DEC;
|
return TOK_INC_DEC;
|
|
|
case '-':
|
case '-':
|
if ((c = inputGetc(ep)) < 0) {
|
if ((c = inputGetc(ep)) < 0) {
|
ejError(ep, T("Syntax Error"));
|
ejError(ep, T("Syntax Error"));
|
return TOK_ERR;
|
return TOK_ERR;
|
}
|
}
|
if (c != '-' ) {
|
if (c != '-' ) {
|
inputPutback(ep, c);
|
inputPutback(ep, c);
|
tokenAddChar(ep, EXPR_MINUS);
|
tokenAddChar(ep, EXPR_MINUS);
|
return TOK_EXPR;
|
return TOK_EXPR;
|
}
|
}
|
tokenAddChar(ep, EXPR_DEC);
|
tokenAddChar(ep, EXPR_DEC);
|
return TOK_INC_DEC;
|
return TOK_INC_DEC;
|
|
|
case '*':
|
case '*':
|
tokenAddChar(ep, EXPR_MUL);
|
tokenAddChar(ep, EXPR_MUL);
|
return TOK_EXPR;
|
return TOK_EXPR;
|
|
|
case '%':
|
case '%':
|
tokenAddChar(ep, EXPR_MOD);
|
tokenAddChar(ep, EXPR_MOD);
|
return TOK_EXPR;
|
return TOK_EXPR;
|
|
|
case '/':
|
case '/':
|
/*
|
/*
|
* Handle the division operator and comments
|
* Handle the division operator and comments
|
*/
|
*/
|
if ((c = inputGetc(ep)) < 0) {
|
if ((c = inputGetc(ep)) < 0) {
|
ejError(ep, T("Syntax Error"));
|
ejError(ep, T("Syntax Error"));
|
return TOK_ERR;
|
return TOK_ERR;
|
}
|
}
|
if (c != '*' && c != '/') {
|
if (c != '*' && c != '/') {
|
inputPutback(ep, c);
|
inputPutback(ep, c);
|
tokenAddChar(ep, EXPR_DIV);
|
tokenAddChar(ep, EXPR_DIV);
|
return TOK_EXPR;
|
return TOK_EXPR;
|
}
|
}
|
style = c;
|
style = c;
|
/*
|
/*
|
* Eat comments. Both C and C++ comment styles are supported.
|
* Eat comments. Both C and C++ comment styles are supported.
|
*/
|
*/
|
while (1) {
|
while (1) {
|
if ((c = inputGetc(ep)) < 0) {
|
if ((c = inputGetc(ep)) < 0) {
|
ejError(ep, T("Syntax Error"));
|
ejError(ep, T("Syntax Error"));
|
return TOK_ERR;
|
return TOK_ERR;
|
}
|
}
|
if (c == '\n' && style == '/') {
|
if (c == '\n' && style == '/') {
|
break;
|
break;
|
} else if (c == '*') {
|
} else if (c == '*') {
|
c = inputGetc(ep);
|
c = inputGetc(ep);
|
if (style == '/') {
|
if (style == '/') {
|
if (c == '\n') {
|
if (c == '\n') {
|
break;
|
break;
|
}
|
}
|
} else {
|
} else {
|
if (c == '/') {
|
if (c == '/') {
|
break;
|
break;
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
/*
|
/*
|
* Continue looking for a token, so get the next character
|
* Continue looking for a token, so get the next character
|
*/
|
*/
|
if ((c = inputGetc(ep)) < 0) {
|
if ((c = inputGetc(ep)) < 0) {
|
return TOK_EOF;
|
return TOK_EOF;
|
}
|
}
|
break;
|
break;
|
|
|
case '<': /* < and <= */
|
case '<': /* < and <= */
|
if ((c = inputGetc(ep)) < 0) {
|
if ((c = inputGetc(ep)) < 0) {
|
ejError(ep, T("Syntax Error"));
|
ejError(ep, T("Syntax Error"));
|
return TOK_ERR;
|
return TOK_ERR;
|
}
|
}
|
if (c == '<') {
|
if (c == '<') {
|
tokenAddChar(ep, EXPR_LSHIFT);
|
tokenAddChar(ep, EXPR_LSHIFT);
|
return TOK_EXPR;
|
return TOK_EXPR;
|
} else if (c == '=') {
|
} else if (c == '=') {
|
tokenAddChar(ep, EXPR_LESSEQ);
|
tokenAddChar(ep, EXPR_LESSEQ);
|
return TOK_EXPR;
|
return TOK_EXPR;
|
}
|
}
|
tokenAddChar(ep, EXPR_LESS);
|
tokenAddChar(ep, EXPR_LESS);
|
inputPutback(ep, c);
|
inputPutback(ep, c);
|
return TOK_EXPR;
|
return TOK_EXPR;
|
|
|
case '>': /* > and >= */
|
case '>': /* > and >= */
|
if ((c = inputGetc(ep)) < 0) {
|
if ((c = inputGetc(ep)) < 0) {
|
ejError(ep, T("Syntax Error"));
|
ejError(ep, T("Syntax Error"));
|
return TOK_ERR;
|
return TOK_ERR;
|
}
|
}
|
if (c == '>') {
|
if (c == '>') {
|
tokenAddChar(ep, EXPR_RSHIFT);
|
tokenAddChar(ep, EXPR_RSHIFT);
|
return TOK_EXPR;
|
return TOK_EXPR;
|
} else if (c == '=') {
|
} else if (c == '=') {
|
tokenAddChar(ep, EXPR_GREATEREQ);
|
tokenAddChar(ep, EXPR_GREATEREQ);
|
return TOK_EXPR;
|
return TOK_EXPR;
|
}
|
}
|
tokenAddChar(ep, EXPR_GREATER);
|
tokenAddChar(ep, EXPR_GREATER);
|
inputPutback(ep, c);
|
inputPutback(ep, c);
|
return TOK_EXPR;
|
return TOK_EXPR;
|
|
|
case '=': /* "==" */
|
case '=': /* "==" */
|
if ((c = inputGetc(ep)) < 0) {
|
if ((c = inputGetc(ep)) < 0) {
|
ejError(ep, T("Syntax Error"));
|
ejError(ep, T("Syntax Error"));
|
return TOK_ERR;
|
return TOK_ERR;
|
}
|
}
|
if (c == '=') {
|
if (c == '=') {
|
tokenAddChar(ep, EXPR_EQ);
|
tokenAddChar(ep, EXPR_EQ);
|
return TOK_EXPR;
|
return TOK_EXPR;
|
}
|
}
|
inputPutback(ep, c);
|
inputPutback(ep, c);
|
return TOK_ASSIGNMENT;
|
return TOK_ASSIGNMENT;
|
|
|
case '!': /* "!=" */
|
case '!': /* "!=" */
|
if ((c = inputGetc(ep)) < 0) {
|
if ((c = inputGetc(ep)) < 0) {
|
ejError(ep, T("Syntax Error"));
|
ejError(ep, T("Syntax Error"));
|
return TOK_ERR;
|
return TOK_ERR;
|
}
|
}
|
if (c == '=') {
|
if (c == '=') {
|
tokenAddChar(ep, EXPR_NOTEQ);
|
tokenAddChar(ep, EXPR_NOTEQ);
|
return TOK_EXPR;
|
return TOK_EXPR;
|
}
|
}
|
tokenAddChar(ep, COND_NOT);
|
tokenAddChar(ep, COND_NOT);
|
return TOK_LOGICAL;
|
return TOK_LOGICAL;
|
|
|
case ';':
|
case ';':
|
tokenAddChar(ep, c);
|
tokenAddChar(ep, c);
|
return TOK_SEMI;
|
return TOK_SEMI;
|
|
|
case ',':
|
case ',':
|
tokenAddChar(ep, c);
|
tokenAddChar(ep, c);
|
return TOK_COMMA;
|
return TOK_COMMA;
|
|
|
case '|': /* "||" */
|
case '|': /* "||" */
|
if ((c = inputGetc(ep)) < 0 || c != '|') {
|
if ((c = inputGetc(ep)) < 0 || c != '|') {
|
ejError(ep, T("Syntax Error"));
|
ejError(ep, T("Syntax Error"));
|
return TOK_ERR;
|
return TOK_ERR;
|
}
|
}
|
tokenAddChar(ep, COND_OR);
|
tokenAddChar(ep, COND_OR);
|
return TOK_LOGICAL;
|
return TOK_LOGICAL;
|
|
|
case '&': /* "&&" */
|
case '&': /* "&&" */
|
if ((c = inputGetc(ep)) < 0 || c != '&') {
|
if ((c = inputGetc(ep)) < 0 || c != '&') {
|
ejError(ep, T("Syntax Error"));
|
ejError(ep, T("Syntax Error"));
|
return TOK_ERR;
|
return TOK_ERR;
|
}
|
}
|
tokenAddChar(ep, COND_AND);
|
tokenAddChar(ep, COND_AND);
|
return TOK_LOGICAL;
|
return TOK_LOGICAL;
|
|
|
case '\"': /* String quote */
|
case '\"': /* String quote */
|
case '\'':
|
case '\'':
|
quote = c;
|
quote = c;
|
if ((c = inputGetc(ep)) < 0) {
|
if ((c = inputGetc(ep)) < 0) {
|
ejError(ep, T("Syntax Error"));
|
ejError(ep, T("Syntax Error"));
|
return TOK_ERR;
|
return TOK_ERR;
|
}
|
}
|
back_quoted = 0;
|
back_quoted = 0;
|
while (c != quote) {
|
while (c != quote) {
|
if (c == '\\' && !back_quoted) {
|
if (c == '\\' && !back_quoted) {
|
back_quoted++;
|
back_quoted++;
|
} else if (back_quoted) {
|
} else if (back_quoted) {
|
if (gisdigit((char_t) c)) {
|
if (gisdigit((char_t) c)) {
|
lval = 0;
|
lval = 0;
|
for (i = 0; i < 3; i++) {
|
for (i = 0; i < 3; i++) {
|
if ('0' <= c && c <= '7') {
|
if ('0' <= c && c <= '7') {
|
break;
|
break;
|
}
|
}
|
lval = lval * 8 + c;
|
lval = lval * 8 + c;
|
if ((c = inputGetc(ep)) < 0) {
|
if ((c = inputGetc(ep)) < 0) {
|
break;
|
break;
|
}
|
}
|
}
|
}
|
c = (int) lval;
|
c = (int) lval;
|
|
|
} else if (back_quoted) {
|
} else if (back_quoted) {
|
switch (c) {
|
switch (c) {
|
case 'n':
|
case 'n':
|
c = '\n'; break;
|
c = '\n'; break;
|
case 'b':
|
case 'b':
|
c = '\b'; break;
|
c = '\b'; break;
|
case 'f':
|
case 'f':
|
c = '\f'; break;
|
c = '\f'; break;
|
case 'r':
|
case 'r':
|
c = '\r'; break;
|
c = '\r'; break;
|
case 't':
|
case 't':
|
c = '\t'; break;
|
c = '\t'; break;
|
case 'x':
|
case 'x':
|
lval = 0;
|
lval = 0;
|
for (i = 0; i < 2; i++) {
|
for (i = 0; i < 2; i++) {
|
if (! gisxdigit((char_t) c)) {
|
if (! gisxdigit((char_t) c)) {
|
break;
|
break;
|
}
|
}
|
lval = lval * 16 + c;
|
lval = lval * 16 + c;
|
if ((c = inputGetc(ep)) < 0) {
|
if ((c = inputGetc(ep)) < 0) {
|
break;
|
break;
|
}
|
}
|
}
|
}
|
c = (int) lval;
|
c = (int) lval;
|
break;
|
break;
|
case 'u':
|
case 'u':
|
lval = 0;
|
lval = 0;
|
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
if (! gisxdigit((char_t) c)) {
|
if (! gisxdigit((char_t) c)) {
|
break;
|
break;
|
}
|
}
|
lval = lval * 16 + c;
|
lval = lval * 16 + c;
|
if ((c = inputGetc(ep)) < 0) {
|
if ((c = inputGetc(ep)) < 0) {
|
break;
|
break;
|
}
|
}
|
}
|
}
|
c = (int) lval;
|
c = (int) lval;
|
break;
|
break;
|
case '\'':
|
case '\'':
|
case '\"':
|
case '\"':
|
break;
|
break;
|
}
|
}
|
}
|
}
|
back_quoted = 0;
|
back_quoted = 0;
|
if (tokenAddChar(ep, c) < 0) {
|
if (tokenAddChar(ep, c) < 0) {
|
return TOK_ERR;
|
return TOK_ERR;
|
}
|
}
|
} else {
|
} else {
|
if (tokenAddChar(ep, c) < 0) {
|
if (tokenAddChar(ep, c) < 0) {
|
return TOK_ERR;
|
return TOK_ERR;
|
}
|
}
|
}
|
}
|
if ((c = inputGetc(ep)) < 0) {
|
if ((c = inputGetc(ep)) < 0) {
|
ejError(ep, T("Unmatched Quote"));
|
ejError(ep, T("Unmatched Quote"));
|
return TOK_ERR;
|
return TOK_ERR;
|
}
|
}
|
}
|
}
|
return TOK_LITERAL;
|
return TOK_LITERAL;
|
|
|
case '0': case '1': case '2': case '3': case '4':
|
case '0': case '1': case '2': case '3': case '4':
|
case '5': case '6': case '7': case '8': case '9':
|
case '5': case '6': case '7': case '8': case '9':
|
do {
|
do {
|
if (tokenAddChar(ep, c) < 0) {
|
if (tokenAddChar(ep, c) < 0) {
|
return TOK_ERR;
|
return TOK_ERR;
|
}
|
}
|
if ((c = inputGetc(ep)) < 0)
|
if ((c = inputGetc(ep)) < 0)
|
break;
|
break;
|
} while (gisdigit((char_t) c));
|
} while (gisdigit((char_t) c));
|
inputPutback(ep, c);
|
inputPutback(ep, c);
|
return TOK_LITERAL;
|
return TOK_LITERAL;
|
|
|
default:
|
default:
|
/*
|
/*
|
* Identifiers or a function names
|
* Identifiers or a function names
|
*/
|
*/
|
back_quoted = 0;
|
back_quoted = 0;
|
while (1) {
|
while (1) {
|
if (c == '\\' && !back_quoted) {
|
if (c == '\\' && !back_quoted) {
|
back_quoted++;
|
back_quoted++;
|
} else {
|
} else {
|
back_quoted = 0;
|
back_quoted = 0;
|
if (tokenAddChar(ep, c) < 0) {
|
if (tokenAddChar(ep, c) < 0) {
|
break;
|
break;
|
}
|
}
|
}
|
}
|
if ((c = inputGetc(ep)) < 0) {
|
if ((c = inputGetc(ep)) < 0) {
|
break;
|
break;
|
}
|
}
|
if (!back_quoted && (!gisalnum((char_t) c) && c != '$' &&
|
if (!back_quoted && (!gisalnum((char_t) c) && c != '$' &&
|
c != '_')) {
|
c != '_')) {
|
break;
|
break;
|
}
|
}
|
}
|
}
|
if (! gisalpha(*tokq->servp) && *tokq->servp != '$' &&
|
if (! gisalpha(*tokq->servp) && *tokq->servp != '$' &&
|
*tokq->servp != '_') {
|
*tokq->servp != '_') {
|
ejError(ep, T("Invalid identifier %s"), tokq->servp);
|
ejError(ep, T("Invalid identifier %s"), tokq->servp);
|
return TOK_ERR;
|
return TOK_ERR;
|
}
|
}
|
/*
|
/*
|
* Check for reserved words (only "if", "else", "var", "for"
|
* Check for reserved words (only "if", "else", "var", "for"
|
* and "return" at the moment)
|
* and "return" at the moment)
|
*/
|
*/
|
if (state == STATE_STMT) {
|
if (state == STATE_STMT) {
|
if (gstrcmp(ep->token, T("if")) == 0) {
|
if (gstrcmp(ep->token, T("if")) == 0) {
|
return TOK_IF;
|
return TOK_IF;
|
} else if (gstrcmp(ep->token, T("else")) == 0) {
|
} else if (gstrcmp(ep->token, T("else")) == 0) {
|
return TOK_ELSE;
|
return TOK_ELSE;
|
} else if (gstrcmp(ep->token, T("var")) == 0) {
|
} else if (gstrcmp(ep->token, T("var")) == 0) {
|
return TOK_VAR;
|
return TOK_VAR;
|
} else if (gstrcmp(ep->token, T("for")) == 0) {
|
} else if (gstrcmp(ep->token, T("for")) == 0) {
|
return TOK_FOR;
|
return TOK_FOR;
|
} else if (gstrcmp(ep->token, T("return")) == 0) {
|
} else if (gstrcmp(ep->token, T("return")) == 0) {
|
return TOK_RETURN;
|
return TOK_RETURN;
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
* skip white space after token to find out whether this is
|
* skip white space after token to find out whether this is
|
* a function or not.
|
* a function or not.
|
*/
|
*/
|
while (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
|
while (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
|
if ((c = inputGetc(ep)) < 0)
|
if ((c = inputGetc(ep)) < 0)
|
break;
|
break;
|
}
|
}
|
|
|
tid = (c == '(') ? TOK_FUNCTION : TOK_ID;
|
tid = (c == '(') ? TOK_FUNCTION : TOK_ID;
|
done++;
|
done++;
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
* Putback the last extra character for next time
|
* Putback the last extra character for next time
|
*/
|
*/
|
inputPutback(ep, c);
|
inputPutback(ep, c);
|
return tid;
|
return tid;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Putback the last token read
|
* Putback the last token read
|
*/
|
*/
|
|
|
void ejLexPutbackToken(ej_t* ep, int tid, char_t *string)
|
void ejLexPutbackToken(ej_t* ep, int tid, char_t *string)
|
{
|
{
|
ejinput_t* ip;
|
ejinput_t* ip;
|
|
|
a_assert(ep);
|
a_assert(ep);
|
ip = ep->input;
|
ip = ep->input;
|
a_assert(ip);
|
a_assert(ip);
|
|
|
if (ip->putBackToken) {
|
if (ip->putBackToken) {
|
bfree(B_L, ip->putBackToken);
|
bfree(B_L, ip->putBackToken);
|
}
|
}
|
ip->putBackTokenId = tid;
|
ip->putBackTokenId = tid;
|
ip->putBackToken = bstrdup(B_L, string);
|
ip->putBackToken = bstrdup(B_L, string);
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Add a character to the token ringq buffer
|
* Add a character to the token ringq buffer
|
*/
|
*/
|
|
|
static int tokenAddChar(ej_t *ep, int c)
|
static int tokenAddChar(ej_t *ep, int c)
|
{
|
{
|
ejinput_t* ip;
|
ejinput_t* ip;
|
|
|
a_assert(ep);
|
a_assert(ep);
|
ip = ep->input;
|
ip = ep->input;
|
a_assert(ip);
|
a_assert(ip);
|
|
|
if (ringqPutc(&ip->tokbuf, (char_t) c) < 0) {
|
if (ringqPutc(&ip->tokbuf, (char_t) c) < 0) {
|
ejError(ep, T("Token too big"));
|
ejError(ep, T("Token too big"));
|
return -1;
|
return -1;
|
}
|
}
|
* ((char_t*) ip->tokbuf.endp) = '\0';
|
* ((char_t*) ip->tokbuf.endp) = '\0';
|
ep->token = (char_t*) ip->tokbuf.servp;
|
ep->token = (char_t*) ip->tokbuf.servp;
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Get another input character
|
* Get another input character
|
*/
|
*/
|
|
|
static int inputGetc(ej_t* ep)
|
static int inputGetc(ej_t* ep)
|
{
|
{
|
ejinput_t *ip;
|
ejinput_t *ip;
|
int c, len;
|
int c, len;
|
|
|
a_assert(ep);
|
a_assert(ep);
|
ip = ep->input;
|
ip = ep->input;
|
|
|
if ((len = ringqLen(&ip->script)) == 0) {
|
if ((len = ringqLen(&ip->script)) == 0) {
|
return -1;
|
return -1;
|
}
|
}
|
|
|
c = ringqGetc(&ip->script);
|
c = ringqGetc(&ip->script);
|
|
|
if (c == '\n') {
|
if (c == '\n') {
|
ip->lineNumber++;
|
ip->lineNumber++;
|
ip->lineColumn = 0;
|
ip->lineColumn = 0;
|
} else {
|
} else {
|
if ((ip->lineColumn + 2) >= ip->lineLength) {
|
if ((ip->lineColumn + 2) >= ip->lineLength) {
|
ip->lineLength += EJ_INC;
|
ip->lineLength += EJ_INC;
|
ip->line = brealloc(B_L, ip->line, ip->lineLength * sizeof(char_t));
|
ip->line = brealloc(B_L, ip->line, ip->lineLength * sizeof(char_t));
|
}
|
}
|
ip->line[ip->lineColumn++] = c;
|
ip->line[ip->lineColumn++] = c;
|
ip->line[ip->lineColumn] = '\0';
|
ip->line[ip->lineColumn] = '\0';
|
}
|
}
|
return c;
|
return c;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Putback a character onto the input queue
|
* Putback a character onto the input queue
|
*/
|
*/
|
|
|
static void inputPutback(ej_t* ep, int c)
|
static void inputPutback(ej_t* ep, int c)
|
{
|
{
|
ejinput_t *ip;
|
ejinput_t *ip;
|
|
|
a_assert(ep);
|
a_assert(ep);
|
|
|
ip = ep->input;
|
ip = ep->input;
|
ringqInsertc(&ip->script, (char_t) c);
|
ringqInsertc(&ip->script, (char_t) c);
|
ip->lineColumn--;
|
ip->lineColumn--;
|
ip->line[ip->lineColumn] = '\0';
|
ip->line[ip->lineColumn] = '\0';
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
|
|