summaryrefslogtreecommitdiff
path: root/source4/lib/appweb/ejs-2.0/ejs/ejsLex.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/lib/appweb/ejs-2.0/ejs/ejsLex.c')
-rw-r--r--source4/lib/appweb/ejs-2.0/ejs/ejsLex.c1033
1 files changed, 1033 insertions, 0 deletions
diff --git a/source4/lib/appweb/ejs-2.0/ejs/ejsLex.c b/source4/lib/appweb/ejs-2.0/ejs/ejsLex.c
new file mode 100644
index 0000000000..fbfee6e4d5
--- /dev/null
+++ b/source4/lib/appweb/ejs-2.0/ejs/ejsLex.c
@@ -0,0 +1,1033 @@
+/*
+ * @file ejsLex.c
+ * @brief EJS Lexical Analyser
+ * @overview EJS lexical analyser. This implementes a lexical analyser
+ * for a subset of the JavaScript language.
+ */
+/********************************* Copyright **********************************/
+/*
+ * @copy default.g
+ *
+ * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved.
+ * Portions Copyright (c) GoAhead Software, 1995-2000. All Rights Reserved.
+ *
+ * This software is distributed under commercial and open source licenses.
+ * You may use the GPL open source license described below or you may acquire
+ * a commercial license from Mbedthis Software. You agree to be fully bound
+ * by the terms of either license. Consult the LICENSE.TXT distributed with
+ * this software for full details.
+ *
+ * This software is open source; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See the GNU General Public License for more
+ * details at: http://www.mbedthis.com/downloads/gplLicense.html
+ *
+ * This program is distributed WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * This GPL license does NOT permit incorporating this software into
+ * proprietary programs. If you are unable to comply with the GPL, you must
+ * acquire a commercial license to use this software. Commercial licenses
+ * for this software and support services are available from Mbedthis
+ * Software at http://www.mbedthis.com
+ *
+ * @end
+ */
+/********************************** Includes **********************************/
+
+#include "ejs.h"
+
+#if BLD_FEATURE_EJS
+
+/****************************** Forward Declarations **************************/
+
+static int getLexicalToken(Ejs *ep, int state);
+static int tokenAddChar(Ejs *ep, int c);
+static int inputGetc(Ejs *ep);
+static void inputPutback(Ejs *ep, int c);
+static int charConvert(Ejs *ep, int base, int maxDig);
+static void parseNumber(Ejs *ep, EjsType type);
+
+/************************************* Code ***********************************/
+/*
+ * Open a new input script
+ */
+
+int ejsLexOpenScript(Ejs *ep, const char *script)
+{
+ EjsInput *ip;
+
+ mprAssert(ep);
+ mprAssert(script);
+
+ if ((ip = mprAllocTypeZeroed(ep, EjsInput)) == NULL) {
+ return MPR_ERR_MEMORY;
+ }
+ ip->next = ep->input;
+ ep->input = ip;
+ ip->procName = ep->proc ? ep->proc->procName : NULL;
+ ip->fileName = ep->fileName ? ep->fileName : NULL;
+
+/*
+ * Create the parse token buffer and script buffer
+ */
+ ip->tokServp = ip->tokbuf;
+ ip->tokEndp = ip->tokbuf;
+
+ ip->script = script;
+ ip->scriptSize = strlen(script);
+ ip->scriptServp = (char*) ip->script;
+
+ ip->lineNumber = 1;
+ ip->lineColumn = 0;
+
+ ip->putBackIndex = -1;
+
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Close the input script
+ */
+
+void ejsLexCloseScript(Ejs *ep)
+{
+ EjsInput *ip;
+
+ mprAssert(ep);
+
+ ip = ep->input;
+ mprAssert(ip);
+ ep->input = ip->next;
+
+ mprFree(ip);
+}
+
+/******************************************************************************/
+/*
+ * Initialize an input state structure
+ */
+
+int ejsInitInputState(EjsInput *ip)
+{
+ mprAssert(ip);
+
+ memset(ip, 0, sizeof(*ip));
+ ip->putBackIndex = -1;
+
+ return 0;
+}
+/******************************************************************************/
+/*
+ * Save the input state
+ */
+
+void ejsLexSaveInputState(Ejs *ep, EjsInput *state)
+{
+ EjsInput *ip;
+ int i;
+
+ mprAssert(ep);
+
+ ip = ep->input;
+ mprAssert(ip);
+
+ *state = *ip;
+
+ for (i = 0; i <= ip->putBackIndex; i++) {
+ mprStrcpy(state->putBack[i].tokbuf, EJS_MAX_TOKEN,
+ ip->putBack[i].tokbuf);
+ state->putBack[i].tid = ip->putBack[i].tid;
+ }
+
+ mprStrcpy(state->line, sizeof(state->line), ip->line);
+
+ state->lineColumn = ip->lineColumn;
+ state->lineNumber = ip->lineNumber;
+}
+
+/******************************************************************************/
+/*
+ * Restore the input state
+ */
+
+void ejsLexRestoreInputState(Ejs *ep, EjsInput *state)
+{
+ EjsInput *ip;
+ EjsToken *tp;
+ int i;
+
+ mprAssert(ep);
+ mprAssert(state);
+
+ ip = ep->input;
+ mprAssert(ip);
+
+ mprStrcpy(ip->tokbuf, sizeof(ip->tokbuf), state->tokbuf);
+ ip->tokServp = state->tokServp;
+ ip->tokEndp = state->tokEndp;
+
+ ip->script = state->script;
+ ip->scriptServp = state->scriptServp;
+ ip->scriptSize = state->scriptSize;
+
+ ip->putBackIndex = state->putBackIndex;
+ for (i = 0; i <= ip->putBackIndex; i++) {
+ tp = &ip->putBack[i];
+ tp->tid = state->putBack[i].tid;
+ mprStrcpy(tp->tokbuf, sizeof(tp->tokbuf), state->putBack[i].tokbuf);
+ }
+
+ mprStrcpy(ip->line, sizeof(ip->line), state->line);
+
+ ip->lineColumn = state->lineColumn;
+ ip->lineNumber = state->lineNumber;
+}
+
+/******************************************************************************/
+/*
+ * Free a saved input state
+ */
+
+void ejsLexFreeInputState(Ejs *ep, EjsInput *state)
+{
+ mprAssert(ep);
+ mprAssert(state);
+
+ state->putBackIndex = -1;
+ state->lineColumn = 0;
+}
+
+/******************************************************************************/
+/*
+ * Get the next EJS token
+ */
+
+int ejsLexGetToken(Ejs *ep, int state)
+{
+ mprAssert(ep);
+
+ ep->tid = getLexicalToken(ep, state);
+ return ep->tid;
+}
+
+/******************************************************************************/
+
+/*
+ * Check for reserved words "if", "else", "var", "for", "delete", "function",
+ * "class", "extends", "public", "private", "protected", "try", "catch",
+ * "finally", "throw", "return", "get", "set", "this", "module", "each"
+ *
+ * The "new" and "in" reserved words are handled below. The "true", "false",
+ * "null" "typeof" and "undefined" reserved words are handled as global
+ * objects.
+ *
+ * Other reserved words not supported:
+ * "break", "case", "continue", "default", "do",
+ * "instanceof", "switch", "while", "with"
+ *
+ * ECMA extensions reserved words (not supported):
+ * "abstract", "boolean", "byte", "char", "const",
+ * "debugger", "double", "enum", "export",
+ * "final", "float", "goto", "implements", "import", "int",
+ * "interface", "long", "native", "package",
+ * "short", "static", "super", "synchronized", "transient", "volatile"
+ *
+ * FUTURE -- use a hash lookup
+ */
+
+static int checkReservedWord(Ejs *ep, int state, int c, int tid)
+{
+ /* FUTURE -- probably should return for all tokens != EJS_TOK_ID */
+ /* FUTURE -- Should have a hash for this. MUCH faster. */
+
+ if (!isalpha(ep->token[0]) || tid == EJS_TOK_LITERAL) {
+ return tid;
+ }
+ if (state == EJS_STATE_STMT) {
+ /* FUTURE OPT -- convert to hash lookup */
+ if (strcmp(ep->token, "if") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_IF;
+ } else if (strcmp(ep->token, "else") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_ELSE;
+ } else if (strcmp(ep->token, "var") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_VAR;
+ } else if (strcmp(ep->token, "new") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_NEW;
+ } else if (strcmp(ep->token, "for") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_FOR;
+ } else if (strcmp(ep->token, "delete") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_DELETE;
+ } else if (strcmp(ep->token, "function") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_FUNCTION;
+ } else if (strcmp(ep->token, "class") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_CLASS;
+ } else if (strcmp(ep->token, "module") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_MODULE;
+ } else if (strcmp(ep->token, "extends") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_EXTENDS;
+ } else if (strcmp(ep->token, "try") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_TRY;
+ } else if (strcmp(ep->token, "catch") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_CATCH;
+ } else if (strcmp(ep->token, "finally") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_FINALLY;
+ } else if (strcmp(ep->token, "throw") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_THROW;
+ } else if (strcmp(ep->token, "public") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_PUBLIC;
+ } else if (strcmp(ep->token, "protected") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_PROTECTED;
+ } else if (strcmp(ep->token, "private") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_PRIVATE;
+ } else if (strcmp(ep->token, "get") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_GET;
+ } else if (strcmp(ep->token, "set") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_SET;
+ } else if (strcmp(ep->token, "extends") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_EXTENDS;
+ } else if (strcmp(ep->token, "try") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_TRY;
+ } else if (strcmp(ep->token, "catch") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_CATCH;
+ } else if (strcmp(ep->token, "finally") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_FINALLY;
+ } else if (strcmp(ep->token, "throw") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_THROW;
+ } else if (strcmp(ep->token, "public") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_PUBLIC;
+ } else if (strcmp(ep->token, "protected") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_PROTECTED;
+ } else if (strcmp(ep->token, "private") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_PRIVATE;
+ } else if (strcmp(ep->token, "get") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_GET;
+ } else if (strcmp(ep->token, "set") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_SET;
+ } else if (strcmp(ep->token, "each") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_EACH;
+ } else if (strcmp(ep->token, "return") == 0) {
+ if ((c == ';') || (c == '(')) {
+ inputPutback(ep, c);
+ }
+ return EJS_TOK_RETURN;
+ }
+
+ } else if (state == EJS_STATE_EXPR) {
+ if (strcmp(ep->token, "new") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_NEW;
+ } else if (strcmp(ep->token, "in") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_IN;
+ } else if (strcmp(ep->token, "function") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_FUNCTION;
+ }
+
+ } else if (state == EJS_STATE_DEC) {
+ if (strcmp(ep->token, "extends") == 0) {
+ inputPutback(ep, c);
+ return EJS_TOK_EXTENDS;
+ }
+ }
+ return tid;
+}
+
+/******************************************************************************/
+/*
+ * Get the next EJS token
+ */
+
+static int getLexicalToken(Ejs *ep, int state)
+{
+ EjsType type;
+ EjsInput *ip;
+ int done, tid, c, quote, style, idx, isHex;
+
+ mprAssert(ep);
+ ip = ep->input;
+ mprAssert(ip);
+
+ ep->tid = -1;
+ tid = -1;
+ type = BLD_FEATURE_NUM_TYPE_ID;
+ isHex = 0;
+
+ /*
+ * Use a putback tokens first. Don't free strings as caller needs access.
+ */
+ if (ip->putBackIndex >= 0) {
+ idx = ip->putBackIndex;
+ tid = ip->putBack[idx].tid;
+ ep->token = (char*) ip->putBack[idx].tokbuf;
+ tid = checkReservedWord(ep, state, 0, tid);
+ ip->putBackIndex--;
+ return tid;
+ }
+ ep->token = ip->tokServp = ip->tokEndp = ip->tokbuf;
+ *ip->tokServp = '\0';
+
+ if ((c = inputGetc(ep)) < 0) {
+ return EJS_TOK_EOF;
+ }
+
+ /*
+ * Main lexical analyser
+ */
+ for (done = 0; !done; ) {
+ switch (c) {
+ case -1:
+ return EJS_TOK_EOF;
+
+ case ' ':
+ case '\t':
+ case '\r':
+ do {
+ if ((c = inputGetc(ep)) < 0)
+ break;
+ } while (c == ' ' || c == '\t' || c == '\r');
+ break;
+
+ case '\n':
+ return EJS_TOK_NEWLINE;
+
+ case '(':
+ tokenAddChar(ep, c);
+ return EJS_TOK_LPAREN;
+
+ case ')':
+ tokenAddChar(ep, c);
+ return EJS_TOK_RPAREN;
+
+ case '[':
+ tokenAddChar(ep, c);
+ return EJS_TOK_LBRACKET;
+
+ case ']':
+ tokenAddChar(ep, c);
+ return EJS_TOK_RBRACKET;
+
+ case '.':
+ tokenAddChar(ep, c);
+ return EJS_TOK_PERIOD;
+
+ case '{':
+ tokenAddChar(ep, c);
+ return EJS_TOK_LBRACE;
+
+ case '}':
+ tokenAddChar(ep, c);
+ return EJS_TOK_RBRACE;
+
+ case '+':
+ if ((c = inputGetc(ep)) < 0) {
+ ejsSyntaxError(ep, 0);
+ return EJS_TOK_ERR;
+ }
+ if (c != '+' ) {
+ inputPutback(ep, c);
+ tokenAddChar(ep, EJS_EXPR_PLUS);
+ return EJS_TOK_EXPR;
+ }
+ tokenAddChar(ep, EJS_EXPR_INC);
+ return EJS_TOK_INC_DEC;
+
+ case '-':
+ if ((c = inputGetc(ep)) < 0) {
+ ejsSyntaxError(ep, 0);
+ return EJS_TOK_ERR;
+ }
+ if (c != '-' ) {
+ inputPutback(ep, c);
+ tokenAddChar(ep, EJS_EXPR_MINUS);
+ return EJS_TOK_EXPR;
+ }
+ tokenAddChar(ep, EJS_EXPR_DEC);
+ return EJS_TOK_INC_DEC;
+
+ case '*':
+ tokenAddChar(ep, EJS_EXPR_MUL);
+ return EJS_TOK_EXPR;
+
+ case '%':
+ tokenAddChar(ep, EJS_EXPR_MOD);
+ return EJS_TOK_EXPR;
+
+ case '/':
+ /*
+ * Handle the division operator and comments
+ */
+ if ((c = inputGetc(ep)) < 0) {
+ ejsSyntaxError(ep, 0);
+ return EJS_TOK_ERR;
+ }
+ if (c != '*' && c != '/') {
+ inputPutback(ep, c);
+ tokenAddChar(ep, EJS_EXPR_DIV);
+ return EJS_TOK_EXPR;
+ }
+ style = c;
+ /*
+ * Eat comments. Both C and C++ comment styles are supported.
+ */
+ while (1) {
+ if ((c = inputGetc(ep)) < 0) {
+ if (style == '/') {
+ return EJS_TOK_EOF;
+ }
+ ejsSyntaxError(ep, 0);
+ return EJS_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 EJS_TOK_EOF;
+ }
+ break;
+
+ case '<': /* < and <= */
+ if ((c = inputGetc(ep)) < 0) {
+ ejsSyntaxError(ep, 0);
+ return EJS_TOK_ERR;
+ }
+ if (c == '<') {
+ tokenAddChar(ep, EJS_EXPR_LSHIFT);
+ return EJS_TOK_EXPR;
+ } else if (c == '=') {
+ tokenAddChar(ep, EJS_EXPR_LESSEQ);
+ return EJS_TOK_EXPR;
+ }
+ tokenAddChar(ep, EJS_EXPR_LESS);
+ inputPutback(ep, c);
+ return EJS_TOK_EXPR;
+
+ case '>': /* > and >= */
+ if ((c = inputGetc(ep)) < 0) {
+ ejsSyntaxError(ep, 0);
+ return EJS_TOK_ERR;
+ }
+ if (c == '>') {
+ tokenAddChar(ep, EJS_EXPR_RSHIFT);
+ return EJS_TOK_EXPR;
+ } else if (c == '=') {
+ tokenAddChar(ep, EJS_EXPR_GREATEREQ);
+ return EJS_TOK_EXPR;
+ }
+ tokenAddChar(ep, EJS_EXPR_GREATER);
+ inputPutback(ep, c);
+ return EJS_TOK_EXPR;
+
+ case '=': /* "==" */
+ if ((c = inputGetc(ep)) < 0) {
+ ejsSyntaxError(ep, 0);
+ return EJS_TOK_ERR;
+ }
+ if (c == '=') {
+ tokenAddChar(ep, EJS_EXPR_EQ);
+ return EJS_TOK_EXPR;
+ }
+ inputPutback(ep, c);
+ return EJS_TOK_ASSIGNMENT;
+
+ case '!': /* "!=" or "!"*/
+ if ((c = inputGetc(ep)) < 0) {
+ ejsSyntaxError(ep, 0);
+ return EJS_TOK_ERR;
+ }
+ if (c == '=') {
+ tokenAddChar(ep, EJS_EXPR_NOTEQ);
+ return EJS_TOK_EXPR;
+ }
+ inputPutback(ep, c);
+ tokenAddChar(ep, EJS_EXPR_BOOL_COMP);
+ return EJS_TOK_EXPR;
+
+ case ';':
+ tokenAddChar(ep, c);
+ return EJS_TOK_SEMI;
+
+ case ',':
+ tokenAddChar(ep, c);
+ return EJS_TOK_COMMA;
+
+ case ':':
+ tokenAddChar(ep, c);
+ return EJS_TOK_COLON;
+
+ case '|': /* "||" */
+ if ((c = inputGetc(ep)) < 0 || c != '|') {
+ ejsSyntaxError(ep, 0);
+ return EJS_TOK_ERR;
+ }
+ tokenAddChar(ep, EJS_COND_OR);
+ return EJS_TOK_LOGICAL;
+
+ case '&': /* "&&" */
+ if ((c = inputGetc(ep)) < 0 || c != '&') {
+ ejsSyntaxError(ep, 0);
+ return EJS_TOK_ERR;
+ }
+ tokenAddChar(ep, EJS_COND_AND);
+ return EJS_TOK_LOGICAL;
+
+ case '\"': /* String quote */
+ case '\'':
+ quote = c;
+ if ((c = inputGetc(ep)) < 0) {
+ ejsSyntaxError(ep, 0);
+ return EJS_TOK_ERR;
+ }
+
+ while (c != quote) {
+ /*
+ * Check for escape sequence characters
+ */
+ if (c == '\\') {
+ c = inputGetc(ep);
+
+ if (isdigit(c)) {
+ /*
+ * Octal support, \101 maps to 65 = 'A'. Put first
+ * char back so converter will work properly.
+ */
+ inputPutback(ep, c);
+ c = charConvert(ep, 8, 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, 16, 2);
+ break;
+ case 'u':
+ /*
+ * Unicode support, \x0401 maps to 65 = 'A'
+ */
+ c = charConvert(ep, 16, 2);
+ c = c*16 + charConvert(ep, 16, 2);
+
+ break;
+ case '\'':
+ case '\"':
+ case '\\':
+ break;
+ default:
+ if (tokenAddChar(ep, '\\') < 0) {
+ return EJS_TOK_ERR;
+ }
+ }
+ }
+ if (tokenAddChar(ep, c) < 0) {
+ return EJS_TOK_ERR;
+ }
+ } else {
+ if (tokenAddChar(ep, c) < 0) {
+ return EJS_TOK_ERR;
+ }
+ }
+ if ((c = inputGetc(ep)) < 0) {
+ ejsSyntaxError(ep, "Unmatched Quote");
+ return EJS_TOK_ERR;
+ }
+ }
+ return EJS_TOK_LITERAL;
+
+ case '0':
+ if (tokenAddChar(ep, c) < 0) {
+ return EJS_TOK_ERR;
+ }
+ if ((c = inputGetc(ep)) < 0) {
+ break;
+ }
+ if (tolower(c) == 'x') {
+ if (tokenAddChar(ep, c) < 0) {
+ return EJS_TOK_ERR;
+ }
+ if ((c = inputGetc(ep)) < 0) {
+ break;
+ }
+ isHex = 1;
+ if (! isxdigit(c)) {
+ parseNumber(ep, type);
+ inputPutback(ep, c);
+ return EJS_TOK_NUMBER;
+ }
+ } else if (! isdigit(c)) {
+#if BLD_FEATURE_FLOATING_POINT
+ if (c == '.' || tolower(c) == 'e' || c == '+' || c == '-') {
+ /* Fall through */
+ type = EJS_TYPE_FLOAT;
+ } else
+#endif
+ {
+ parseNumber(ep, type);
+ inputPutback(ep, c);
+ return EJS_TOK_NUMBER;
+ }
+ }
+ /* Fall through to get more digits */
+
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (isHex) {
+ do {
+ if (tokenAddChar(ep, c) < 0) {
+ return EJS_TOK_ERR;
+ }
+ if ((c = inputGetc(ep)) < 0) {
+ break;
+ }
+ } while (isxdigit(c));
+
+ } else {
+#if BLD_FEATURE_FLOATING_POINT
+ do {
+ if (tokenAddChar(ep, c) < 0) {
+ return EJS_TOK_ERR;
+ }
+ if ((c = inputGetc(ep)) < 0) {
+ break;
+ }
+ c = tolower(c);
+ if (c == '.' || c == 'e' || c == 'f') {
+ type = EJS_TYPE_FLOAT;
+ }
+ } while (isdigit(c) || c == '.' || c == 'e' ||
+ c == 'f' ||
+ ((type == EJS_TYPE_FLOAT) && (c == '+' || c == '-')));
+#else
+ do {
+ if (tokenAddChar(ep, c) < 0) {
+ return EJS_TOK_ERR;
+ }
+ if ((c = inputGetc(ep)) < 0) {
+ break;
+ }
+ } while (isdigit(c));
+#endif
+ }
+
+ parseNumber(ep, type);
+ inputPutback(ep, c);
+ return EJS_TOK_NUMBER;
+
+ default:
+ /*
+ * Identifiers or a function names
+ */
+ while (1) {
+ if (c == '\\') {
+ if ((c = inputGetc(ep)) < 0) {
+ break;
+ }
+ if (c == '\n' || c == '\r') {
+ break;
+ }
+ } else if (tokenAddChar(ep, c) < 0) {
+ break;
+ }
+ if ((c = inputGetc(ep)) < 0) {
+ break;
+ }
+ if (!isalnum(c) && c != '$' && c != '_' &&
+ c != '\\' && c != '@') {
+ break;
+ }
+ }
+ if (*ep->token == '\0') {
+ c = inputGetc(ep);
+ break;
+ }
+
+ if (! isalpha((int) *ep->token) && *ep->token != '$' &&
+ *ep->token != '_' && *ep->token != '@') {
+ ejsError(ep, EJS_SYNTAX_ERROR, "Invalid identifier %s",
+ ep->token);
+ return EJS_TOK_ERR;
+ }
+
+ tid = checkReservedWord(ep, state, c, EJS_TOK_ID);
+ if (tid != EJS_TOK_ID) {
+ return tid;
+ }
+
+ /*
+ * 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 = EJS_TOK_ID;
+ if ((strlen(ep->token) + 1) >= EJS_MAX_ID) {
+ ejsError(ep, EJS_SYNTAX_ERROR,
+ "Identifier too big. Max is %d letters.", EJS_MAX_ID);
+ return EJS_TOK_ERR;
+ }
+ done++;
+ }
+ }
+
+ /*
+ * Putback the last extra character for next time
+ */
+ inputPutback(ep, c);
+ return tid;
+}
+
+/******************************************************************************/
+
+static void parseNumber(Ejs *ep, EjsType type)
+{
+ switch (type) {
+ case EJS_TYPE_INT:
+ ep->tokenNumber.integer = ejsParseInteger(ep->token);
+ ep->tokenNumber.type = type;
+ break;
+
+#if BLD_FEATURE_FLOATING_POINT
+ case EJS_TYPE_FLOAT:
+ ep->tokenNumber.floating = atof(ep->token);
+ ep->tokenNumber.type = type;
+ break;
+#endif
+
+#if BLD_FEATURE_INT64
+ case EJS_TYPE_INT64:
+ ep->tokenNumber.integer64 = ejsParseInteger64(ep->token);
+ ep->tokenNumber.type = type;
+ break;
+#endif
+ }
+}
+
+/******************************************************************************/
+/*
+ * Convert a hex or octal character back to binary, return original char if
+ * not a hex digit
+ */
+
+static int charConvert(Ejs *ep, int base, int maxDig)
+{
+ int i, c, lval, convChar;
+
+ lval = 0;
+ for (i = 0; i < maxDig; i++) {
+ if ((c = inputGetc(ep)) < 0) {
+ break;
+ }
+ /*
+ * Initialize to out of range value
+ */
+ convChar = base;
+ if (isdigit(c)) {
+ convChar = c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ convChar = c - 'a' + 10;
+ } else if (c >= 'A' && c <= 'F') {
+ convChar = c - 'A' + 10;
+ }
+ /*
+ * If unexpected character then return it to buffer.
+ */
+ if (convChar >= base) {
+ inputPutback(ep, c);
+ break;
+ }
+ lval = (lval * base) + convChar;
+ }
+ return lval;
+}
+
+/******************************************************************************/
+/*
+ * Putback the last token read. Accept at most one push back token.
+ */
+
+void ejsLexPutbackToken(Ejs *ep, int tid, char *string)
+{
+ EjsInput *ip;
+ EjsToken *tp;
+ int idx;
+
+ mprAssert(ep);
+ ip = ep->input;
+ mprAssert(ip);
+
+ ip->putBackIndex += 1;
+
+ mprAssert(ip->putBackIndex < EJS_TOKEN_STACK);
+ idx = ip->putBackIndex;
+
+ tp = &ip->putBack[idx];
+ tp->tid = tid;
+
+ mprStrcpy(tp->tokbuf, sizeof(tp->tokbuf), string);
+}
+
+/******************************************************************************/
+/*
+ * Add a character to the token buffer
+ */
+
+static int tokenAddChar(Ejs *ep, int c)
+{
+ EjsInput *ip;
+
+ mprAssert(ep);
+ ip = ep->input;
+ mprAssert(ip);
+
+ if (ip->tokEndp >= &ip->tokbuf[sizeof(ip->tokbuf) - 1]) {
+ ejsSyntaxError(ep, "Token too big");
+ return -1;
+ }
+ *ip->tokEndp++ = c;
+ *ip->tokEndp = '\0';
+
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Get another input character
+ */
+
+static int inputGetc(Ejs *ep)
+{
+ EjsInput *ip;
+ int c;
+
+ mprAssert(ep);
+ ip = ep->input;
+
+ if (ip->scriptSize <= 0) {
+ return -1;
+ }
+
+ c = (uchar) (*ip->scriptServp++);
+ ip->scriptSize--;
+
+ /*
+ * For debugging, accumulate the line number and the currenly parsed line
+ */
+ if (c == '\n') {
+#if 0 && BLD_DEBUG
+ if (ip->lineColumn > 0) {
+ printf("PARSED: %s\n", ip->line);
+ }
+#endif
+ ip->lineNumber++;
+ ip->lineColumn = 0;
+ } else if ((ip->lineColumn + 2) < sizeof(ip->line)) {
+ ip->line[ip->lineColumn++] = c;
+ ip->line[ip->lineColumn] = '\0';
+ }
+ return c;
+}
+
+/******************************************************************************/
+/*
+ * Putback a character onto the input queue
+ */
+
+static void inputPutback(Ejs *ep, int c)
+{
+ EjsInput *ip;
+
+ mprAssert(ep);
+
+ if (c > 0) {
+ ip = ep->input;
+ *--ip->scriptServp = c;
+ ip->scriptSize++;
+ if (--(ip->lineColumn) < 0) {
+ ip->lineColumn = 0;
+ }
+ mprAssert(ip->line);
+ mprAssert(ip->lineColumn >= 0);
+ mprAssert(ip->lineColumn < sizeof(ip->line));
+ ip->line[ip->lineColumn] = '\0';
+ }
+}
+
+/******************************************************************************/
+
+#else
+void ejsLexDummy() {}
+
+/******************************************************************************/
+#endif /* BLD_FEATURE_EJS */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim:tw=78
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */