Exemplo n.º 1
0
static JSBool
HandleNumber(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
{
    JSBool ok;
    jsuint length;
    if (!JS_GetArrayLength(cx, jp->objectStack, &length))
        return JS_FALSE;
 
    jsval o;
    if (!JS_GetElement(cx, jp->objectStack, length - 1, &o))
        return JS_FALSE;
    JS_ASSERT(JSVAL_IS_OBJECT(o));
    JSObject *obj = JSVAL_TO_OBJECT(o);
 
    const jschar *ep;
    double val;    
    // if (!js_strtod(cx, buf, buf + len, &ep, &val) || ep != buf + len)
    if (!js_strtod(cx, buf, &ep, &val) || ep != buf + len)
        return JS_FALSE;
 
    jsval numVal;
    if (JS_NewNumberValue(cx, val, &numVal))
        ok = PushValue(cx, jp, obj, numVal);
    else
        ok = JS_FALSE; // decode error
 
    return ok;
}
Exemplo n.º 2
0
double js_stringtofloat(const char *s, char **ep)
{
	char *end;
	double n;
	const char *e = s;
	int isflt = 0;
	if (*e == '+' || *e == '-') ++e;
	while (*e >= '0' && *e <= '9') ++e;
	if (*e == '.') { ++e; isflt = 1; }
	while (*e >= '0' && *e <= '9') ++e;
	if (*e == 'e' || *e == 'E') {
		++e;
		if (*e == '+' || *e == '-') ++e;
		while (*e >= '0' && *e <= '9') ++e;
		isflt = 1;
	}
	if (isflt || e - s > 9)
		n = js_strtod(s, &end);
	else
		n = strtol(s, &end, 10);
	if (end == e) {
		*ep = (char*)e;
		return n;
	}
	*ep = (char*)s;
	return 0;
}
Exemplo n.º 3
0
static JSBool
HandleNumber(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
{
    const jschar *ep;
    double val;
    if (!js_strtod(cx, buf, buf + len, &ep, &val) || ep != buf + len)
        return JS_FALSE;

    JSBool ok;
    jsval numVal;
    JSObject *obj = GetTopOfObjectStack(cx, jp);
    if (obj && JS_NewNumberValue(cx, val, &numVal))
        ok = PushValue(cx, jp, obj, numVal);
    else
        ok = JS_FALSE; // decode error

    return ok;
}
Exemplo n.º 4
0
static int lexnumber(js_State *J)
{
	const char *s = J->source - 1;

	if (jsY_accept(J, '0')) {
		if (jsY_accept(J, 'x') || jsY_accept(J, 'X')) {
			J->number = lexhex(J);
			return TK_NUMBER;
		}
		if (jsY_isdec(J->lexchar))
			jsY_error(J, "number with leading zero");
		if (jsY_accept(J, '.')) {
			while (jsY_isdec(J->lexchar))
				jsY_next(J);
		}
	} else if (jsY_accept(J, '.')) {
		if (!jsY_isdec(J->lexchar))
			return '.';
		while (jsY_isdec(J->lexchar))
			jsY_next(J);
	} else {
		while (jsY_isdec(J->lexchar))
			jsY_next(J);
		if (jsY_accept(J, '.')) {
			while (jsY_isdec(J->lexchar))
				jsY_next(J);
		}
	}

	if (jsY_accept(J, 'e') || jsY_accept(J, 'E')) {
		if (J->lexchar == '-' || J->lexchar == '+')
			jsY_next(J);
		while (jsY_isdec(J->lexchar))
			jsY_next(J);
	}

	if (jsY_isidentifierstart(J->lexchar))
		jsY_error(J, "number with letter suffix");

	J->number = js_strtod(s, NULL);
	return TK_NUMBER;

}
Exemplo n.º 5
0
double js_stringtofloat(const char *s, char **ep)
{
	char *end;
	double n;
	const char *e = s;
	if (*e == '+' || *e == '-') ++e;
	while (*e >= '0' && *e <= '9') ++e;
	if (*e == '.') ++e;
	while (*e >= '0' && *e <= '9') ++e;
	if (*e == 'e' || *e == 'E') {
		++e;
		if (*e == '+' || *e == '-') ++e;
		while (*e >= '0' && *e <= '9') ++e;
	}
	n = js_strtod(s, &end);
	if (end == e) {
		*ep = (char*)e;
		return n;
	}
	*ep = (char*)s;
	return 0;
}
Exemplo n.º 6
0
/*
 * compute decimal integer m, exp such that:
 *	f = m*10^exp
 *	m is as short as possible with losing exactness
 * assumes special cases (NaN, +Inf, -Inf) have been handled.
 */
void
js_dtoa(double f, char *s, int *exp, int *neg, int *ns)
{
	int c, d, e2, e, ee, i, ndigit, oerrno;
	char tmp[NSIGNIF+10];
	double g;

	oerrno = errno; /* in case strtod smashes errno */

	/*
	 * make f non-negative.
	 */
	*neg = 0;
	if(f < 0) {
		f = -f;
		*neg = 1;
	}

	/*
	 * must handle zero specially.
	 */
	if(f == 0){
		*exp = 0;
		s[0] = '0';
		s[1] = '\0';
		*ns = 1;
		return;
	}

	/*
	 * find g,e such that f = g*10^e.
	 * guess 10-exponent using 2-exponent, then fine tune.
	 */
	frexp(f, &e2);
	e = (int)(e2 * .301029995664);
	g = f * pow10(-e);
	while(g < 1) {
		e--;
		g = f * pow10(-e);
	}
	while(g >= 10) {
		e++;
		g = f * pow10(-e);
	}

	/*
	 * convert NSIGNIF digits as a first approximation.
	 */
	for(i=0; i<NSIGNIF; i++) {
		d = (int)g;
		s[i] = d+'0';
		g = (g-d) * 10;
	}
	s[i] = 0;

	/*
	 * adjust e because s is 314159... not 3.14159...
	 */
	e -= NSIGNIF-1;
	js_fmtexp(s+NSIGNIF, e);

	/*
	 * adjust conversion until strtod(s) == f exactly.
	 */
	for(i=0; i<10; i++) {
		g = js_strtod(s, NULL);
		if(f > g) {
			if(xadd1(s, NSIGNIF)) {
				/* gained a digit */
				e--;
				js_fmtexp(s+NSIGNIF, e);
			}
			continue;
		}
		if(f < g) {
			if(xsub1(s, NSIGNIF)) {
				/* lost a digit */
				e++;
				js_fmtexp(s+NSIGNIF, e);
			}
			continue;
		}
		break;
	}

	/*
	 * play with the decimal to try to simplify.
	 */

	/*
	 * bump last few digits up to 9 if we can
	 */
	for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) {
		c = s[i];
		if(c != '9') {
			s[i] = '9';
			g = js_strtod(s, NULL);
			if(g != f) {
				s[i] = c;
				break;
			}
		}
	}

	/*
	 * add 1 in hopes of turning 9s to 0s
	 */
	if(s[NSIGNIF-1] == '9') {
		strcpy(tmp, s);
		ee = e;
		if(xadd1(tmp, NSIGNIF)) {
			ee--;
			js_fmtexp(tmp+NSIGNIF, ee);
		}
		g = js_strtod(tmp, NULL);
		if(g == f) {
			strcpy(s, tmp);
			e = ee;
		}
	}

	/*
	 * bump last few digits down to 0 as we can.
	 */
	for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) {
		c = s[i];
		if(c != '0') {
			s[i] = '0';
			g = js_strtod(s, NULL);
			if(g != f) {
				s[i] = c;
				break;
			}
		}
	}

	/*
	 * remove trailing zeros.
	 */
	ndigit = NSIGNIF;
	while(ndigit > 1 && s[ndigit-1] == '0'){
		e++;
		--ndigit;
	}
	s[ndigit] = 0;
	*exp = e;
	*ns = ndigit;
	errno = oerrno;
}
Exemplo n.º 7
0
Arquivo: jsscan.c Projeto: artcom/y60
JSTokenType
js_GetToken(JSContext *cx, JSTokenStream *ts)
{
    JSTokenType tt;
    JSToken *tp;
    int32 c;
    JSAtom *atom;
    JSBool hadUnicodeEscape;

#define INIT_TOKENBUF(tb)   ((tb)->ptr = (tb)->base)
#define FINISH_TOKENBUF(tb) if (!AddToTokenBuf(cx, tb, 0)) RETURN(TOK_ERROR)
#define TOKEN_LENGTH(tb)    ((tb)->ptr - (tb)->base - 1)
#define RETURN(tt)          { if (tt == TOK_ERROR) ts->flags |= TSF_ERROR;    \
                              tp->pos.end.index = ts->linepos +               \
                                  (ts->linebuf.ptr - ts->linebuf.base) -      \
                                  ts->ungetpos;                               \
                              return (tp->type = tt); }

    /* If there was a fatal error, keep returning TOK_ERROR. */
    if (ts->flags & TSF_ERROR)
        return TOK_ERROR;

    /* Check for a pushed-back token resulting from mismatching lookahead. */
    while (ts->lookahead != 0) {
        ts->lookahead--;
        ts->cursor = (ts->cursor + 1) & NTOKENS_MASK;
        tt = CURRENT_TOKEN(ts).type;
        if (tt != TOK_EOL || (ts->flags & TSF_NEWLINES))
            return tt;
    }

