1 #include "jsi.h"
2 #include "jslex.h"
3 #include "utf.h"
4 
5 JS_NORETURN static void jsY_error(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
6 
jsY_error(js_State * J,const char * fmt,...)7 static void jsY_error(js_State *J, const char *fmt, ...)
8 {
9 	va_list ap;
10 	char buf[512];
11 	char msgbuf[256];
12 
13 	va_start(ap, fmt);
14 	vsnprintf(msgbuf, 256, fmt, ap);
15 	va_end(ap);
16 
17 	snprintf(buf, 256, "%s:%d: ", J->filename, J->lexline);
18 	strcat(buf, msgbuf);
19 
20 	js_newsyntaxerror(J, buf);
21 	js_throw(J);
22 }
23 
24 static const char *tokenstring[] = {
25 	"(end-of-file)",
26 	"'\\x01'", "'\\x02'", "'\\x03'", "'\\x04'", "'\\x05'", "'\\x06'", "'\\x07'",
27 	"'\\x08'", "'\\x09'", "'\\x0A'", "'\\x0B'", "'\\x0C'", "'\\x0D'", "'\\x0E'", "'\\x0F'",
28 	"'\\x10'", "'\\x11'", "'\\x12'", "'\\x13'", "'\\x14'", "'\\x15'", "'\\x16'", "'\\x17'",
29 	"'\\x18'", "'\\x19'", "'\\x1A'", "'\\x1B'", "'\\x1C'", "'\\x1D'", "'\\x1E'", "'\\x1F'",
30 	"' '", "'!'", "'\"'", "'#'", "'$'", "'%'", "'&'", "'\\''",
31 	"'('", "')'", "'*'", "'+'", "','", "'-'", "'.'", "'/'",
32 	"'0'", "'1'", "'2'", "'3'", "'4'", "'5'", "'6'", "'7'",
33 	"'8'", "'9'", "':'", "';'", "'<'", "'='", "'>'", "'?'",
34 	"'@'", "'A'", "'B'", "'C'", "'D'", "'E'", "'F'", "'G'",
35 	"'H'", "'I'", "'J'", "'K'", "'L'", "'M'", "'N'", "'O'",
36 	"'P'", "'Q'", "'R'", "'S'", "'T'", "'U'", "'V'", "'W'",
37 	"'X'", "'Y'", "'Z'", "'['", "'\'", "']'", "'^'", "'_'",
38 	"'`'", "'a'", "'b'", "'c'", "'d'", "'e'", "'f'", "'g'",
39 	"'h'", "'i'", "'j'", "'k'", "'l'", "'m'", "'n'", "'o'",
40 	"'p'", "'q'", "'r'", "'s'", "'t'", "'u'", "'v'", "'w'",
41 	"'x'", "'y'", "'z'", "'{'", "'|'", "'}'", "'~'", "'\\x7F'",
42 
43 	0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
44 	0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
45 	0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
46 	0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
47 	0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
48 	0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
49 	0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
50 	0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
51 
52 	"(identifier)", "(number)", "(string)", "(regexp)",
53 
54 	"'<='", "'>='", "'=='", "'!='", "'==='", "'!=='",
55 	"'<<'", "'>>'", "'>>>'", "'&&'", "'||'",
56 	"'+='", "'-='", "'*='", "'/='", "'%='",
57 	"'<<='", "'>>='", "'>>>='", "'&='", "'|='", "'^='",
58 	"'++'", "'--'",
59 
60 	"'break'", "'case'", "'catch'", "'continue'", "'debugger'",
61 	"'default'", "'delete'", "'do'", "'else'", "'false'", "'finally'", "'for'",
62 	"'function'", "'if'", "'in'", "'instanceof'", "'new'", "'null'", "'return'",
63 	"'switch'", "'this'", "'throw'", "'true'", "'try'", "'typeof'", "'var'",
64 	"'void'", "'while'", "'with'",
65 };
66 
jsY_tokenstring(int token)67 const char *jsY_tokenstring(int token)
68 {
69 	if (token >= 0 && token < (int)nelem(tokenstring))
70 		if (tokenstring[token])
71 			return tokenstring[token];
72 	return "<unknown>";
73 }
74 
75 static const char *keywords[] = {
76 	"break", "case", "catch", "continue", "debugger", "default", "delete",
77 	"do", "else", "false", "finally", "for", "function", "if", "in",
78 	"instanceof", "new", "null", "return", "switch", "this", "throw",
79 	"true", "try", "typeof", "var", "void", "while", "with",
80 };
81 
jsY_findword(const char * s,const char ** list,int num)82 int jsY_findword(const char *s, const char **list, int num)
83 {
84 	int l = 0;
85 	int r = num - 1;
86 	while (l <= r) {
87 		int m = (l + r) >> 1;
88 		int c = strcmp(s, list[m]);
89 		if (c < 0)
90 			r = m - 1;
91 		else if (c > 0)
92 			l = m + 1;
93 		else
94 			return m;
95 	}
96 	return -1;
97 }
98 
jsY_findkeyword(js_State * J,const char * s)99 static int jsY_findkeyword(js_State *J, const char *s)
100 {
101 	int i = jsY_findword(s, keywords, nelem(keywords));
102 	if (i >= 0) {
103 		J->text = keywords[i];
104 		return TK_BREAK + i; /* first keyword + i */
105 	}
106 	J->text = js_intern(J, s);
107 	return TK_IDENTIFIER;
108 }
109 
jsY_iswhite(int c)110 int jsY_iswhite(int c)
111 {
112 	return c == 0x9 || c == 0xB || c == 0xC || c == 0x20 || c == 0xA0 || c == 0xFEFF;
113 }
114 
jsY_isnewline(int c)115 int jsY_isnewline(int c)
116 {
117 	return c == 0xA || c == 0xD || c == 0x2028 || c == 0x2029;
118 }
119 
120 #define isalpha(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
121 #define isdigit(c) (c >= '0' && c <= '9')
122 #define ishex(c) ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
123 
jsY_isidentifierstart(int c)124 static int jsY_isidentifierstart(int c)
125 {
126 	return isalpha(c) || c == '$' || c == '_' || isalpharune(c);
127 }
128 
jsY_isidentifierpart(int c)129 static int jsY_isidentifierpart(int c)
130 {
131 	return isdigit(c) || isalpha(c) || c == '$' || c == '_' || isalpharune(c);
132 }
133 
jsY_isdec(int c)134 static int jsY_isdec(int c)
135 {
136 	return isdigit(c);
137 }
138 
jsY_ishex(int c)139 int jsY_ishex(int c)
140 {
141 	return isdigit(c) || ishex(c);
142 }
143 
jsY_tohex(int c)144 int jsY_tohex(int c)
145 {
146 	if (c >= '0' && c <= '9') return c - '0';
147 	if (c >= 'a' && c <= 'f') return c - 'a' + 0xA;
148 	if (c >= 'A' && c <= 'F') return c - 'A' + 0xA;
149 	return 0;
150 }
151 
jsY_next(js_State * J)152 static void jsY_next(js_State *J)
153 {
154 	Rune c;
155 	J->source += chartorune(&c, J->source);
156 	/* consume CR LF as one unit */
157 	if (c == '\r' && *J->source == '\n')
158 		++J->source;
159 	if (jsY_isnewline(c)) {
160 		J->line++;
161 		c = '\n';
162 	}
163 	J->lexchar = c;
164 }
165 
166 #define jsY_accept(J, x) (J->lexchar == x ? (jsY_next(J), 1) : 0)
167 
168 #define jsY_expect(J, x) if (!jsY_accept(J, x)) jsY_error(J, "expected '%c'", x)
169 
jsY_unescape(js_State * J)170 static void jsY_unescape(js_State *J)
171 {
172 	if (jsY_accept(J, '\\')) {
173 		if (jsY_accept(J, 'u')) {
174 			int x = 0;
175 			if (!jsY_ishex(J->lexchar)) { goto error; } x |= jsY_tohex(J->lexchar) << 12; jsY_next(J);
176 			if (!jsY_ishex(J->lexchar)) { goto error; } x |= jsY_tohex(J->lexchar) << 8; jsY_next(J);
177 			if (!jsY_ishex(J->lexchar)) { goto error; } x |= jsY_tohex(J->lexchar) << 4; jsY_next(J);
178 			if (!jsY_ishex(J->lexchar)) { goto error; } x |= jsY_tohex(J->lexchar);
179 			J->lexchar = x;
180 			return;
181 		}
182 error:
183 		jsY_error(J, "unexpected escape sequence");
184 	}
185 }
186 
textinit(js_State * J)187 static void textinit(js_State *J)
188 {
189 	if (!J->lexbuf.text) {
190 		J->lexbuf.cap = 4096;
191 		J->lexbuf.text = js_malloc(J, J->lexbuf.cap);
192 	}
193 	J->lexbuf.len = 0;
194 }
195 
textpush(js_State * J,Rune c)196 static void textpush(js_State *J, Rune c)
197 {
198 	int n = runelen(c);
199 	if (J->lexbuf.len + n > J->lexbuf.cap) {
200 		J->lexbuf.cap = J->lexbuf.cap * 2;
201 		J->lexbuf.text = js_realloc(J, J->lexbuf.text, J->lexbuf.cap);
202 	}
203 	J->lexbuf.len += runetochar(J->lexbuf.text + J->lexbuf.len, &c);
204 }
205 
textend(js_State * J)206 static char *textend(js_State *J)
207 {
208 	textpush(J, 0);
209 	return J->lexbuf.text;
210 }
211 
lexlinecomment(js_State * J)212 static void lexlinecomment(js_State *J)
213 {
214 	while (J->lexchar && J->lexchar != '\n')
215 		jsY_next(J);
216 }
217 
lexcomment(js_State * J)218 static int lexcomment(js_State *J)
219 {
220 	/* already consumed initial '/' '*' sequence */
221 	while (J->lexchar != 0) {
222 		if (jsY_accept(J, '*')) {
223 			while (J->lexchar == '*')
224 				jsY_next(J);
225 			if (jsY_accept(J, '/'))
226 				return 0;
227 		}
228 		else
229 			jsY_next(J);
230 	}
231 	return -1;
232 }
233 
lexhex(js_State * J)234 static double lexhex(js_State *J)
235 {
236 	double n = 0;
237 	if (!jsY_ishex(J->lexchar))
238 		jsY_error(J, "malformed hexadecimal number");
239 	while (jsY_ishex(J->lexchar)) {
240 		n = n * 16 + jsY_tohex(J->lexchar);
241 		jsY_next(J);
242 	}
243 	return n;
244 }
245 
246 #if 0
247 
248 static double lexinteger(js_State *J)
249 {
250 	double n = 0;
251 	if (!jsY_isdec(J->lexchar))
252 		jsY_error(J, "malformed number");
253 	while (jsY_isdec(J->lexchar)) {
254 		n = n * 10 + (J->lexchar - '0');
255 		jsY_next(J);
256 	}
257 	return n;
258 }
259 
260 static double lexfraction(js_State *J)
261 {
262 	double n = 0;
263 	double d = 1;
264 	while (jsY_isdec(J->lexchar)) {
265 		n = n * 10 + (J->lexchar - '0');
266 		d = d * 10;
267 		jsY_next(J);
268 	}
269 	return n / d;
270 }
271 
272 static double lexexponent(js_State *J)
273 {
274 	double sign;
275 	if (jsY_accept(J, 'e') || jsY_accept(J, 'E')) {
276 		if (jsY_accept(J, '-')) sign = -1;
277 		else if (jsY_accept(J, '+')) sign = 1;
278 		else sign = 1;
279 		return sign * lexinteger(J);
280 	}
281 	return 0;
282 }
283 
284 static int lexnumber(js_State *J)
285 {
286 	double n;
287 	double e;
288 
289 	if (jsY_accept(J, '0')) {
290 		if (jsY_accept(J, 'x') || jsY_accept(J, 'X')) {
291 			J->number = lexhex(J);
292 			return TK_NUMBER;
293 		}
294 		if (jsY_isdec(J->lexchar))
295 			jsY_error(J, "number with leading zero");
296 		n = 0;
297 		if (jsY_accept(J, '.'))
298 			n += lexfraction(J);
299 	} else if (jsY_accept(J, '.')) {
300 		if (!jsY_isdec(J->lexchar))
301 			return '.';
302 		n = lexfraction(J);
303 	} else {
304 		n = lexinteger(J);
305 		if (jsY_accept(J, '.'))
306 			n += lexfraction(J);
307 	}
308 
309 	e = lexexponent(J);
310 	if (e < 0)
311 		n /= pow(10, -e);
312 	else if (e > 0)
313 		n *= pow(10, e);
314 
315 	if (jsY_isidentifierstart(J->lexchar))
316 		jsY_error(J, "number with letter suffix");
317 
318 	J->number = n;
319 	return TK_NUMBER;
320 }
321 
322 #else
323 
lexnumber(js_State * J)324 static int lexnumber(js_State *J)
325 {
326 	const char *s = J->source - 1;
327 
328 	if (jsY_accept(J, '0')) {
329 		if (jsY_accept(J, 'x') || jsY_accept(J, 'X')) {
330 			J->number = lexhex(J);
331 			return TK_NUMBER;
332 		}
333 		if (jsY_isdec(J->lexchar))
334 			jsY_error(J, "number with leading zero");
335 		if (jsY_accept(J, '.')) {
336 			while (jsY_isdec(J->lexchar))
337 				jsY_next(J);
338 		}
339 	} else if (jsY_accept(J, '.')) {
340 		if (!jsY_isdec(J->lexchar))
341 			return '.';
342 		while (jsY_isdec(J->lexchar))
343 			jsY_next(J);
344 	} else {
345 		while (jsY_isdec(J->lexchar))
346 			jsY_next(J);
347 		if (jsY_accept(J, '.')) {
348 			while (jsY_isdec(J->lexchar))
349 				jsY_next(J);
350 		}
351 	}
352 
353 	if (jsY_accept(J, 'e') || jsY_accept(J, 'E')) {
354 		if (J->lexchar == '-' || J->lexchar == '+')
355 			jsY_next(J);
356 		if (jsY_isdec(J->lexchar))
357 			while (jsY_isdec(J->lexchar))
358 				jsY_next(J);
359 		else
360 			jsY_error(J, "missing exponent");
361 	}
362 
363 	if (jsY_isidentifierstart(J->lexchar))
364 		jsY_error(J, "number with letter suffix");
365 
366 	J->number = js_strtod(s, NULL);
367 	return TK_NUMBER;
368 }
369 
370 #endif
371 
lexescape(js_State * J)372 static int lexescape(js_State *J)
373 {
374 	int x = 0;
375 
376 	/* already consumed '\' */
377 
378 	if (jsY_accept(J, '\n'))
379 		return 0;
380 
381 	switch (J->lexchar) {
382 	case 0: jsY_error(J, "unterminated escape sequence");
383 	case 'u':
384 		jsY_next(J);
385 		if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar) << 12; jsY_next(J); }
386 		if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar) << 8; jsY_next(J); }
387 		if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar) << 4; jsY_next(J); }
388 		if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar); jsY_next(J); }
389 		textpush(J, x);
390 		break;
391 	case 'x':
392 		jsY_next(J);
393 		if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar) << 4; jsY_next(J); }
394 		if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar); jsY_next(J); }
395 		textpush(J, x);
396 		break;
397 	case '0': textpush(J, 0); jsY_next(J); break;
398 	case '\\': textpush(J, '\\'); jsY_next(J); break;
399 	case '\'': textpush(J, '\''); jsY_next(J); break;
400 	case '"': textpush(J, '"'); jsY_next(J); break;
401 	case 'b': textpush(J, '\b'); jsY_next(J); break;
402 	case 'f': textpush(J, '\f'); jsY_next(J); break;
403 	case 'n': textpush(J, '\n'); jsY_next(J); break;
404 	case 'r': textpush(J, '\r'); jsY_next(J); break;
405 	case 't': textpush(J, '\t'); jsY_next(J); break;
406 	case 'v': textpush(J, '\v'); jsY_next(J); break;
407 	default: textpush(J, J->lexchar); jsY_next(J); break;
408 	}
409 	return 0;
410 }
411 
lexstring(js_State * J)412 static int lexstring(js_State *J)
413 {
414 	const char *s;
415 
416 	int q = J->lexchar;
417 	jsY_next(J);
418 
419 	textinit(J);
420 
421 	while (J->lexchar != q) {
422 		if (J->lexchar == 0 || J->lexchar == '\n')
423 			jsY_error(J, "string not terminated");
424 		if (jsY_accept(J, '\\')) {
425 			if (lexescape(J))
426 				jsY_error(J, "malformed escape sequence");
427 		} else {
428 			textpush(J, J->lexchar);
429 			jsY_next(J);
430 		}
431 	}
432 	jsY_expect(J, q);
433 
434 	s = textend(J);
435 
436 	J->text = js_intern(J, s);
437 	return TK_STRING;
438 }
439 
440 /* the ugliest language wart ever... */
isregexpcontext(int last)441 static int isregexpcontext(int last)
442 {
443 	switch (last) {
444 	case ']':
445 	case ')':
446 	case '}':
447 	case TK_IDENTIFIER:
448 	case TK_NUMBER:
449 	case TK_STRING:
450 	case TK_FALSE:
451 	case TK_NULL:
452 	case TK_THIS:
453 	case TK_TRUE:
454 		return 0;
455 	default:
456 		return 1;
457 	}
458 }
459 
lexregexp(js_State * J)460 static int lexregexp(js_State *J)
461 {
462 	const char *s;
463 	int g, m, i;
464 	int inclass = 0;
465 
466 	/* already consumed initial '/' */
467 
468 	textinit(J);
469 
470 	/* regexp body */
471 	while (J->lexchar != '/' || inclass) {
472 		if (J->lexchar == 0 || J->lexchar == '\n') {
473 			jsY_error(J, "regular expression not terminated");
474 		} else if (jsY_accept(J, '\\')) {
475 			if (jsY_accept(J, '/')) {
476 				textpush(J, '/');
477 			} else {
478 				textpush(J, '\\');
479 				if (J->lexchar == 0 || J->lexchar == '\n')
480 					jsY_error(J, "regular expression not terminated");
481 				textpush(J, J->lexchar);
482 				jsY_next(J);
483 			}
484 		} else {
485 			if (J->lexchar == '[' && !inclass)
486 				inclass = 1;
487 			if (J->lexchar == ']' && inclass)
488 				inclass = 0;
489 			textpush(J, J->lexchar);
490 			jsY_next(J);
491 		}
492 	}
493 	jsY_expect(J, '/');
494 
495 	s = textend(J);
496 
497 	/* regexp flags */
498 	g = i = m = 0;
499 
500 	while (jsY_isidentifierpart(J->lexchar)) {
501 		if (jsY_accept(J, 'g')) ++g;
502 		else if (jsY_accept(J, 'i')) ++i;
503 		else if (jsY_accept(J, 'm')) ++m;
504 		else jsY_error(J, "illegal flag in regular expression: %c", J->lexchar);
505 	}
506 
507 	if (g > 1 || i > 1 || m > 1)
508 		jsY_error(J, "duplicated flag in regular expression");
509 
510 	J->text = js_intern(J, s);
511 	J->number = 0;
512 	if (g) J->number += JS_REGEXP_G;
513 	if (i) J->number += JS_REGEXP_I;
514 	if (m) J->number += JS_REGEXP_M;
515 	return TK_REGEXP;
516 }
517 
518 /* simple "return [no Line Terminator here] ..." contexts */
isnlthcontext(int last)519 static int isnlthcontext(int last)
520 {
521 	switch (last) {
522 	case TK_BREAK:
523 	case TK_CONTINUE:
524 	case TK_RETURN:
525 	case TK_THROW:
526 		return 1;
527 	default:
528 		return 0;
529 	}
530 }
531 
jsY_lexx(js_State * J)532 static int jsY_lexx(js_State *J)
533 {
534 	J->newline = 0;
535 
536 	while (1) {
537 		J->lexline = J->line; /* save location of beginning of token */
538 
539 		while (jsY_iswhite(J->lexchar))
540 			jsY_next(J);
541 
542 		if (jsY_accept(J, '\n')) {
543 			J->newline = 1;
544 			if (isnlthcontext(J->lasttoken))
545 				return ';';
546 			continue;
547 		}
548 
549 		if (jsY_accept(J, '/')) {
550 			if (jsY_accept(J, '/')) {
551 				lexlinecomment(J);
552 				continue;
553 			} else if (jsY_accept(J, '*')) {
554 				if (lexcomment(J))
555 					jsY_error(J, "multi-line comment not terminated");
556 				continue;
557 			} else if (isregexpcontext(J->lasttoken)) {
558 				return lexregexp(J);
559 			} else if (jsY_accept(J, '=')) {
560 				return TK_DIV_ASS;
561 			} else {
562 				return '/';
563 			}
564 		}
565 
566 		if (J->lexchar >= '0' && J->lexchar <= '9') {
567 			return lexnumber(J);
568 		}
569 
570 		switch (J->lexchar) {
571 		case '(': jsY_next(J); return '(';
572 		case ')': jsY_next(J); return ')';
573 		case ',': jsY_next(J); return ',';
574 		case ':': jsY_next(J); return ':';
575 		case ';': jsY_next(J); return ';';
576 		case '?': jsY_next(J); return '?';
577 		case '[': jsY_next(J); return '[';
578 		case ']': jsY_next(J); return ']';
579 		case '{': jsY_next(J); return '{';
580 		case '}': jsY_next(J); return '}';
581 		case '~': jsY_next(J); return '~';
582 
583 		case '\'':
584 		case '"':
585 			return lexstring(J);
586 
587 		case '.':
588 			return lexnumber(J);
589 
590 		case '<':
591 			jsY_next(J);
592 			if (jsY_accept(J, '<')) {
593 				if (jsY_accept(J, '='))
594 					return TK_SHL_ASS;
595 				return TK_SHL;
596 			}
597 			if (jsY_accept(J, '='))
598 				return TK_LE;
599 			return '<';
600 
601 		case '>':
602 			jsY_next(J);
603 			if (jsY_accept(J, '>')) {
604 				if (jsY_accept(J, '>')) {
605 					if (jsY_accept(J, '='))
606 						return TK_USHR_ASS;
607 					return TK_USHR;
608 				}
609 				if (jsY_accept(J, '='))
610 					return TK_SHR_ASS;
611 				return TK_SHR;
612 			}
613 			if (jsY_accept(J, '='))
614 				return TK_GE;
615 			return '>';
616 
617 		case '=':
618 			jsY_next(J);
619 			if (jsY_accept(J, '=')) {
620 				if (jsY_accept(J, '='))
621 					return TK_STRICTEQ;
622 				return TK_EQ;
623 			}
624 			return '=';
625 
626 		case '!':
627 			jsY_next(J);
628 			if (jsY_accept(J, '=')) {
629 				if (jsY_accept(J, '='))
630 					return TK_STRICTNE;
631 				return TK_NE;
632 			}
633 			return '!';
634 
635 		case '+':
636 			jsY_next(J);
637 			if (jsY_accept(J, '+'))
638 				return TK_INC;
639 			if (jsY_accept(J, '='))
640 				return TK_ADD_ASS;
641 			return '+';
642 
643 		case '-':
644 			jsY_next(J);
645 			if (jsY_accept(J, '-'))
646 				return TK_DEC;
647 			if (jsY_accept(J, '='))
648 				return TK_SUB_ASS;
649 			return '-';
650 
651 		case '*':
652 			jsY_next(J);
653 			if (jsY_accept(J, '='))
654 				return TK_MUL_ASS;
655 			return '*';
656 
657 		case '%':
658 			jsY_next(J);
659 			if (jsY_accept(J, '='))
660 				return TK_MOD_ASS;
661 			return '%';
662 
663 		case '&':
664 			jsY_next(J);
665 			if (jsY_accept(J, '&'))
666 				return TK_AND;
667 			if (jsY_accept(J, '='))
668 				return TK_AND_ASS;
669 			return '&';
670 
671 		case '|':
672 			jsY_next(J);
673 			if (jsY_accept(J, '|'))
674 				return TK_OR;
675 			if (jsY_accept(J, '='))
676 				return TK_OR_ASS;
677 			return '|';
678 
679 		case '^':
680 			jsY_next(J);
681 			if (jsY_accept(J, '='))
682 				return TK_XOR_ASS;
683 			return '^';
684 
685 		case 0:
686 			return 0; /* EOF */
687 		}
688 
689 		/* Handle \uXXXX escapes in identifiers */
690 		jsY_unescape(J);
691 		if (jsY_isidentifierstart(J->lexchar)) {
692 			textinit(J);
693 			textpush(J, J->lexchar);
694 
695 			jsY_next(J);
696 			jsY_unescape(J);
697 			while (jsY_isidentifierpart(J->lexchar)) {
698 				textpush(J, J->lexchar);
699 				jsY_next(J);
700 				jsY_unescape(J);
701 			}
702 
703 			textend(J);
704 
705 			return jsY_findkeyword(J, J->lexbuf.text);
706 		}
707 
708 		if (J->lexchar >= 0x20 && J->lexchar <= 0x7E)
709 			jsY_error(J, "unexpected character: '%c'", J->lexchar);
710 		jsY_error(J, "unexpected character: \\u%04X", J->lexchar);
711 	}
712 }
713 
jsY_initlex(js_State * J,const char * filename,const char * source)714 void jsY_initlex(js_State *J, const char *filename, const char *source)
715 {
716 	J->filename = filename;
717 	J->source = source;
718 	J->line = 1;
719 	J->lasttoken = 0;
720 	jsY_next(J); /* load first lookahead character */
721 }
722 
jsY_lex(js_State * J)723 int jsY_lex(js_State *J)
724 {
725 	return J->lasttoken = jsY_lexx(J);
726 }
727 
lexjsonnumber(js_State * J)728 static int lexjsonnumber(js_State *J)
729 {
730 	const char *s = J->source - 1;
731 
732 	if (J->lexchar == '-')
733 		jsY_next(J);
734 
735 	if (J->lexchar == '0')
736 		jsY_next(J);
737 	else if (J->lexchar >= '1' && J->lexchar <= '9')
738 		while (isdigit(J->lexchar))
739 			jsY_next(J);
740 	else
741 		jsY_error(J, "unexpected non-digit");
742 	if (jsY_accept(J, '.'))
743 	{
744 		if (isdigit(J->lexchar))
745 			while (isdigit(J->lexchar))
746 				jsY_next(J);
747 		else
748 			jsY_error(J, "missing digits after decimal point");
749 	}
750 
751 	if (jsY_accept(J, 'e') || jsY_accept(J, 'E')) {
752 		if (J->lexchar == '-' || J->lexchar == '+')
753 			jsY_next(J);
754 		if (isdigit(J->lexchar))
755 			while (isdigit(J->lexchar))
756 				jsY_next(J);
757 		else
758 			jsY_error(J, "missing digits after exponent indicator");
759 	}
760 
761 	J->number = js_strtod(s, NULL);
762 	return TK_NUMBER;
763 }
764 
lexjsonescape(js_State * J)765 static int lexjsonescape(js_State *J)
766 {
767 	int x = 0;
768 
769 	/* already consumed '\' */
770 
771 	switch (J->lexchar) {
772 	default: jsY_error(J, "invalid escape sequence");
773 	case 'u':
774 		jsY_next(J);
775 		if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar) << 12; jsY_next(J); }
776 		if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar) << 8; jsY_next(J); }
777 		if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar) << 4; jsY_next(J); }
778 		if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar); jsY_next(J); }
779 		textpush(J, x);
780 		break;
781 	case '"': textpush(J, '"'); jsY_next(J); break;
782 	case '\\': textpush(J, '\\'); jsY_next(J); break;
783 	case 'b': textpush(J, '\b'); jsY_next(J); break;
784 	case 'f': textpush(J, '\f'); jsY_next(J); break;
785 	case 'n': textpush(J, '\n'); jsY_next(J); break;
786 	case 'r': textpush(J, '\r'); jsY_next(J); break;
787 	case 't': textpush(J, '\t'); jsY_next(J); break;
788 	}
789 	return 0;
790 }
791 
lexjsonstring(js_State * J)792 static int lexjsonstring(js_State *J)
793 {
794 	const char *s;
795 
796 	textinit(J);
797 
798 	while (J->lexchar != '"') {
799 		if (J->lexchar == 0)
800 			jsY_error(J, "unterminated string");
801 		else if (J->lexchar < 32)
802 			jsY_error(J, "invalid control character in string");
803 		else if (jsY_accept(J, '\\'))
804 			lexjsonescape(J);
805 		else {
806 			textpush(J, J->lexchar);
807 			jsY_next(J);
808 		}
809 	}
810 	jsY_expect(J, '"');
811 
812 	s = textend(J);
813 
814 	J->text = js_intern(J, s);
815 	return TK_STRING;
816 }
817 
jsY_lexjson(js_State * J)818 int jsY_lexjson(js_State *J)
819 {
820 	while (1) {
821 		J->lexline = J->line; /* save location of beginning of token */
822 
823 		while (jsY_iswhite(J->lexchar) || J->lexchar == '\n')
824 			jsY_next(J);
825 
826 		if ((J->lexchar >= '0' && J->lexchar <= '9') || J->lexchar == '-')
827 			return lexjsonnumber(J);
828 
829 		switch (J->lexchar) {
830 		case ',': jsY_next(J); return ',';
831 		case ':': jsY_next(J); return ':';
832 		case '[': jsY_next(J); return '[';
833 		case ']': jsY_next(J); return ']';
834 		case '{': jsY_next(J); return '{';
835 		case '}': jsY_next(J); return '}';
836 
837 		case '"':
838 			jsY_next(J);
839 			return lexjsonstring(J);
840 
841 		case 'f':
842 			jsY_next(J); jsY_expect(J, 'a'); jsY_expect(J, 'l'); jsY_expect(J, 's'); jsY_expect(J, 'e');
843 			return TK_FALSE;
844 
845 		case 'n':
846 			jsY_next(J); jsY_expect(J, 'u'); jsY_expect(J, 'l'); jsY_expect(J, 'l');
847 			return TK_NULL;
848 
849 		case 't':
850 			jsY_next(J); jsY_expect(J, 'r'); jsY_expect(J, 'u'); jsY_expect(J, 'e');
851 			return TK_TRUE;
852 
853 		case 0:
854 			return 0; /* EOF */
855 		}
856 
857 		if (J->lexchar >= 0x20 && J->lexchar <= 0x7E)
858 			jsY_error(J, "unexpected character: '%c'", J->lexchar);
859 		jsY_error(J, "unexpected character: \\u%04X", J->lexchar);
860 	}
861 }
862