home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / js / src / jsscan.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  26.2 KB  |  1,022 lines

  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /*
  20.  * JS lexical scanner.
  21.  */
  22. #include <stdio.h>    /* first to avoid trouble on some systems */
  23. #include <errno.h>
  24. #include <limits.h>
  25. #include <math.h>
  26. #include <memory.h>
  27. #include <stdarg.h>
  28. #include <stddef.h>
  29. #include <stdlib.h>
  30. #include <string.h>
  31. #include "prtypes.h"
  32. #ifndef NSPR20
  33. #include "prarena.h"
  34. #else
  35. #include "plarena.h"
  36. #endif
  37. #include "prlog.h"
  38. #include "prdtoa.h"
  39. #include "prprf.h"
  40. #include "jsapi.h"
  41. #include "jsatom.h"
  42. #include "jscntxt.h"
  43. #include "jsconfig.h"
  44. #include "jsemit.h"
  45. #include "jsnum.h"
  46. #include "jsopcode.h"
  47. #include "jsregexp.h"
  48. #include "jsscan.h"
  49.  
  50. #define RESERVE_JAVA_KEYWORDS
  51.  
  52. static struct keyword {
  53.     char        *name;
  54.     int16       tokentype;      /* JSTokenType */
  55.     int8        op;             /* JSOp */
  56.     int8        version;        /* JSVersion */
  57. } keywords[] = {
  58.     {"break",           TOK_BREAK,              JSOP_NOP},
  59.     {"case",            TOK_CASE,               JSOP_NOP},
  60.     {"continue",        TOK_CONTINUE,           JSOP_NOP},
  61.     {"default",         TOK_DEFAULT,            JSOP_NOP},
  62.     {"delete",          TOK_DELETE,             JSOP_NOP},
  63.     {"do",              TOK_DO,                 JSOP_NOP},
  64.     {"else",            TOK_ELSE,               JSOP_NOP},
  65.     {"export",          TOK_EXPORT,             JSOP_NOP,       JSVERSION_1_2},
  66.     {"false",           TOK_PRIMARY,            JSOP_FALSE},
  67.     {"for",             TOK_FOR,                JSOP_NOP},
  68.     {"function",        TOK_FUNCTION,           JSOP_NOP},
  69.     {"if",              TOK_IF,                 JSOP_NOP},
  70.     {"in",              TOK_IN,                 JSOP_NOP},
  71.     {"new",             TOK_NEW,                JSOP_NEW},
  72.     {"null",            TOK_PRIMARY,            JSOP_NULL},
  73.     {"return",          TOK_RETURN,             JSOP_NOP},
  74.     {"switch",          TOK_SWITCH,             JSOP_NOP},
  75.     {"this",            TOK_PRIMARY,            JSOP_THIS},
  76.     {"true",            TOK_PRIMARY,            JSOP_TRUE},
  77.     {"typeof",          TOK_UNARYOP,            JSOP_TYPEOF},
  78.     {"var",             TOK_VAR,                JSOP_NOP},
  79.     {"void",            TOK_UNARYOP,            JSOP_VOID},
  80.     {"while",           TOK_WHILE,              JSOP_NOP},
  81.     {"with",            TOK_WITH,               JSOP_NOP},
  82.  
  83. #ifdef RESERVE_JAVA_KEYWORDS
  84.     {"abstract",        TOK_RESERVED,           JSOP_NOP},
  85.     {"boolean",         TOK_RESERVED,           JSOP_NOP},
  86.     {"byte",            TOK_RESERVED,           JSOP_NOP},
  87.     {"catch",           TOK_RESERVED,           JSOP_NOP},
  88.     {"char",            TOK_RESERVED,           JSOP_NOP},
  89.     {"class",           TOK_RESERVED,           JSOP_NOP},
  90.     {"const",           TOK_RESERVED,           JSOP_NOP},
  91.     {"double",          TOK_RESERVED,           JSOP_NOP},
  92.     {"extends",         TOK_RESERVED,           JSOP_NOP},
  93.     {"final",           TOK_RESERVED,           JSOP_NOP},
  94.     {"finally",         TOK_RESERVED,           JSOP_NOP},
  95.     {"float",           TOK_RESERVED,           JSOP_NOP},
  96.     {"goto",            TOK_RESERVED,           JSOP_NOP},
  97.     {"implements",      TOK_RESERVED,           JSOP_NOP},
  98.     {"import",          TOK_IMPORT,             JSOP_NOP},
  99.     {"instanceof",      TOK_RESERVED,           JSOP_NOP},
  100.     {"int",             TOK_RESERVED,           JSOP_NOP},
  101.     {"interface",       TOK_RESERVED,           JSOP_NOP},
  102.     {"long",            TOK_RESERVED,           JSOP_NOP},
  103.     {"native",          TOK_RESERVED,           JSOP_NOP},
  104.     {"package",         TOK_RESERVED,           JSOP_NOP},
  105.     {"private",         TOK_RESERVED,           JSOP_NOP},
  106.     {"protected",       TOK_RESERVED,           JSOP_NOP},
  107.     {"public",          TOK_RESERVED,           JSOP_NOP},
  108.     {"short",           TOK_RESERVED,           JSOP_NOP},
  109.     {"static",          TOK_RESERVED,           JSOP_NOP},
  110.     {"super",           TOK_PRIMARY,            JSOP_NOP},
  111.     {"synchronized",    TOK_RESERVED,           JSOP_NOP},
  112.     {"throw",           TOK_RESERVED,           JSOP_NOP},
  113.     {"throws",          TOK_RESERVED,           JSOP_NOP},
  114.     {"transient",       TOK_RESERVED,           JSOP_NOP},
  115.     {"try",             TOK_RESERVED,           JSOP_NOP},
  116.     {"volatile",        TOK_RESERVED,           JSOP_NOP},
  117. #endif
  118.  
  119.     {0}
  120. };
  121.  
  122. JSBool
  123. js_InitScanner(JSContext *cx)
  124. {
  125.     struct keyword *kw;
  126.     JSAtom *atom;
  127.  
  128.     for (kw = keywords; kw->name; kw++) {
  129.     atom = js_Atomize(cx, kw->name, strlen(kw->name), 0);
  130.     if (!atom)
  131.         return JS_FALSE;
  132.     atom->kwindex = (kw->version <= cx->version) ? kw - keywords : -1;
  133.     }
  134.     return JS_TRUE;
  135. }
  136.  
  137. JS_FRIEND_API(void)
  138. js_MapKeywords(void (*mapfun)(const char *))
  139. {
  140.     struct keyword *kw;
  141.  
  142.     for (kw = keywords; kw->name; kw++)
  143.     mapfun(kw->name);
  144. }
  145.  
  146. JSTokenStream *
  147. js_NewTokenStream(JSContext *cx, const jschar *base, size_t length,
  148.           const char *filename, uintN lineno,
  149.           JSPrincipals *principals)
  150. {
  151.     JSTokenStream *ts;
  152.  
  153.     ts = js_NewBufferTokenStream(cx, base, length);
  154.     if (!ts)
  155.     return NULL;
  156.     ts->filename = filename;
  157.     ts->lineno = lineno;
  158.     if (principals)
  159.         JSPRINCIPALS_HOLD(cx, principals);
  160.     ts->principals = principals;
  161.     return ts;
  162. }
  163.  
  164. JS_FRIEND_API(JSTokenStream *)
  165. js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length)
  166. {
  167.     size_t nb;
  168.     JSTokenStream *ts;
  169.  
  170.     nb = sizeof(JSTokenStream) + JS_LINE_LIMIT * sizeof(jschar);
  171.     PR_ARENA_ALLOCATE(ts, &cx->tempPool, nb);
  172.     if (!ts) {
  173.     JS_ReportOutOfMemory(cx);
  174.     return NULL;
  175.     }
  176.     memset(ts, 0, nb);
  177.     CLEAR_PUSHBACK(ts);
  178.     ts->lineno = 1;
  179.     ts->linebuf.base = ts->linebuf.limit = ts->linebuf.ptr = (jschar *)(ts + 1);
  180.     ts->userbuf.base = (jschar *)base;
  181.     ts->userbuf.limit = (jschar *)base + length;
  182.     ts->userbuf.ptr = (jschar *)base;
  183.     return ts;
  184. }
  185.  
  186. #ifdef JSFILE
  187. JS_FRIEND_API(JSTokenStream *)
  188. js_NewFileTokenStream(JSContext *cx, const char *filename)
  189. {
  190.     JSTokenStream *ts;
  191.     FILE *file;
  192.  
  193.     ts = js_NewBufferTokenStream(cx, NULL, 0);
  194.     if (!ts)
  195.     return NULL;
  196.     file = fopen(filename, "r");
  197.     if (!file) {
  198.     JS_ReportError(cx, "can't open %s: %s", filename, strerror(errno));
  199.     return NULL;
  200.     }
  201.     ts->file = file;
  202.     ts->filename = filename;
  203.     return ts;
  204. }
  205. #endif
  206.  
  207. JS_FRIEND_API(JSBool)
  208. js_CloseTokenStream(JSContext *cx, JSTokenStream *ts)
  209. {
  210.     if (ts->principals)
  211.         JSPRINCIPALS_DROP(cx, ts->principals);
  212. #ifdef JSFILE
  213.     return !ts->file || fclose(ts->file) == 0;
  214. #else
  215.     return JS_TRUE;
  216. #endif
  217. }
  218.  
  219. static int32
  220. GetChar(JSTokenStream *ts)
  221. {
  222.     ptrdiff_t length;
  223.     jschar *nl;
  224.     int32 c;
  225.  
  226.     if (ts->ungetpos != 0) {
  227.     c = ts->ungetbuf[--ts->ungetpos];
  228.     } else {
  229.     if (ts->linebuf.ptr >= ts->linebuf.limit) {
  230. #ifdef JSFILE
  231.         if (ts->file) {
  232.         char cbuf[JS_LINE_LIMIT];
  233.         ptrdiff_t i;
  234.  
  235.         if (!fgets(cbuf, JS_LINE_LIMIT, ts->file)) {
  236.             ts->flags |= TSF_EOF;
  237.             return EOF;
  238.         }
  239.         length = strlen(cbuf);
  240.         for (i = 0; i < length; i++)
  241.             ts->linebuf.base[i] = (jschar) cbuf[i];
  242.         } else
  243. #endif
  244.         {
  245.         length = ts->userbuf.limit - ts->userbuf.ptr;
  246.         if (length <= 0) {
  247.             ts->flags |= TSF_EOF;
  248.             return EOF;
  249.         }
  250.  
  251.         /*
  252.          * Any one of \n, \r, or \r\n ends a line (longest match wins).
  253.          */
  254.         for (nl = ts->userbuf.ptr; nl < ts->userbuf.limit; nl++) {
  255.             if (*nl == '\n')
  256.             break;
  257.             if (*nl == '\r') {
  258.             if (nl + 1 < ts->userbuf.limit && nl[1] == '\n')
  259.                 nl++;
  260.             break;
  261.             }
  262.         }
  263.  
  264.         /*
  265.          * If there was a line terminator, copy thru it into linebuf.
  266.          * Else copy JS_LINE_LIMIT-1 bytes into linebuf.
  267.          */
  268.         if (nl < ts->userbuf.limit)
  269.             length = nl - ts->userbuf.ptr + 1;
  270.         if (length >= JS_LINE_LIMIT)
  271.             length = JS_LINE_LIMIT - 1;
  272.         js_strncpy(ts->linebuf.base, ts->userbuf.ptr, length);
  273.         ts->userbuf.ptr += length;
  274.  
  275.         /*
  276.          * Make sure linebuf contains \n for EOL (don't do this in
  277.          * userbuf because the user's string might be readonly).
  278.          */
  279.         if (nl < ts->userbuf.limit) {
  280.             if (*nl == '\r') {
  281.             if (ts->linebuf.base[length-1] == '\r')
  282.                 ts->linebuf.base[length-1] = '\n';
  283.             } else if (*nl == '\n') {
  284.             if (nl > ts->userbuf.base &&
  285.                 nl[-1] == '\r' &&
  286.                 ts->linebuf.base[length-2] == '\r') {
  287.                 length--;
  288.                 PR_ASSERT(ts->linebuf.base[length] == '\n');
  289.                 ts->linebuf.base[length-1] = '\n';
  290.             }
  291.             }
  292.         }
  293.         }
  294.         ts->linebuf.limit = ts->linebuf.base + length;
  295.         ts->linebuf.ptr = ts->linebuf.base;
  296.     }
  297.     c = *ts->linebuf.ptr++;
  298.     }
  299.     if (c == '\n')
  300.     ts->lineno++;
  301.     return c;
  302. }
  303.  
  304. static void
  305. UngetChar(JSTokenStream *ts, int32 c)
  306. {
  307.     if (c == EOF)
  308.     return;
  309.     PR_ASSERT(ts->ungetpos < sizeof ts->ungetbuf / sizeof ts->ungetbuf[0]);
  310.     if (c == '\n')
  311.     ts->lineno--;
  312.     ts->ungetbuf[ts->ungetpos++] = (jschar)c;
  313. }
  314.  
  315. static int32
  316. PeekChar(JSTokenStream *ts)
  317. {
  318.     int32 c;
  319.  
  320.     c = GetChar(ts);
  321.     UngetChar(ts, c);
  322.     return c;
  323. }
  324.  
  325. static JSBool
  326. PeekChars(JSTokenStream *ts, intN n, jschar *cp)
  327. {
  328.     intN i, j;
  329.     int32 c;
  330.  
  331.     for (i = 0; i < n; i++) {
  332.     c = GetChar(ts);
  333.     if (c == EOF)
  334.         break;
  335.     cp[i] = (jschar)c;
  336.     }
  337.     for (j = i - 1; j >= 0; j--)
  338.     UngetChar(ts, cp[j]);
  339.     return i == n;
  340. }
  341.  
  342. static void
  343. SkipChars(JSTokenStream *ts, intN n)
  344. {
  345.     while (--n >= 0)
  346.     GetChar(ts);
  347. }
  348.  
  349. static int32
  350. MatchChar(JSTokenStream *ts, int32 nextChar)
  351. {
  352.     int32 c;
  353.  
  354.     c = GetChar(ts);
  355.     if (c == nextChar)
  356.     return 1;
  357.     UngetChar(ts, c);
  358.     return 0;
  359. }
  360.  
  361. void
  362. js_ReportCompileError(JSContext *cx, JSTokenStream *ts, const char *format,
  363.               ...)
  364. {
  365.     va_list ap;
  366.     char *message;
  367.     jschar *limit, lastc;
  368.     JSErrorReporter onError;
  369.     JSErrorReport report;
  370.     JSString *linestr;
  371.  
  372.     va_start(ap, format);
  373.     message = PR_vsmprintf(format, ap);
  374.     va_end(ap);
  375.     if (!message) {
  376.     JS_ReportOutOfMemory(cx);
  377.     return;
  378.     }
  379.  
  380.     PR_ASSERT(ts->linebuf.limit < ts->linebuf.base + JS_LINE_LIMIT);
  381.     limit = ts->linebuf.limit;
  382.     lastc = limit[-1];
  383.     if (lastc == '\n')
  384.     limit[-1] = 0;
  385.     onError = cx->errorReporter;
  386.     if (onError) {
  387.     report.filename = ts->filename;
  388.     report.lineno = ts->lineno;
  389.     linestr = js_NewStringCopyZ(cx, ts->linebuf.base, 0);
  390.     report.linebuf  = linestr
  391.               ? JS_GetStringBytes(linestr)
  392.               : NULL;
  393.     report.tokenptr = linestr
  394.               ? report.linebuf + (ts->token.ptr - ts->linebuf.base)
  395.               : NULL;
  396.     report.uclinebuf = ts->linebuf.base;
  397.     report.uctokenptr = ts->token.ptr;
  398.     (*onError)(cx, message, &report);
  399. #if !defined XP_PC || !defined _MSC_VER || _MSC_VER > 800
  400.     } else {
  401.     if (!(ts->flags & TSF_INTERACTIVE))
  402.         fprintf(stderr, "JavaScript error: ");
  403.     if (ts->filename)
  404.         fprintf(stderr, "%s, ", ts->filename);
  405.     if (ts->lineno)
  406.         fprintf(stderr, "line %u: ", ts->lineno);
  407.     fprintf(stderr, "%s:\n%s\n", message, ts->linebuf.base);
  408. #endif
  409.     }
  410.     if (lastc == '\n')
  411.     limit[-1] = lastc;
  412.     free(message);
  413. }
  414.  
  415. JSTokenType
  416. js_PeekToken(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg)
  417. {
  418.     JSTokenType tt;
  419.  
  420.     tt = ts->pushback.type;
  421.     if (tt == TOK_EOF) {
  422.     ts->flags |= TSF_LOOKAHEAD;
  423.     tt = js_GetToken(cx, ts, cg);
  424.     ts->flags &= ~TSF_LOOKAHEAD;
  425.     js_UngetToken(ts);
  426.     }
  427.     return tt;
  428. }
  429.  
  430. JSTokenType
  431. js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg)
  432. {
  433.     uintN newlines;
  434.     JSTokenType tt;
  435.  
  436.     newlines = ts->flags & TSF_NEWLINES;
  437.     if (!newlines)
  438.     SCAN_NEWLINES(ts);
  439.     tt = js_PeekToken(cx, ts, cg);
  440.     if (!newlines)
  441.     HIDE_NEWLINES(ts);
  442.     return tt;
  443. }
  444.  
  445. #define TBINCR         64
  446.  
  447. static JSBool
  448. GrowTokenBuf(JSContext *cx, JSTokenBuf *tb)
  449. {
  450.     jschar *base;
  451.     ptrdiff_t offset, length;
  452.     size_t tbincr, tbsize;
  453.     PRArenaPool *pool;
  454.  
  455.     base = tb->base;
  456.     offset = tb->ptr - base;
  457.     length = tb->limit - base;
  458.     tbincr = TBINCR * sizeof(jschar);
  459.     pool = &cx->tempPool;
  460.     if (!base) {
  461.     PR_ARENA_ALLOCATE(base, pool, tbincr);
  462.     } else {
  463.     tbsize = (size_t)(length * sizeof(jschar));
  464.     PR_ARENA_GROW(base, pool, tbsize, tbincr);
  465.     }
  466.     if (!base) {
  467.     JS_ReportOutOfMemory(cx);
  468.     return JS_FALSE;
  469.     }
  470.     tb->base = base;
  471.     tb->limit = base + length + TBINCR;
  472.     tb->ptr = base + offset;
  473.     return JS_TRUE;
  474. }
  475.  
  476. static JSBool
  477. AddToTokenBuf(JSContext *cx, JSTokenBuf *tb, jschar c)
  478. {
  479.     if (tb->ptr == tb->limit && !GrowTokenBuf(cx, tb))
  480.     return JS_FALSE;
  481.     *tb->ptr++ = c;
  482.     return JS_TRUE;
  483. }
  484.  
  485. JSTokenType
  486. js_GetToken(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg)
  487. {
  488.     int32 c;
  489.     JSAtom *atom;
  490.  
  491. #define INIT_TOKENBUF(tb)   ((tb)->ptr = (tb)->base)
  492. #define FINISH_TOKENBUF(tb) if (!AddToTokenBuf(cx, tb, 0)) RETURN(TOK_ERROR)
  493. #define TOKEN_LENGTH(tb)    ((tb)->ptr - (tb)->base - 1)
  494. #define RETURN(tt)          { if (tt == TOK_ERROR) ts->flags |= TSF_ERROR;    \
  495.                   return (ts->token.type = tt); }
  496.  
  497. #define NOTE_NEWLINE() {                                                      \
  498.     if (cg) {                                                                 \
  499.     if (ts->flags & TSF_LOOKAHEAD) {                                      \
  500.         /* Don't emit newline source notes during lookahead. */           \
  501.         ts->newlines++;                                                   \
  502.     } else {                                                              \
  503.         if (js_NewSrcNote(cx, cg, SRC_NEWLINE) < 0)                       \
  504.         RETURN(TOK_ERROR);                                            \
  505.     }                                                                     \
  506.     }                                                                         \
  507. }
  508.  
  509.     /* If there was a fatal error, keep returning TOK_ERROR. */
  510.     if (ts->flags & TSF_ERROR)
  511.     return TOK_ERROR;
  512.  
  513.     /* If done with lookahead, emit source notes for any skipped newlines. */
  514.     if (!(ts->flags & TSF_LOOKAHEAD) &&
  515.     ts->newlines != 0 &&
  516.     !js_FlushNewlines(cx, ts, cg)) {
  517.     RETURN(TOK_ERROR);
  518.     }
  519.  
  520.     /* Check for a pushed-back token resulting from mismatching lookahead. */
  521.     if (ts->pushback.type != TOK_EOF) {
  522.     ts->token = ts->pushback;
  523.     CLEAR_PUSHBACK(ts);
  524.     return ts->token.type;
  525.     }
  526.  
  527. retry:
  528.     do {
  529.     c = GetChar(ts);
  530.     if (c == '\n') {
  531.         NOTE_NEWLINE();
  532.         if (ts->flags & TSF_NEWLINES)
  533.         break;
  534.     }
  535.     } while (JS_ISSPACE(c));
  536.  
  537.     ts->token.ptr = ts->linebuf.ptr - 1;
  538.     if (c == EOF)
  539.     RETURN(TOK_EOF);
  540.  
  541.     if (JS_ISIDENT(c)) {
  542.     INIT_TOKENBUF(&ts->tokenbuf);
  543.     do {
  544.         if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
  545.         RETURN(TOK_ERROR);
  546.         c = GetChar(ts);
  547.     } while (JS_ISIDENT2(c));
  548.     UngetChar(ts, c);
  549.     FINISH_TOKENBUF(&ts->tokenbuf);
  550.  
  551.     atom = js_AtomizeChars(cx,
  552.                    ts->tokenbuf.base,
  553.                    TOKEN_LENGTH(&ts->tokenbuf),
  554.                    ATOM_NOHOLD);
  555.     if (!atom)
  556.         RETURN(TOK_ERROR);
  557.     if (atom->kwindex >= 0) {
  558.         struct keyword *kw;
  559.  
  560.         kw = &keywords[atom->kwindex];
  561.         ts->token.u.op = kw->op;
  562.         RETURN(kw->tokentype);
  563.     }
  564.     ts->token.u.atom = atom;
  565.     RETURN(TOK_NAME);
  566.     }
  567.  
  568.     if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) {
  569.     jsint radix;
  570.     jschar *endptr;
  571.     jsdouble dval;
  572.  
  573.     radix = 10;
  574.     INIT_TOKENBUF(&ts->tokenbuf);
  575.  
  576.     if (c == '0') {
  577.         if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
  578.         RETURN(TOK_ERROR);
  579.         c = GetChar(ts);
  580.         if (JS_TOLOWER(c) == 'x') {
  581.         if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
  582.             RETURN(TOK_ERROR);
  583.         c = GetChar(ts);
  584.         radix = 16;
  585.         } else if (JS7_ISDEC(c) && c < '8') {
  586.         radix = 8;
  587.         }
  588.     }
  589.  
  590.     while (JS7_ISHEX(c)) {
  591.         if (radix < 16 && (JS7_ISLET(c) || (radix == 8 && c >= '8')))
  592.         break;
  593.         if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
  594.         RETURN(TOK_ERROR);
  595.         c = GetChar(ts);
  596.     }
  597.  
  598.     if (radix == 10 && (c == '.' || JS_TOLOWER(c) == 'e')) {
  599.         if (c == '.') {
  600.         do {
  601.             if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
  602.             RETURN(TOK_ERROR);
  603.             c = GetChar(ts);
  604.         } while (JS7_ISDEC(c));
  605.         }
  606.         if (JS_TOLOWER(c) == 'e') {
  607.         if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
  608.             RETURN(TOK_ERROR);
  609.         c = GetChar(ts);
  610.         if (c == '+' || c == '-') {
  611.             if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
  612.             RETURN(TOK_ERROR);
  613.             c = GetChar(ts);
  614.         }
  615.         if (!JS7_ISDEC(c)) {
  616.             js_ReportCompileError(cx, ts, "missing exponent");
  617.             RETURN(TOK_ERROR);
  618.         }
  619.         do {
  620.             if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
  621.             RETURN(TOK_ERROR);
  622.             c = GetChar(ts);
  623.         } while (JS7_ISDEC(c));
  624.         }
  625.     }
  626.  
  627.     UngetChar(ts, c);
  628.     FINISH_TOKENBUF(&ts->tokenbuf);
  629.  
  630.     if (radix == 10) {
  631.         /* Let js_strtod() do the hard work and validity checks. */
  632.         if (!js_strtod(ts->tokenbuf.base, &endptr, &dval)) {
  633.         js_ReportCompileError(cx, ts,
  634.                       "floating point literal out of range");
  635.         RETURN(TOK_ERROR);
  636.         }
  637.     } else {
  638.         /* Let js_strtol() do the hard work, then check for overflow */
  639.         if (!js_strtol(ts->tokenbuf.base, &endptr, radix, &dval)) {
  640.         js_ReportCompileError(cx, ts, "integer literal too large");
  641.         RETURN(TOK_ERROR);
  642.         }
  643.     }
  644.     ts->token.u.dval = dval;
  645.     RETURN(TOK_NUMBER);
  646.     }
  647.  
  648.     if (c == '"' || c == '\'') {
  649.     int32 val, qc = c;
  650.  
  651.     INIT_TOKENBUF(&ts->tokenbuf);
  652.     while ((c = GetChar(ts)) != qc) {
  653.         if (c == '\n' || c == EOF) {
  654.         UngetChar(ts, c);
  655.         js_ReportCompileError(cx, ts, "unterminated string literal");
  656.         RETURN(TOK_ERROR);
  657.         }
  658.         if (c == '\\') {
  659.         switch (c = GetChar(ts)) {
  660.           case 'b': c = '\b'; break;
  661.           case 'f': c = '\f'; break;
  662.           case 'n': c = '\n'; break;
  663.           case 'r': c = '\r'; break;
  664.           case 't': c = '\t'; break;
  665.           case 'v': c = '\v'; break;
  666.  
  667.           default:
  668.             if ('0' <= c && c < '8') {
  669.             val = JS7_UNDEC(c);
  670.             c = PeekChar(ts);
  671.             if ('0' <= c && c < '8') {
  672.                 val = 8 * val + JS7_UNDEC(c);
  673.                 GetChar(ts);
  674.                 c = PeekChar(ts);
  675.                 if ('0' <= c && c < '8') {
  676.                 int32 save = val;
  677.                 val = 8 * val + JS7_UNDEC(c);
  678.                 if (val <= 0377)
  679.                     GetChar(ts);
  680.                 else
  681.                     val = save;
  682.                 }
  683.             }
  684.             c = (jschar)val;
  685.             } else if (c == 'u') {
  686.             jschar cp[4];
  687.             if (PeekChars(ts, 4, cp) &&
  688.                 JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) &&
  689.                 JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3])) {
  690.                 c = (((((JS7_UNHEX(cp[0]) << 4)
  691.                     + JS7_UNHEX(cp[1])) << 4)
  692.                   + JS7_UNHEX(cp[2])) << 4)
  693.                 + JS7_UNHEX(cp[3]);
  694.                 SkipChars(ts, 4);
  695.             }
  696.             } else if (c == 'x') {
  697.             jschar cp[2];
  698.             if (PeekChars(ts, 2, cp) &&
  699.                 JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1])) {
  700.                 c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]);
  701.                 SkipChars(ts, 2);
  702.             }
  703.             } else if (c == '\n') {
  704.             NOTE_NEWLINE();
  705.             }
  706.             break;
  707.         }
  708.         }
  709.         if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
  710.         RETURN(TOK_ERROR);
  711.     }
  712.     FINISH_TOKENBUF(&ts->tokenbuf);
  713.     atom = js_AtomizeChars(cx,
  714.                    ts->tokenbuf.base,
  715.                    TOKEN_LENGTH(&ts->tokenbuf),
  716.                    ATOM_NOHOLD);
  717.     if (!atom)
  718.         RETURN(TOK_ERROR);
  719.     ts->token.u.atom = atom;
  720.     RETURN(TOK_STRING);
  721.     }
  722.  
  723.     switch (c) {
  724.       case '\n': c = TOK_EOL; break;
  725.       case ';': c = TOK_SEMI; break;
  726.       case '[': c = TOK_LB; break;
  727.       case ']': c = TOK_RB; break;
  728.       case '{': c = TOK_LC; break;
  729.       case '}': c = TOK_RC; break;
  730.       case '(': c = TOK_LP; break;
  731.       case ')': c = TOK_RP; break;
  732.       case ',': c = TOK_COMMA; break;
  733.       case '?': c = TOK_HOOK; break;
  734.       case ':': c = TOK_COLON; break;
  735.       case '.': c = TOK_DOT; break;
  736.  
  737.       case '|':
  738.     if (MatchChar(ts, c)) {
  739.         c = TOK_OR;
  740.     } else if (MatchChar(ts, '=')) {
  741.         ts->token.u.op = JSOP_BITOR;
  742.         c = TOK_ASSIGN;
  743.     } else {
  744.         c = TOK_BITOR;
  745.     }
  746.     break;
  747.  
  748.       case '^':
  749.     if (MatchChar(ts, '=')) {
  750.         ts->token.u.op = JSOP_BITXOR;
  751.         c = TOK_ASSIGN;
  752.     } else {
  753.         c = TOK_BITXOR;
  754.     }
  755.     break;
  756.  
  757.       case '&':
  758.     if (MatchChar(ts, c)) {
  759.         c = TOK_AND;
  760.     } else if (MatchChar(ts, '=')) {
  761.         ts->token.u.op = JSOP_BITAND;
  762.         c = TOK_ASSIGN;
  763.     } else {
  764.         c = TOK_BITAND;
  765.     }
  766.     break;
  767.  
  768.       case '=':
  769.     if (MatchChar(ts, c)) {
  770. #if JS_HAS_TRIPLE_EQOPS
  771.         ts->token.u.op = MatchChar(ts, c) ? JSOP_NEW_EQ : cx->jsop_eq;
  772. #else
  773.         ts->token.u.op = cx->jsop_eq;
  774. #endif
  775.         c = TOK_EQOP;
  776.     } else {
  777.         ts->token.u.op = JSOP_NOP;
  778.         c = TOK_ASSIGN;
  779.     }
  780.     break;
  781.  
  782.       case '!':
  783.     if (MatchChar(ts, '=')) {
  784. #if JS_HAS_TRIPLE_EQOPS
  785.         ts->token.u.op = MatchChar(ts, '=') ? JSOP_NEW_NE : cx->jsop_ne;
  786. #else
  787.         ts->token.u.op = cx->jsop_ne;
  788. #endif
  789.         c = TOK_EQOP;
  790.     } else {
  791.         ts->token.u.op = JSOP_NOT;
  792.         c = TOK_UNARYOP;
  793.     }
  794.     break;
  795.  
  796.       case '<':
  797.     /* NB: treat HTML begin-comment as comment-till-end-of-line */
  798.     if (MatchChar(ts, '!')) {
  799.         if (MatchChar(ts, '-')) {
  800.         if (MatchChar(ts, '-'))
  801.             goto skipline;
  802.         UngetChar(ts, '-');
  803.         }
  804.         UngetChar(ts, '!');
  805.     }
  806.     if (MatchChar(ts, c)) {
  807.         ts->token.u.op = JSOP_LSH;
  808.         c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;
  809.     } else {
  810.         ts->token.u.op = MatchChar(ts, '=') ? JSOP_LE : JSOP_LT;
  811.         c = TOK_RELOP;
  812.     }
  813.     break;
  814.  
  815.       case '>':
  816.     if (MatchChar(ts, c)) {
  817.         ts->token.u.op = MatchChar(ts, c) ? JSOP_URSH : JSOP_RSH;
  818.         c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;
  819.     } else {
  820.         ts->token.u.op = MatchChar(ts, '=') ? JSOP_GE : JSOP_GT;
  821.         c = TOK_RELOP;
  822.     }
  823.     break;
  824.  
  825.       case '*':
  826.     ts->token.u.op = JSOP_MUL;
  827.     c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_STAR;
  828.     break;
  829.  
  830.       case '/':
  831.     if (MatchChar(ts, '/')) {
  832. skipline:
  833.         while ((c = GetChar(ts)) != EOF && c != '\n')
  834.         /* skip to end of line */;
  835.         UngetChar(ts, c);
  836.         goto retry;
  837.     }
  838.     if (MatchChar(ts, '*')) {
  839.         while ((c = GetChar(ts)) != EOF
  840.         && !(c == '*' && MatchChar(ts, '/'))) {
  841.         if (c == '\n') {
  842.             NOTE_NEWLINE();
  843.         } else if (c == '/' && MatchChar(ts, '*')) {
  844.             if (MatchChar(ts, '/'))
  845.             goto retry;
  846.             js_ReportCompileError(cx, ts, "nested comment");
  847.         }
  848.         }
  849.         if (c == EOF) {
  850.         js_ReportCompileError(cx, ts, "unterminated comment");
  851.         RETURN(TOK_ERROR);
  852.         }
  853.         goto retry;
  854.     }
  855.  
  856. #if JS_HAS_REGEXPS
  857.     if (ts->flags & TSF_REGEXP) {
  858.         JSObject *obj;
  859.         uintN flags;
  860.  
  861.         INIT_TOKENBUF(&ts->tokenbuf);
  862.         while ((c = GetChar(ts)) != '/') {
  863.         if (c == '\n' || c == EOF) {
  864.             UngetChar(ts, c);
  865.             js_ReportCompileError(cx, ts,
  866.             "unterminated regular expression literal");
  867.             RETURN(TOK_ERROR);
  868.         }
  869.         if (c == '\\') {
  870.             if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
  871.             RETURN(TOK_ERROR);
  872.             c = GetChar(ts);
  873.         }
  874.         if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
  875.             RETURN(TOK_ERROR);
  876.         }
  877.         FINISH_TOKENBUF(&ts->tokenbuf);
  878.         for (flags = 0; ; ) {
  879.         if (MatchChar(ts, 'g'))
  880.             flags |= JSREG_GLOB;
  881.         else if (MatchChar(ts, 'i'))
  882.             flags |= JSREG_FOLD;
  883.         else
  884.             break;
  885.         }
  886.         c = PeekChar(ts);
  887.         if (JS7_ISLET(c)) {
  888.         ts->token.ptr = ts->linebuf.ptr - 1;
  889.         js_ReportCompileError(cx, ts,
  890.                       "invalid flag after regular expression");
  891.         RETURN(TOK_ERROR);
  892.         }
  893.         obj = js_NewRegExpObject(cx,
  894.                      ts->tokenbuf.base,
  895.                      TOKEN_LENGTH(&ts->tokenbuf),
  896.                      flags);
  897.         if (!obj)
  898.         RETURN(TOK_ERROR);
  899.         atom = js_AtomizeObject(cx, obj, ATOM_NOHOLD);
  900.         if (!atom)
  901.         RETURN(TOK_ERROR);
  902.         ts->token.u.atom = atom;
  903.         RETURN(TOK_OBJECT);
  904.     }
  905. #endif /* JS_HAS_REGEXPS */
  906.  
  907.     ts->token.u.op = JSOP_DIV;
  908.     c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP;
  909.     break;
  910.  
  911.       case '%':
  912.     ts->token.u.op = JSOP_MOD;
  913.     c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP;
  914.     break;
  915.  
  916.       case '~':
  917.     ts->token.u.op = JSOP_BITNOT;
  918.     c = TOK_UNARYOP;
  919.     break;
  920.  
  921.       case '+':
  922.       case '-':
  923.     if (MatchChar(ts, '=')) {
  924.         ts->token.u.op = (c == '+') ? JSOP_ADD : JSOP_SUB;
  925.         c = TOK_ASSIGN;
  926.     } else if (MatchChar(ts, c)) {
  927.         c = (c == '+') ? TOK_INC : TOK_DEC;
  928.     } else if (c == '-') {
  929.         ts->token.u.op = JSOP_NEG;
  930.         c = TOK_MINUS;
  931.     } else {
  932.         ts->token.u.op = JSOP_POS;
  933.         c = TOK_PLUS;
  934.     }
  935.     break;
  936.  
  937. #if JS_HAS_SHARP_VARS
  938.       case '#':
  939.       {
  940.     uint32 n;
  941.  
  942.     c = GetChar(ts);
  943.     if (!JS7_ISDEC(c)) {
  944.         UngetChar(ts, c);
  945.         goto badchar;
  946.     }
  947.     n = (uint32)JS7_UNDEC(c);
  948.     for (;;) {
  949.         c = GetChar(ts);
  950.         if (!JS7_ISDEC(c))
  951.         break;
  952.         n = 10 * n + JS7_UNDEC(c);
  953.         if (n >= ATOM_INDEX_LIMIT) {
  954.         js_ReportCompileError(cx, ts,
  955.                       "overlarge sharp variable number");
  956.         RETURN(TOK_ERROR);
  957.         }
  958.     }
  959.     ts->token.u.dval = (jsdouble) n;
  960.     if (c == '=')
  961.         RETURN(TOK_DEFSHARP);
  962.     if (c == '#')
  963.         RETURN(TOK_USESHARP);
  964.     goto badchar;
  965.       }
  966.  
  967. #endif /* JS_HAS_SHARP_VARS */
  968.  
  969.       badchar:
  970.       default:
  971.     js_ReportCompileError(cx, ts, "illegal character");
  972.     RETURN(TOK_ERROR);
  973.     }
  974.  
  975.     PR_ASSERT(c < TOK_LIMIT);
  976.     RETURN(c);
  977.  
  978. #undef INIT_TOKENBUF
  979. #undef FINISH_TOKENBUF
  980. #undef TOKEN_LENGTH
  981. #undef RETURN
  982. #undef NOTE_NEWLINE
  983. }
  984.  
  985. void
  986. js_UngetToken(JSTokenStream *ts)
  987. {
  988.     PR_ASSERT(ts->pushback.type == TOK_EOF);
  989.     if (ts->flags & TSF_ERROR)
  990.     return;
  991.     ts->pushback = ts->token;
  992. }
  993.  
  994. JSBool
  995. js_MatchToken(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg,
  996.          JSTokenType tt)
  997. {
  998.     JSTokenType got;
  999.  
  1000.     ts->flags |= TSF_LOOKAHEAD;
  1001.     got = js_GetToken(cx, ts, cg);
  1002.     ts->flags &= ~TSF_LOOKAHEAD;
  1003.     if (got == tt) {
  1004.     if (ts->newlines != 0 && !js_FlushNewlines(cx, ts, cg))
  1005.         return JS_FALSE;
  1006.     return JS_TRUE;
  1007.     }
  1008.     js_UngetToken(ts);
  1009.     return JS_FALSE;
  1010. }
  1011.  
  1012. JSBool
  1013. js_FlushNewlines(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg)
  1014. {
  1015.     while (ts->newlines != 0) {
  1016.     if (js_NewSrcNote(cx, cg, SRC_NEWLINE) < 0)
  1017.         return JS_FALSE;
  1018.     ts->newlines--;
  1019.     }
  1020.     return JS_TRUE;
  1021. }
  1022.