retry:
    do {
        c = GetChar(ts);
        if (c == '\n') {
            ts->flags &= ~TSF_DIRTYLINE;
            if (ts->flags & TSF_NEWLINES)
                break;
        }
    } while (JS_ISSPACE(c));

    ts->cursor = (ts->cursor + 1) & NTOKENS_MASK;
    tp = &CURRENT_TOKEN(ts);
    tp->ptr = ts->linebuf.ptr - 1;
    tp->pos.begin.index = ts->linepos + (tp->ptr - ts->linebuf.base);
    tp->pos.begin.lineno = tp->pos.end.lineno = ts->lineno;

    if (c == EOF)
        RETURN(TOK_EOF);
    if (c != '-' && c != '\n')
        ts->flags |= TSF_DIRTYLINE;

    hadUnicodeEscape = JS_FALSE;
    if (JS_ISIDENT_START(c) ||
        (c == '\\' &&
         (c = GetUnicodeEscape(ts),
          hadUnicodeEscape = JS_ISIDENT_START(c)))) {
        INIT_TOKENBUF(&ts->tokenbuf);
        for (;;) {
            if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
                RETURN(TOK_ERROR);
            c = GetChar(ts);
            if (c == '\\') {
                c = GetUnicodeEscape(ts);
                if (!JS_ISIDENT(c))
                    break;
                hadUnicodeEscape = JS_TRUE;
            } else {
                if (!JS_ISIDENT(c))
                    break;
            }
        }
        UngetChar(ts, c);
        FINISH_TOKENBUF(&ts->tokenbuf);

        atom = js_AtomizeChars(cx,
                               ts->tokenbuf.base,
                               TOKEN_LENGTH(&ts->tokenbuf),
                               0);
        if (!atom)
            RETURN(TOK_ERROR);
        if (!hadUnicodeEscape && ATOM_KEYWORD(atom)) {
            struct keyword *kw = ATOM_KEYWORD(atom);

            if (JSVERSION_IS_ECMA(cx->version) || kw->version <= cx->version) {
                tp->t_op = (JSOp) kw->op;
                RETURN(kw->tokentype);
            }
        }
        tp->t_op = JSOP_NAME;
        tp->t_atom = atom;
        RETURN(TOK_NAME);
    }

    if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) {
        jsint radix;
        const jschar *endptr;
        jsdouble dval;

        radix = 10;
        INIT_TOKENBUF(&ts->tokenbuf);

        if (c == '0') {
            if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
                RETURN(TOK_ERROR);
            c = GetChar(ts);
            if (JS_TOLOWER(c) == 'x') {
                if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
                    RETURN(TOK_ERROR);
                c = GetChar(ts);
                radix = 16;
            } else if (JS7_ISDEC(c)) {
                radix = 8;
            }
        }

        while (JS7_ISHEX(c)) {
            if (radix < 16) {
                if (JS7_ISLET(c))
                    break;

                /*
                 * We permit 08 and 09 as decimal numbers, which makes our
                 * behaviour a superset of the ECMA numeric grammar.  We might
                 * not always be so permissive, so we warn about it.
                 */
                if (radix == 8 && c >= '8') {
                    if (!js_ReportCompileErrorNumber(cx, ts, NULL,
                                                     JSREPORT_WARNING,
                                                     JSMSG_BAD_OCTAL,
                                                     c == '8' ? "08" : "09")) {
                        RETURN(TOK_ERROR);
                    }
                    radix = 10;
                }
            }
            if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
                RETURN(TOK_ERROR);
            c = GetChar(ts);
        }

        if (radix == 10 && (c == '.' || JS_TOLOWER(c) == 'e')) {
            if (c == '.') {
                do {
                    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
                        RETURN(TOK_ERROR);
                    c = GetChar(ts);
                } while (JS7_ISDEC(c));
            }
            if (JS_TOLOWER(c) == 'e') {
                if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
                    RETURN(TOK_ERROR);
                c = GetChar(ts);
                if (c == '+' || c == '-') {
                    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
                        RETURN(TOK_ERROR);
                    c = GetChar(ts);
                }
                if (!JS7_ISDEC(c)) {
                    js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
                                                JSMSG_MISSING_EXPONENT);
                    RETURN(TOK_ERROR);
                }
                do {
                    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
                        RETURN(TOK_ERROR);
                    c = GetChar(ts);
                } while (JS7_ISDEC(c));
            }
        }

        UngetChar(ts, c);
        FINISH_TOKENBUF(&ts->tokenbuf);

        if (radix == 10) {
            if (!js_strtod(cx, ts->tokenbuf.base, &endptr, &dval)) {
                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
                                            JSMSG_OUT_OF_MEMORY);
                RETURN(TOK_ERROR);
            }
        } else {
            if (!js_strtointeger(cx, ts->tokenbuf.base, &endptr, radix, &dval)) {
                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
                                            JSMSG_OUT_OF_MEMORY);
                RETURN(TOK_ERROR);
            }
        }
        tp->t_dval = dval;
        RETURN(TOK_NUMBER);
    }

    if (c == '"' || c == '\'') {
        int32 val, qc = c;

        INIT_TOKENBUF(&ts->tokenbuf);
        while ((c = GetChar(ts)) != qc) {
            if (c == '\n' || c == EOF) {
                UngetChar(ts, c);
                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
                                            JSMSG_UNTERMINATED_STRING);
                RETURN(TOK_ERROR);
            }
            if (c == '\\') {
                switch (c = GetChar(ts)) {
                  case 'b': c = '\b'; break;
                  case 'f': c = '\f'; break;
                  case 'n': c = '\n'; break;
                  case 'r': c = '\r'; break;
                  case 't': c = '\t'; break;
                  case 'v': c = '\v'; break;

                  default:
                    if ('0' <= c && c < '8') {
                        val = JS7_UNDEC(c);
                        c = PeekChar(ts);
                        if ('0' <= c && c < '8') {
                            val = 8 * val + JS7_UNDEC(c);
                            GetChar(ts);
                            c = PeekChar(ts);
                            if ('0' <= c && c < '8') {
                                int32 save = val;
                                val = 8 * val + JS7_UNDEC(c);
                                if (val <= 0377)
                                    GetChar(ts);
                                else
                                    val = save;
                            }
                        }
                        c = (jschar)val;
                    } else if (c == 'u') {
                        jschar cp[4];
                        if (PeekChars(ts, 4, cp) &&
                            JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) &&
                            JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3])) {
                            c = (((((JS7_UNHEX(cp[0]) << 4)
                                    + JS7_UNHEX(cp[1])) << 4)
                                  + JS7_UNHEX(cp[2])) << 4)
                                + JS7_UNHEX(cp[3]);
                            SkipChars(ts, 4);
                        }
                    } else if (c == 'x') {
                        jschar cp[2];
                        if (PeekChars(ts, 2, cp) &&
                            JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1])) {
                            c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]);
                            SkipChars(ts, 2);
                        }
                    } else if (c == '\n' && JSVERSION_IS_ECMA(cx->version)) {
                        /* ECMA follows C by removing escaped newlines. */
                        continue;
                    }
                    break;
                }
            }
            if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
                RETURN(TOK_ERROR);
        }
        FINISH_TOKENBUF(&ts->tokenbuf);
        atom = js_AtomizeChars(cx,
                               ts->tokenbuf.base,
                               TOKEN_LENGTH(&ts->tokenbuf),
                               0);
        if (!atom)
            RETURN(TOK_ERROR);
        tp->pos.end.lineno = ts->lineno;
        tp->t_op = JSOP_STRING;
        tp->t_atom = atom;
        RETURN(TOK_STRING);
    }

    switch (c) {
      case '\n': 
        c = TOK_EOL; 
        break;

      case ';': c = TOK_SEMI; break;
      case '.': c = TOK_DOT; break;
      case '[': c = TOK_LB; break;
      case ']': c = TOK_RB; break;
      case '{': c = TOK_LC; break;
      case '}': c = TOK_RC; break;
      case '(': c = TOK_LP; break;
      case ')': c = TOK_RP; break;
      case ',': c = TOK_COMMA; break;
      case '?': c = TOK_HOOK; break;

      case ':':
        /*
         * Default so compiler can modify to JSOP_GETTER if 'p getter: v' in an
         * object initializer, likewise for setter.
         */
        tp->t_op = JSOP_NOP;
        c = TOK_COLON;
        break;

      case '|':
        if (MatchChar(ts, c)) {
            c = TOK_OR;
        } else if (MatchChar(ts, '=')) {
            tp->t_op = JSOP_BITOR;
            c = TOK_ASSIGN;
        } else {
            c = TOK_BITOR;
        }
        break;

      case '^':
        if (MatchChar(ts, '=')) {
            tp->t_op = JSOP_BITXOR;
            c = TOK_ASSIGN;
        } else {
            c = TOK_BITXOR;
        }
        break;

      case '&':
        if (MatchChar(ts, c)) {
            c = TOK_AND;
        } else if (MatchChar(ts, '=')) {
            tp->t_op = JSOP_BITAND;
            c = TOK_ASSIGN;
        } else {
            c = TOK_BITAND;
        }
        break;

      case '=':
        if (MatchChar(ts, c)) {
#if JS_HAS_TRIPLE_EQOPS
            tp->t_op = MatchChar(ts, c) ? JSOP_NEW_EQ : (JSOp)cx->jsop_eq;
#else
            tp->t_op = cx->jsop_eq;
#endif
            c = TOK_EQOP;
        } else {
            tp->t_op = JSOP_NOP;
            c = TOK_ASSIGN;
        }
        break;

      case '!':
        if (MatchChar(ts, '=')) {
#if JS_HAS_TRIPLE_EQOPS
            tp->t_op = MatchChar(ts, '=') ? JSOP_NEW_NE : (JSOp)cx->jsop_ne;
#else
            tp->t_op = cx->jsop_ne;
#endif
            c = TOK_EQOP;
        } else {
            tp->t_op = JSOP_NOT;
            c = TOK_UNARYOP;
        }
        break;

      case '<':
        /* NB: treat HTML begin-comment as comment-till-end-of-line */
        if (MatchChar(ts, '!')) {
            if (MatchChar(ts, '-')) {
                if (MatchChar(ts, '-'))
                    goto skipline;
                UngetChar(ts, '-');
            }
            UngetChar(ts, '!');
        }
        if (MatchChar(ts, c)) {
            tp->t_op = JSOP_LSH;
            c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;
        } else {
            tp->t_op = MatchChar(ts, '=') ? JSOP_LE : JSOP_LT;
            c = TOK_RELOP;
        }
        break;

      case '>':
        if (MatchChar(ts, c)) {
            tp->t_op = MatchChar(ts, c) ? JSOP_URSH : JSOP_RSH;
            c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;
        } else {
            tp->t_op = MatchChar(ts, '=') ? JSOP_GE : JSOP_GT;
            c = TOK_RELOP;
        }
        break;

      case '*':
        tp->t_op = JSOP_MUL;
        c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_STAR;
        break;

      case '/':
        if (MatchChar(ts, '/')) {
skipline:
            while ((c = GetChar(ts)) != EOF && c != '\n')
                /* skip to end of line */;
            UngetChar(ts, c);
            goto retry;
        }
        if (MatchChar(ts, '*')) {
            while ((c = GetChar(ts)) != EOF &&
                   !(c == '*' && MatchChar(ts, '/'))) {
                /* Ignore all characters until comment close. */
            }
            if (c == EOF) {
                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
                                            JSMSG_UNTERMINATED_COMMENT);
                RETURN(TOK_ERROR);
            }
            goto retry;
        }

