1 | let 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 |
|
17 | function 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 |
|
23 | function 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 |
|
31 | export 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 |
|
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 |
|
208 | function 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 |
|
288 | function 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 | */
|
438 | let acornPos;
|
439 | function 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 |
|
465 | function 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 |
|
508 | function 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 |
|
536 | function 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 |
|
557 | function 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 |
|
572 | function 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 |
|
640 | function 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 |
|
660 | function 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 |
|
677 | function 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 |
|
690 | function lineComment () {
|
691 | while (pos++ < end) {
|
692 | const ch = source.charCodeAt(pos);
|
693 | if (ch === 10/*\n*/ || ch === 13/*\r*/)
|
694 | return;
|
695 | }
|
696 | }
|
697 |
|
698 | function 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 |
|
714 | function 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 |
|
727 | function 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 |
|
742 | function 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
|
752 | function isBr (c) {
|
753 | return c === 13/*\r*/ || c === 10/*\n*/;
|
754 | }
|
755 |
|
756 | function isWsNotBr (c) {
|
757 | return c === 9 || c === 11 || c === 12 || c === 32 || c === 160;
|
758 | }
|
759 |
|
760 | function isBrOrWs (c) {
|
761 | return c > 8 && c < 14 || c === 32 || c === 160;
|
762 | }
|
763 |
|
764 | function isBrOrWsOrPunctuatorNotDot (c) {
|
765 | return c > 8 && c < 14 || c === 32 || c === 160 || isPunctuator(c) && c !== 46/*.*/;
|
766 | }
|
767 |
|
768 | function keywordStart (pos) {
|
769 | return pos === 0 || isBrOrWsOrPunctuatorNotDot(source.charCodeAt(pos - 1));
|
770 | }
|
771 |
|
772 | function 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 |
|
778 | function 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
|
784 | function 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 |
|
856 | function 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 |
|
862 | function 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 |
|
870 | function 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 |
|
877 | function 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 |
|
897 | function 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 |