UNPKG

25.5 kBJavaScriptView Raw
1let source, pos, end,
2 openTokenDepth,
3 lastTokenPos,
4 openTokenPosStack,
5 openClassPosStack,
6 curDynamicImport,
7 templateStackDepth,
8 facade,
9 lastSlashWasDivision,
10 nextBraceIsClass,
11 templateDepth,
12 templateStack,
13 imports,
14 exports,
15 name;
16
17function addImport (ss, s, e, d) {
18 const impt = { ss, se: d === -2 ? e : d === -1 ? e + 1 : 0, s, e, d, a: -1, n: undefined };
19 imports.push(impt);
20 return impt;
21}
22
23function readName (impt) {
24 let { d, s } = impt;
25 if (d !== -1)
26 s++;
27 impt.n = readString(s, source.charCodeAt(s - 1));
28}
29
30// Note: parsing is based on the _assumption_ that the source is already valid
31export function parse (_source, _name) {
32 openTokenDepth = 0;
33 curDynamicImport = null;
34 templateDepth = -1;
35 lastTokenPos = -1;
36 lastSlashWasDivision = false;
37 templateStack = Array(1024);
38 templateStackDepth = 0;
39 openTokenPosStack = Array(1024);
40 openClassPosStack = Array(1024);
41 nextBraceIsClass = false;
42 facade = true;
43 name = _name || '@';
44
45 imports = [];
46 exports = new Set();
47
48 source = _source;
49 pos = -1;
50 end = source.length - 1;
51 let ch = 0;
52
53 // start with a pure "module-only" parser
54 m: while (pos++ < end) {
55 ch = source.charCodeAt(pos);
56
57 if (ch === 32 || ch < 14 && ch > 8)
58 continue;
59
60 switch (ch) {
61 case 101/*e*/:
62 if (openTokenDepth === 0 && keywordStart(pos) && source.startsWith('xport', pos + 1)) {
63 tryParseExportStatement();
64 // export might have been a non-pure declaration
65 if (!facade) {
66 lastTokenPos = pos;
67 break m;
68 }
69 }
70 break;
71 case 105/*i*/:
72 if (keywordStart(pos) && source.startsWith('mport', pos + 1))
73 tryParseImportStatement();
74 break;
75 case 59/*;*/:
76 break;
77 case 47/*/*/: {
78 const next_ch = source.charCodeAt(pos + 1);
79 if (next_ch === 47/*/*/) {
80 lineComment();
81 // dont update lastToken
82 continue;
83 }
84 else if (next_ch === 42/***/) {
85 blockComment(true);
86 // dont update lastToken
87 continue;
88 }
89 // fallthrough
90 }
91 default:
92 // as soon as we hit a non-module token, we go to main parser
93 facade = false;
94 pos--;
95 break m;
96 }
97 lastTokenPos = pos;
98 }
99
100 while (pos++ < end) {
101 ch = source.charCodeAt(pos);
102
103 if (ch === 32 || ch < 14 && ch > 8)
104 continue;
105
106 switch (ch) {
107 case 101/*e*/:
108 if (openTokenDepth === 0 && keywordStart(pos) && source.startsWith('xport', pos + 1))
109 tryParseExportStatement();
110 break;
111 case 105/*i*/:
112 if (keywordStart(pos) && source.startsWith('mport', pos + 1))
113 tryParseImportStatement();
114 break;
115 case 99/*c*/:
116 if (keywordStart(pos) && source.startsWith('lass', pos + 1) && isBrOrWs(source.charCodeAt(pos + 5)))
117 nextBraceIsClass = true;
118 break;
119 case 40/*(*/:
120 openTokenPosStack[openTokenDepth++] = lastTokenPos;
121 break;
122 case 41/*)*/:
123 if (openTokenDepth === 0)
124 syntaxError();
125 openTokenDepth--;
126 if (curDynamicImport && curDynamicImport.d === openTokenPosStack[openTokenDepth]) {
127 if (curDynamicImport.e === 0)
128 curDynamicImport.e = pos;
129 curDynamicImport.se = pos;
130 curDynamicImport = null;
131 }
132 break;
133 case 123/*{*/:
134 // dynamic import followed by { is not a dynamic import (so remove)
135 // this is a sneaky way to get around { import () {} } v { import () }
136 // block / object ambiguity without a parser (assuming source is valid)
137 if (source.charCodeAt(lastTokenPos) === 41/*)*/ && imports.length && imports[imports.length - 1].e === lastTokenPos) {
138 imports.pop();
139 }
140 openClassPosStack[openTokenDepth] = nextBraceIsClass;
141 nextBraceIsClass = false;
142 openTokenPosStack[openTokenDepth++] = lastTokenPos;
143 break;
144 case 125/*}*/:
145 if (openTokenDepth === 0)
146 syntaxError();
147 if (openTokenDepth-- === templateDepth) {
148 templateDepth = templateStack[--templateStackDepth];
149 templateString();
150 }
151 else {
152 if (templateDepth !== -1 && openTokenDepth < templateDepth)
153 syntaxError();
154 }
155 break;
156 case 39/*'*/:
157 case 34/*"*/:
158 stringLiteral(ch);
159 break;
160 case 47/*/*/: {
161 const next_ch = source.charCodeAt(pos + 1);
162 if (next_ch === 47/*/*/) {
163 lineComment();
164 // dont update lastToken
165 continue;
166 }
167 else if (next_ch === 42/***/) {
168 blockComment(true);
169 // dont update lastToken
170 continue;
171 }
172 else {
173 // Division / regex ambiguity handling based on checking backtrack analysis of:
174 // - what token came previously (lastToken)
175 // - if a closing brace or paren, what token came before the corresponding
176 // opening brace or paren (lastOpenTokenIndex)
177 const lastToken = source.charCodeAt(lastTokenPos);
178 if (isExpressionPunctuator(lastToken) &&
179 !(lastToken === 46/*.*/ && (source.charCodeAt(lastTokenPos - 1) >= 48/*0*/ && source.charCodeAt(lastTokenPos - 1) <= 57/*9*/)) &&
180 !(lastToken === 43/*+*/ && source.charCodeAt(lastTokenPos - 1) === 43/*+*/) && !(lastToken === 45/*-*/ && source.charCodeAt(lastTokenPos - 1) === 45/*-*/) ||
181 lastToken === 41/*)*/ && isParenKeyword(openTokenPosStack[openTokenDepth]) ||
182 lastToken === 125/*}*/ && (isExpressionTerminator(openTokenPosStack[openTokenDepth]) || openClassPosStack[openTokenDepth]) ||
183 lastToken === 47/*/*/ && lastSlashWasDivision ||
184 isExpressionKeyword(lastTokenPos) ||
185 !lastToken) {
186 regularExpression();
187 lastSlashWasDivision = false;
188 }
189 else {
190 lastSlashWasDivision = true;
191 }
192 }
193 break;
194 }
195 case 96/*`*/:
196 templateString();
197 break;
198 }
199 lastTokenPos = pos;
200 }
201
202 if (templateDepth !== -1 || openTokenDepth)
203 syntaxError();
204
205 return [imports, [...exports], facade];
206}
207
208function tryParseImportStatement () {
209 const startPos = pos;
210
211 pos += 6;
212
213 let ch = commentWhitespace(true);
214
215 switch (ch) {
216 // dynamic import
217 case 40/*(*/:
218 openTokenPosStack[openTokenDepth++] = startPos;
219 if (source.charCodeAt(lastTokenPos) === 46/*.*/)
220 return;
221 // dynamic import indicated by positive d
222 const impt = addImport(startPos, pos + 1, 0, startPos);
223 curDynamicImport = impt;
224 // try parse a string, to record a safe dynamic import string
225 pos++;
226 ch = commentWhitespace(true);
227 if (ch === 39/*'*/ || ch === 34/*"*/) {
228 stringLiteral(ch);
229 }
230 else {
231 pos--;
232 return;
233 }
234 pos++;
235 ch = commentWhitespace(true);
236 if (ch === 44/*,*/) {
237 impt.e = pos;
238 pos++;
239 ch = commentWhitespace(true);
240 impt.a = pos;
241 readName(impt);
242 pos--;
243 }
244 else if (ch === 41/*)*/) {
245 openTokenDepth--;
246 impt.e = pos;
247 impt.se = pos;
248 readName(impt);
249 }
250 else {
251 pos--;
252 }
253 return;
254 // import.meta
255 case 46/*.*/:
256 pos++;
257 ch = commentWhitespace(true);
258 // import.meta indicated by d === -2
259 if (ch === 109/*m*/ && source.startsWith('eta', pos + 1) && source.charCodeAt(lastTokenPos) !== 46/*.*/)
260 addImport(startPos, startPos, pos + 4, -2);
261 return;
262
263 default:
264 // no space after "import" -> not an import keyword
265 if (pos === startPos + 6)
266 break;
267 case 34/*"*/:
268 case 39/*'*/:
269 case 123/*{*/:
270 case 42/***/:
271 // import statement only permitted at base-level
272 if (openTokenDepth !== 0) {
273 pos--;
274 return;
275 }
276 while (pos < end) {
277 ch = source.charCodeAt(pos);
278 if (ch === 39/*'*/ || ch === 34/*"*/) {
279 readImportString(startPos, ch);
280 return;
281 }
282 pos++;
283 }
284 syntaxError();
285 }
286}
287
288function tryParseExportStatement () {
289 const sStartPos = pos;
290
291 pos += 6;
292
293 const curPos = pos;
294
295 let ch = commentWhitespace(true);
296
297 if (pos === curPos && !isPunctuator(ch))
298 return;
299
300 switch (ch) {
301 // export default ...
302 case 100/*d*/:
303 exports.add(source.slice(pos, pos + 7));
304 return;
305
306 // export async? function*? name () {
307 case 97/*a*/:
308 pos += 5;
309 commentWhitespace(true);
310 // fallthrough
311 case 102/*f*/:
312 pos += 8;
313 ch = commentWhitespace(true);
314 if (ch === 42/***/) {
315 pos++;
316 ch = commentWhitespace(true);
317 }
318 const startPos = pos;
319 ch = readToWsOrPunctuator(ch);
320 exports.add(source.slice(startPos, pos));
321 pos--;
322 return;
323
324 case 99/*c*/:
325 if (source.startsWith('lass', pos + 1) && isBrOrWsOrPunctuatorNotDot(source.charCodeAt(pos + 5))) {
326 pos += 5;
327 ch = commentWhitespace(true);
328 const startPos = pos;
329 ch = readToWsOrPunctuator(ch);
330 exports.add(source.slice(startPos, pos));
331 pos--;
332 return;
333 }
334 pos += 2;
335 // fallthrough
336
337 // export var/let/const name = ...(, name = ...)+
338 case 118/*v*/:
339 case 109/*l*/:
340 // destructured initializations not currently supported (skipped for { or [)
341 // also, lexing names after variable equals is skipped (export var p = function () { ... }, q = 5 skips "q")
342 pos += 2;
343 facade = false;
344 do {
345 pos++;
346 ch = commentWhitespace(true);
347 const startPos = pos;
348 ch = readToWsOrPunctuator(ch);
349 // dont yet handle [ { destructurings
350 if (ch === 123/*{*/ || ch === 91/*[*/) {
351 pos--;
352 return;
353 }
354 if (pos === startPos)
355 return;
356 exports.add(source.slice(startPos, pos));
357 ch = commentWhitespace(true);
358 if (ch === 61/*=*/) {
359 pos--;
360 return;
361 }
362 } while (ch === 44/*,*/);
363 pos--;
364 return;
365
366
367 // export {...}
368 case 123/*{*/:
369 pos++;
370 ch = commentWhitespace(true);
371 while (true) {
372 const startPos = pos;
373 readToWsOrPunctuator(ch);
374 const endPos = pos;
375 commentWhitespace(true);
376 ch = readExportAs(startPos, endPos);
377 // ,
378 if (ch === 44/*,*/) {
379 pos++;
380 ch = commentWhitespace(true);
381 }
382 if (ch === 125/*}*/)
383 break;
384 if (pos === startPos)
385 return syntaxError();
386 if (pos > end)
387 return syntaxError();
388 }
389 pos++;
390 ch = commentWhitespace(true);
391 break;
392
393 // export *
394 // export * as X
395 case 42/***/:
396 pos++;
397 commentWhitespace(true);
398 ch = readExportAs(pos, pos);
399 ch = commentWhitespace(true);
400 break;
401 }
402
403 // from ...
404 if (ch === 102/*f*/ && source.startsWith('rom', pos + 1)) {
405 pos += 4;
406 readImportString(sStartPos, commentWhitespace(true));
407 }
408 else {
409 pos--;
410 }
411}
412
413/*
414 * Ported from Acorn
415 *
416 * MIT License
417
418 * Copyright (C) 2012-2020 by various contributors (see AUTHORS)
419
420 * Permission is hereby granted, free of charge, to any person obtaining a copy
421 * of this software and associated documentation files (the "Software"), to deal
422 * in the Software without restriction, including without limitation the rights
423 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
424 * copies of the Software, and to permit persons to whom the Software is
425 * furnished to do so, subject to the following conditions:
426
427 * The above copyright notice and this permission notice shall be included in
428 * all copies or substantial portions of the Software.
429
430 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
431 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
432 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
433 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
434 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
435 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
436 * THE SOFTWARE.
437 */
438let acornPos;
439function readString (start, quote) {
440 acornPos = start;
441 let out = '', chunkStart = acornPos;
442 for (;;) {
443 if (acornPos >= source.length) syntaxError();
444 const ch = source.charCodeAt(acornPos);
445 if (ch === quote) break;
446 if (ch === 92) { // '\'
447 out += source.slice(chunkStart, acornPos);
448 out += readEscapedChar();
449 chunkStart = acornPos;
450 }
451 else if (ch === 0x2028 || ch === 0x2029) {
452 ++acornPos;
453 }
454 else {
455 if (isBr(ch)) syntaxError();
456 ++acornPos;
457 }
458 }
459 out += source.slice(chunkStart, acornPos++);
460 return out;
461}
462
463// Used to read escaped characters
464
465function readEscapedChar () {
466 let ch = source.charCodeAt(++acornPos);
467 ++acornPos;
468 switch (ch) {
469 case 110: return '\n'; // 'n' -> '\n'
470 case 114: return '\r'; // 'r' -> '\r'
471 case 120: return String.fromCharCode(readHexChar(2)); // 'x'
472 case 117: return readCodePointToString(); // 'u'
473 case 116: return '\t'; // 't' -> '\t'
474 case 98: return '\b'; // 'b' -> '\b'
475 case 118: return '\u000b'; // 'v' -> '\u000b'
476 case 102: return '\f'; // 'f' -> '\f'
477 case 13: if (source.charCodeAt(acornPos) === 10) ++acornPos; // '\r\n'
478 case 10: // ' \n'
479 return '';
480 case 56:
481 case 57:
482 syntaxError();
483 default:
484 if (ch >= 48 && ch <= 55) {
485 let octalStr = source.substr(acornPos - 1, 3).match(/^[0-7]+/)[0];
486 let octal = parseInt(octalStr, 8);
487 if (octal > 255) {
488 octalStr = octalStr.slice(0, -1);
489 octal = parseInt(octalStr, 8);
490 }
491 acornPos += octalStr.length - 1;
492 ch = source.charCodeAt(acornPos);
493 if (octalStr !== '0' || ch === 56 || ch === 57)
494 syntaxError();
495 return String.fromCharCode(octal);
496 }
497 if (isBr(ch)) {
498 // Unicode new line characters after \ get removed from output in both
499 // template literals and strings
500 return '';
501 }
502 return String.fromCharCode(ch);
503 }
504}
505
506// Used to read character escape sequences ('\x', '\u', '\U').
507
508function readHexChar (len) {
509 const start = acornPos;
510 let total = 0, lastCode = 0;
511 for (let i = 0; i < len; ++i, ++acornPos) {
512 let code = source.charCodeAt(acornPos), val;
513
514 if (code === 95) {
515 if (lastCode === 95 || i === 0) syntaxError();
516 lastCode = code;
517 continue;
518 }
519
520 if (code >= 97) val = code - 97 + 10; // a
521 else if (code >= 65) val = code - 65 + 10; // A
522 else if (code >= 48 && code <= 57) val = code - 48; // 0-9
523 else break;
524 if (val >= 16) break;
525 lastCode = code;
526 total = total * 16 + val;
527 }
528
529 if (lastCode === 95 || acornPos - start !== len) syntaxError();
530
531 return total;
532}
533
534// Read a string value, interpreting backslash-escapes.
535
536function readCodePointToString () {
537 const ch = source.charCodeAt(acornPos);
538 let code;
539 if (ch === 123) { // '{'
540 ++acornPos;
541 code = readHexChar(source.indexOf('}', acornPos) - acornPos);
542 ++acornPos;
543 if (code > 0x10FFFF) syntaxError();
544 } else {
545 code = readHexChar(4);
546 }
547 // UTF-16 Decoding
548 if (code <= 0xFFFF) return String.fromCharCode(code);
549 code -= 0x10000;
550 return String.fromCharCode((code >> 10) + 0xD800, (code & 1023) + 0xDC00);
551}
552
553/*
554 * </ Acorn Port>
555 */
556
557function readExportAs (startPos, endPos) {
558 let ch = source.charCodeAt(pos);
559 if (ch === 97 /*a*/) {
560 pos += 2;
561 ch = commentWhitespace(true);
562 startPos = pos;
563 readToWsOrPunctuator(ch);
564 endPos = pos;
565 ch = commentWhitespace(true);
566 }
567 if (pos !== startPos)
568 exports.add(source.slice(startPos, endPos));
569 return ch;
570}
571
572function readImportString (ss, ch) {
573 const startPos = pos + 1;
574 if (ch === 39/*'*/ || ch === 34/*"*/) {
575 stringLiteral(ch);
576 }
577 else {
578 syntaxError();
579 return;
580 }
581 const impt = addImport(ss, startPos, pos, -1);
582 readName(impt);
583 pos++;
584 ch = commentWhitespace(false);
585 if (ch !== 97/*a*/ || !source.startsWith('ssert', pos + 1)) {
586 pos--;
587 return;
588 }
589 const assertIndex = pos;
590
591 pos += 6;
592 ch = commentWhitespace(true);
593 if (ch !== 123/*{*/) {
594 pos = assertIndex;
595 return;
596 }
597 const assertStart = pos;
598 do {
599 pos++;
600 ch = commentWhitespace(true);
601 if (ch === 39/*'*/ || ch === 34/*"*/) {
602 stringLiteral(ch);
603 pos++;
604 ch = commentWhitespace(true);
605 }
606 else {
607 ch = readToWsOrPunctuator(ch);
608 }
609 if (ch !== 58/*:*/) {
610 pos = assertIndex;
611 return;
612 }
613 pos++;
614 ch = commentWhitespace(true);
615 if (ch === 39/*'*/ || ch === 34/*"*/) {
616 stringLiteral(ch);
617 }
618 else {
619 pos = assertIndex;
620 return;
621 }
622 pos++;
623 ch = commentWhitespace(true);
624 if (ch === 44/*,*/) {
625 pos++;
626 ch = commentWhitespace(true);
627 if (ch === 125/*}*/)
628 break;
629 continue;
630 }
631 if (ch === 125/*}*/)
632 break;
633 pos = assertIndex;
634 return;
635 } while (true);
636 impt.a = assertStart;
637 impt.se = pos + 1;
638}
639
640function commentWhitespace (br) {
641 let ch;
642 do {
643 ch = source.charCodeAt(pos);
644 if (ch === 47/*/*/) {
645 const next_ch = source.charCodeAt(pos + 1);
646 if (next_ch === 47/*/*/)
647 lineComment();
648 else if (next_ch === 42/***/)
649 blockComment(br);
650 else
651 return ch;
652 }
653 else if (br ? !isBrOrWs(ch): !isWsNotBr(ch)) {
654 return ch;
655 }
656 } while (pos++ < end);
657 return ch;
658}
659
660function templateString () {
661 while (pos++ < end) {
662 const ch = source.charCodeAt(pos);
663 if (ch === 36/*$*/ && source.charCodeAt(pos + 1) === 123/*{*/) {
664 pos++;
665 templateStack[templateStackDepth++] = templateDepth;
666 templateDepth = ++openTokenDepth;
667 return;
668 }
669 if (ch === 96/*`*/)
670 return;
671 if (ch === 92/*\*/)
672 pos++;
673 }
674 syntaxError();
675}
676
677function blockComment (br) {
678 pos++;
679 while (pos++ < end) {
680 const ch = source.charCodeAt(pos);
681 if (!br && isBr(ch))
682 return;
683 if (ch === 42/***/ && source.charCodeAt(pos + 1) === 47/*/*/) {
684 pos++;
685 return;
686 }
687 }
688}
689
690function lineComment () {
691 while (pos++ < end) {
692 const ch = source.charCodeAt(pos);
693 if (ch === 10/*\n*/ || ch === 13/*\r*/)
694 return;
695 }
696}
697
698function stringLiteral (quote) {
699 while (pos++ < end) {
700 let ch = source.charCodeAt(pos);
701 if (ch === quote)
702 return;
703 if (ch === 92/*\*/) {
704 ch = source.charCodeAt(++pos);
705 if (ch === 13/*\r*/ && source.charCodeAt(pos + 1) === 10/*\n*/)
706 pos++;
707 }
708 else if (isBr(ch))
709 break;
710 }
711 syntaxError();
712}
713
714function regexCharacterClass () {
715 while (pos++ < end) {
716 let ch = source.charCodeAt(pos);
717 if (ch === 93/*]*/)
718 return ch;
719 if (ch === 92/*\*/)
720 pos++;
721 else if (ch === 10/*\n*/ || ch === 13/*\r*/)
722 break;
723 }
724 syntaxError();
725}
726
727function regularExpression () {
728 while (pos++ < end) {
729 let ch = source.charCodeAt(pos);
730 if (ch === 47/*/*/)
731 return;
732 if (ch === 91/*[*/)
733 ch = regexCharacterClass();
734 else if (ch === 92/*\*/)
735 pos++;
736 else if (ch === 10/*\n*/ || ch === 13/*\r*/)
737 break;
738 }
739 syntaxError();
740}
741
742function readToWsOrPunctuator (ch) {
743 do {
744 if (isBrOrWs(ch) || isPunctuator(ch))
745 return ch;
746 } while (ch = source.charCodeAt(++pos));
747 return ch;
748}
749
750// Note: non-asii BR and whitespace checks omitted for perf / footprint
751// if there is a significant user need this can be reconsidered
752function isBr (c) {
753 return c === 13/*\r*/ || c === 10/*\n*/;
754}
755
756function isWsNotBr (c) {
757 return c === 9 || c === 11 || c === 12 || c === 32 || c === 160;
758}
759
760function isBrOrWs (c) {
761 return c > 8 && c < 14 || c === 32 || c === 160;
762}
763
764function isBrOrWsOrPunctuatorNotDot (c) {
765 return c > 8 && c < 14 || c === 32 || c === 160 || isPunctuator(c) && c !== 46/*.*/;
766}
767
768function keywordStart (pos) {
769 return pos === 0 || isBrOrWsOrPunctuatorNotDot(source.charCodeAt(pos - 1));
770}
771
772function readPrecedingKeyword (pos, match) {
773 if (pos < match.length - 1)
774 return false;
775 return source.startsWith(match, pos - match.length + 1) && (pos === 0 || isBrOrWsOrPunctuatorNotDot(source.charCodeAt(pos - match.length)));
776}
777
778function readPrecedingKeyword1 (pos, ch) {
779 return source.charCodeAt(pos) === ch && (pos === 0 || isBrOrWsOrPunctuatorNotDot(source.charCodeAt(pos - 1)));
780}
781
782// Detects one of case, debugger, delete, do, else, in, instanceof, new,
783// return, throw, typeof, void, yield, await
784function isExpressionKeyword (pos) {
785 switch (source.charCodeAt(pos)) {
786 case 100/*d*/:
787 switch (source.charCodeAt(pos - 1)) {
788 case 105/*i*/:
789 // void
790 return readPrecedingKeyword(pos - 2, 'vo');
791 case 108/*l*/:
792 // yield
793 return readPrecedingKeyword(pos - 2, 'yie');
794 default:
795 return false;
796 }
797 case 101/*e*/:
798 switch (source.charCodeAt(pos - 1)) {
799 case 115/*s*/:
800 switch (source.charCodeAt(pos - 2)) {
801 case 108/*l*/:
802 // else
803 return readPrecedingKeyword1(pos - 3, 101/*e*/);
804 case 97/*a*/:
805 // case
806 return readPrecedingKeyword1(pos - 3, 99/*c*/);
807 default:
808 return false;
809 }
810 case 116/*t*/:
811 // delete
812 return readPrecedingKeyword(pos - 2, 'dele');
813 default:
814 return false;
815 }
816 case 102/*f*/:
817 if (source.charCodeAt(pos - 1) !== 111/*o*/ || source.charCodeAt(pos - 2) !== 101/*e*/)
818 return false;
819 switch (source.charCodeAt(pos - 3)) {
820 case 99/*c*/:
821 // instanceof
822 return readPrecedingKeyword(pos - 4, 'instan');
823 case 112/*p*/:
824 // typeof
825 return readPrecedingKeyword(pos - 4, 'ty');
826 default:
827 return false;
828 }
829 case 110/*n*/:
830 // in, return
831 return readPrecedingKeyword1(pos - 1, 105/*i*/) || readPrecedingKeyword(pos - 1, 'retur');
832 case 111/*o*/:
833 // do
834 return readPrecedingKeyword1(pos - 1, 100/*d*/);
835 case 114/*r*/:
836 // debugger
837 return readPrecedingKeyword(pos - 1, 'debugge');
838 case 116/*t*/:
839 // await
840 return readPrecedingKeyword(pos - 1, 'awai');
841 case 119/*w*/:
842 switch (source.charCodeAt(pos - 1)) {
843 case 101/*e*/:
844 // new
845 return readPrecedingKeyword1(pos - 2, 110/*n*/);
846 case 111/*o*/:
847 // throw
848 return readPrecedingKeyword(pos - 2, 'thr');
849 default:
850 return false;
851 }
852 }
853 return false;
854}
855
856function isParenKeyword (curPos) {
857 return source.charCodeAt(curPos) === 101/*e*/ && source.startsWith('whil', curPos - 4) ||
858 source.charCodeAt(curPos) === 114/*r*/ && source.startsWith('fo', curPos - 2) ||
859 source.charCodeAt(curPos - 1) === 105/*i*/ && source.charCodeAt(curPos) === 102/*f*/;
860}
861
862function isPunctuator (ch) {
863 // 23 possible punctuator endings: !%&()*+,-./:;<=>?[]^{}|~
864 return ch === 33/*!*/ || ch === 37/*%*/ || ch === 38/*&*/ ||
865 ch > 39 && ch < 48 || ch > 57 && ch < 64 ||
866 ch === 91/*[*/ || ch === 93/*]*/ || ch === 94/*^*/ ||
867 ch > 122 && ch < 127;
868}
869
870function isExpressionPunctuator (ch) {
871 // 20 possible expression endings: !%&(*+,-.:;<=>?[^{|~
872 return ch === 33/*!*/ || ch === 37/*%*/ || ch === 38/*&*/ ||
873 ch > 39 && ch < 47 && ch !== 41 || ch > 57 && ch < 64 ||
874 ch === 91/*[*/ || ch === 94/*^*/ || ch > 122 && ch < 127 && ch !== 125/*}*/;
875}
876
877function isExpressionTerminator (curPos) {
878 // detects:
879 // => ; ) finally catch else
880 // as all of these followed by a { will indicate a statement brace
881 switch (source.charCodeAt(curPos)) {
882 case 62/*>*/:
883 return source.charCodeAt(curPos - 1) === 61/*=*/;
884 case 59/*;*/:
885 case 41/*)*/:
886 return true;
887 case 104/*h*/:
888 return source.startsWith('catc', curPos - 4);
889 case 121/*y*/:
890 return source.startsWith('finall', curPos - 6);
891 case 101/*e*/:
892 return source.startsWith('els', curPos - 3);
893 }
894 return false;
895}
896
897function syntaxError () {
898 throw Object.assign(new Error(`Parse error ${name}:${source.slice(0, pos).split('\n').length}:${pos - source.lastIndexOf('\n', pos - 1)}`), { idx: pos });
899}
\No newline at end of file