#if JS_HAS_REGEXPS
        if (ts->flags & TSF_REGEXP) {
            JSObject *obj;
            uintN flags;

            INIT_TOKENBUF(&ts->tokenbuf);
            while ((c = GetChar(ts)) != '/') {
                if (c == '\n' || c == EOF) {
                    UngetChar(ts, c);
                    js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
                                                JSMSG_UNTERMINATED_REGEXP);
                    RETURN(TOK_ERROR);
                }
                if (c == '\\') {
                    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
                        RETURN(TOK_ERROR);
                    c = GetChar(ts);
                }
                if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
                    RETURN(TOK_ERROR);
            }
            FINISH_TOKENBUF(&ts->tokenbuf);
            for (flags = 0; ; ) {
                if (MatchChar(ts, 'g'))
                    flags |= JSREG_GLOB;
                else if (MatchChar(ts, 'i'))
                    flags |= JSREG_FOLD;
                else if (MatchChar(ts, 'm'))
                    flags |= JSREG_MULTILINE;
                else
                    break;
            }
            c = PeekChar(ts);
            if (JS7_ISLET(c)) {
                tp->ptr = ts->linebuf.ptr - 1;
                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
                                            JSMSG_BAD_REGEXP_FLAG);
                (void) GetChar(ts);
                RETURN(TOK_ERROR);
            }
            obj = js_NewRegExpObject(cx, ts,
                                     ts->tokenbuf.base,
                                     TOKEN_LENGTH(&ts->tokenbuf),
                                     flags);
            if (!obj)
                RETURN(TOK_ERROR);
            atom = js_AtomizeObject(cx, obj, 0);
            if (!atom)
                RETURN(TOK_ERROR);
            tp->t_op = JSOP_OBJECT;
            tp->t_atom = atom;
            RETURN(TOK_OBJECT);
        }
#endif /* JS_HAS_REGEXPS */

        tp->t_op = JSOP_DIV;
        c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP;
        break;

      case '%':
        tp->t_op = JSOP_MOD;
        c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP;
        break;

      case '~':
        tp->t_op = JSOP_BITNOT;
        c = TOK_UNARYOP;
        break;

      case '+':
        if (MatchChar(ts, '=')) {
            tp->t_op = JSOP_ADD;
            c = TOK_ASSIGN;
        } else if (MatchChar(ts, c)) {
            c = TOK_INC;
        } else {
            tp->t_op = JSOP_POS;
            c = TOK_PLUS;
        }
        break;

      case '-':
        if (MatchChar(ts, '=')) {
            tp->t_op = JSOP_SUB;
            c = TOK_ASSIGN;
        } else if (MatchChar(ts, c)) {
            if (PeekChar(ts) == '>' && !(ts->flags & TSF_DIRTYLINE))
                goto skipline;
            c = TOK_DEC;
        } else {
            tp->t_op = JSOP_NEG;
            c = TOK_MINUS;
        }
        ts->flags |= TSF_DIRTYLINE;
        break;

#if JS_HAS_SHARP_VARS
      case '#':
      {
        uint32 n;

        c = GetChar(ts);
        if (!JS7_ISDEC(c)) {
            UngetChar(ts, c);
            goto badchar;
        }
        n = (uint32)JS7_UNDEC(c);
        for (;;) {
            c = GetChar(ts);
            if (!JS7_ISDEC(c))
                break;
            n = 10 * n + JS7_UNDEC(c);
            if (n >= ATOM_INDEX_LIMIT) {
                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
                                            JSMSG_SHARPVAR_TOO_BIG);
                RETURN(TOK_ERROR);
            }
        }
        tp->t_dval = (jsdouble) n;
        if (JS_HAS_STRICT_OPTION(cx) &&
            (c == '=' || c == '#')) {
            char buf[20];
            JS_snprintf(buf, sizeof buf, "#%u%c", n, c);
            if (!js_ReportCompileErrorNumber(cx, ts, NULL,
                                             JSREPORT_WARNING |
                                             JSREPORT_STRICT,
                                             JSMSG_DEPRECATED_USAGE,
                                             buf)) {
                RETURN(TOK_ERROR);
            }
        }
        if (c == '=')
            RETURN(TOK_DEFSHARP);
        if (c == '#')
            RETURN(TOK_USESHARP);
        goto badchar;
      }

      badchar:
#endif /* JS_HAS_SHARP_VARS */

      default:
        js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
                                    JSMSG_ILLEGAL_CHARACTER);
        RETURN(TOK_ERROR);
    }

    JS_ASSERT(c < TOK_LIMIT);
    RETURN((JSTokenType)c);

#undef INIT_TOKENBUF
#undef FINISH_TOKENBUF
#undef TOKEN_LENGTH
#undef RETURN
}