UNPKG

22.8 kBJavaScriptView Raw
1/* eslint max-len: 0 */
2
3import {input, isFlowEnabled, state} from "../traverser/base";
4import {unexpected} from "../traverser/util";
5import {charCodes} from "../util/charcodes";
6import {IS_IDENTIFIER_CHAR, IS_IDENTIFIER_START} from "../util/identifier";
7import {IS_WHITESPACE} from "../util/whitespace";
8import {ContextualKeyword} from "./keywords";
9import readWord from "./readWord";
10import { TokenType as tt} from "./types";
11
12export var IdentifierRole; (function (IdentifierRole) {
13 const Access = 0; IdentifierRole[IdentifierRole["Access"] = Access] = "Access";
14 const ExportAccess = Access + 1; IdentifierRole[IdentifierRole["ExportAccess"] = ExportAccess] = "ExportAccess";
15 const TopLevelDeclaration = ExportAccess + 1; IdentifierRole[IdentifierRole["TopLevelDeclaration"] = TopLevelDeclaration] = "TopLevelDeclaration";
16 const FunctionScopedDeclaration = TopLevelDeclaration + 1; IdentifierRole[IdentifierRole["FunctionScopedDeclaration"] = FunctionScopedDeclaration] = "FunctionScopedDeclaration";
17 const BlockScopedDeclaration = FunctionScopedDeclaration + 1; IdentifierRole[IdentifierRole["BlockScopedDeclaration"] = BlockScopedDeclaration] = "BlockScopedDeclaration";
18 const ObjectShorthandTopLevelDeclaration = BlockScopedDeclaration + 1; IdentifierRole[IdentifierRole["ObjectShorthandTopLevelDeclaration"] = ObjectShorthandTopLevelDeclaration] = "ObjectShorthandTopLevelDeclaration";
19 const ObjectShorthandFunctionScopedDeclaration = ObjectShorthandTopLevelDeclaration + 1; IdentifierRole[IdentifierRole["ObjectShorthandFunctionScopedDeclaration"] = ObjectShorthandFunctionScopedDeclaration] = "ObjectShorthandFunctionScopedDeclaration";
20 const ObjectShorthandBlockScopedDeclaration = ObjectShorthandFunctionScopedDeclaration + 1; IdentifierRole[IdentifierRole["ObjectShorthandBlockScopedDeclaration"] = ObjectShorthandBlockScopedDeclaration] = "ObjectShorthandBlockScopedDeclaration";
21 const ObjectShorthand = ObjectShorthandBlockScopedDeclaration + 1; IdentifierRole[IdentifierRole["ObjectShorthand"] = ObjectShorthand] = "ObjectShorthand";
22 const ObjectKey = ObjectShorthand + 1; IdentifierRole[IdentifierRole["ObjectKey"] = ObjectKey] = "ObjectKey";
23})(IdentifierRole || (IdentifierRole = {}));
24
25export function isDeclaration(token) {
26 const role = token.identifierRole;
27 return (
28 role === IdentifierRole.TopLevelDeclaration ||
29 role === IdentifierRole.FunctionScopedDeclaration ||
30 role === IdentifierRole.BlockScopedDeclaration ||
31 role === IdentifierRole.ObjectShorthandTopLevelDeclaration ||
32 role === IdentifierRole.ObjectShorthandFunctionScopedDeclaration ||
33 role === IdentifierRole.ObjectShorthandBlockScopedDeclaration
34 );
35}
36
37export function isNonTopLevelDeclaration(token) {
38 const role = token.identifierRole;
39 return (
40 role === IdentifierRole.FunctionScopedDeclaration ||
41 role === IdentifierRole.BlockScopedDeclaration ||
42 role === IdentifierRole.ObjectShorthandFunctionScopedDeclaration ||
43 role === IdentifierRole.ObjectShorthandBlockScopedDeclaration
44 );
45}
46
47export function isBlockScopedDeclaration(token) {
48 const role = token.identifierRole;
49 // Treat top-level declarations as block scope since the distinction doesn't matter here.
50 return (
51 role === IdentifierRole.TopLevelDeclaration ||
52 role === IdentifierRole.BlockScopedDeclaration ||
53 role === IdentifierRole.ObjectShorthandTopLevelDeclaration ||
54 role === IdentifierRole.ObjectShorthandBlockScopedDeclaration
55 );
56}
57
58export function isFunctionScopedDeclaration(token) {
59 const role = token.identifierRole;
60 return (
61 role === IdentifierRole.FunctionScopedDeclaration ||
62 role === IdentifierRole.ObjectShorthandFunctionScopedDeclaration
63 );
64}
65
66export function isObjectShorthandDeclaration(token) {
67 return (
68 token.identifierRole === IdentifierRole.ObjectShorthandTopLevelDeclaration ||
69 token.identifierRole === IdentifierRole.ObjectShorthandBlockScopedDeclaration ||
70 token.identifierRole === IdentifierRole.ObjectShorthandFunctionScopedDeclaration
71 );
72}
73
74// Object type used to represent tokens. Note that normally, tokens
75// simply exist as properties on the parser object. This is only
76// used for the onToken callback and the external tokenizer.
77export class Token {
78 constructor() {
79 this.type = state.type;
80 this.contextualKeyword = state.contextualKeyword;
81 this.start = state.start;
82 this.end = state.end;
83 this.isType = state.isType;
84 this.identifierRole = null;
85 this.shadowsGlobal = false;
86 this.contextId = null;
87 this.rhsEndIndex = null;
88 this.isExpression = false;
89 }
90
91
92
93
94
95
96
97 // Initially false for all tokens, then may be computed in a follow-up step that does scope
98 // analysis.
99
100
101
102 // For class tokens, records if the class is a class expression or a class statement.
103
104}
105
106// ## Tokenizer
107
108// Move to the next token
109export function next() {
110 state.tokens.push(new Token());
111 nextToken();
112}
113
114// Call instead of next when inside a template, since that needs to be handled differently.
115export function nextTemplateToken() {
116 state.tokens.push(new Token());
117 state.start = state.pos;
118 readTmplToken();
119}
120
121// The tokenizer never parses regexes by default. Instead, the parser is responsible for
122// instructing it to parse a regex when we see a slash at the start of an expression.
123export function retokenizeSlashAsRegex() {
124 if (state.type === tt.assign) {
125 --state.pos;
126 }
127 readRegexp();
128}
129
130export function pushTypeContext(existingTokensInType) {
131 for (let i = state.tokens.length - existingTokensInType; i < state.tokens.length; i++) {
132 state.tokens[i].isType = true;
133 }
134 const oldIsType = state.isType;
135 state.isType = true;
136 return oldIsType;
137}
138
139export function popTypeContext(oldIsType) {
140 state.isType = oldIsType;
141}
142
143export function eat(type) {
144 if (match(type)) {
145 next();
146 return true;
147 } else {
148 return false;
149 }
150}
151
152export function match(type) {
153 return state.type === type;
154}
155
156export function lookaheadType() {
157 const snapshot = state.snapshot();
158 next();
159 const type = state.type;
160 state.restoreFromSnapshot(snapshot);
161 return type;
162}
163
164export class TypeAndKeyword {
165
166
167 constructor(type, contextualKeyword) {
168 this.type = type;
169 this.contextualKeyword = contextualKeyword;
170 }
171}
172
173export function lookaheadTypeAndKeyword() {
174 const snapshot = state.snapshot();
175 next();
176 const type = state.type;
177 const contextualKeyword = state.contextualKeyword;
178 state.restoreFromSnapshot(snapshot);
179 return new TypeAndKeyword(type, contextualKeyword);
180}
181
182// Read a single token, updating the parser object's token-related
183// properties.
184export function nextToken() {
185 skipSpace();
186 state.start = state.pos;
187 if (state.pos >= input.length) {
188 const tokens = state.tokens;
189 // We normally run past the end a bit, but if we're way past the end, avoid an infinite loop.
190 // Also check the token positions rather than the types since sometimes we rewrite the token
191 // type to something else.
192 if (
193 tokens.length >= 2 &&
194 tokens[tokens.length - 1].start >= input.length &&
195 tokens[tokens.length - 2].start >= input.length
196 ) {
197 unexpected("Unexpectedly reached the end of input.");
198 }
199 finishToken(tt.eof);
200 return;
201 }
202 readToken(input.charCodeAt(state.pos));
203}
204
205function readToken(code) {
206 // Identifier or keyword. '\uXXXX' sequences are allowed in
207 // identifiers, so '\' also dispatches to that.
208 if (
209 IS_IDENTIFIER_START[code] ||
210 code === charCodes.backslash ||
211 (code === charCodes.atSign && input.charCodeAt(state.pos + 1) === charCodes.atSign)
212 ) {
213 readWord();
214 } else {
215 getTokenFromCode(code);
216 }
217}
218
219function skipBlockComment() {
220 while (
221 input.charCodeAt(state.pos) !== charCodes.asterisk ||
222 input.charCodeAt(state.pos + 1) !== charCodes.slash
223 ) {
224 state.pos++;
225 if (state.pos > input.length) {
226 unexpected("Unterminated comment", state.pos - 2);
227 return;
228 }
229 }
230 state.pos += 2;
231}
232
233export function skipLineComment(startSkip) {
234 let ch = input.charCodeAt((state.pos += startSkip));
235 if (state.pos < input.length) {
236 while (
237 ch !== charCodes.lineFeed &&
238 ch !== charCodes.carriageReturn &&
239 ch !== charCodes.lineSeparator &&
240 ch !== charCodes.paragraphSeparator &&
241 ++state.pos < input.length
242 ) {
243 ch = input.charCodeAt(state.pos);
244 }
245 }
246}
247
248// Called at the start of the parse and after every token. Skips
249// whitespace and comments.
250export function skipSpace() {
251 while (state.pos < input.length) {
252 const ch = input.charCodeAt(state.pos);
253 switch (ch) {
254 case charCodes.carriageReturn:
255 if (input.charCodeAt(state.pos + 1) === charCodes.lineFeed) {
256 ++state.pos;
257 }
258
259 case charCodes.lineFeed:
260 case charCodes.lineSeparator:
261 case charCodes.paragraphSeparator:
262 ++state.pos;
263 break;
264
265 case charCodes.slash:
266 switch (input.charCodeAt(state.pos + 1)) {
267 case charCodes.asterisk:
268 skipBlockComment();
269 break;
270
271 case charCodes.slash:
272 skipLineComment(2);
273 break;
274
275 default:
276 return;
277 }
278 break;
279
280 default:
281 if (IS_WHITESPACE[ch]) {
282 ++state.pos;
283 } else {
284 return;
285 }
286 }
287 }
288}
289
290// Called at the end of every token. Sets various fields, and skips the space after the token, so
291// that the next one's `start` will point at the right position.
292export function finishToken(
293 type,
294 contextualKeyword = ContextualKeyword.NONE,
295) {
296 state.end = state.pos;
297 state.type = type;
298 state.contextualKeyword = contextualKeyword;
299}
300
301// ### Token reading
302
303// This is the function that is called to fetch the next token. It
304// is somewhat obscure, because it works in character codes rather
305// than characters, and because operator parsing has been inlined
306// into it.
307//
308// All in the name of speed.
309function readToken_dot() {
310 const nextChar = input.charCodeAt(state.pos + 1);
311 if (nextChar >= charCodes.digit0 && nextChar <= charCodes.digit9) {
312 readNumber(true);
313 return;
314 }
315
316 const next2 = input.charCodeAt(state.pos + 2);
317 if (nextChar === charCodes.dot && next2 === charCodes.dot) {
318 state.pos += 3;
319 finishToken(tt.ellipsis);
320 } else {
321 ++state.pos;
322 finishToken(tt.dot);
323 }
324}
325
326function readToken_slash() {
327 const nextChar = input.charCodeAt(state.pos + 1);
328 if (nextChar === charCodes.equalsTo) {
329 finishOp(tt.assign, 2);
330 } else {
331 finishOp(tt.slash, 1);
332 }
333}
334
335function readToken_mult_modulo(code) {
336 // '%*'
337 let tokenType = code === charCodes.asterisk ? tt.star : tt.modulo;
338 let width = 1;
339 let nextChar = input.charCodeAt(state.pos + 1);
340
341 // Exponentiation operator **
342 if (code === charCodes.asterisk && nextChar === charCodes.asterisk) {
343 width++;
344 nextChar = input.charCodeAt(state.pos + 2);
345 tokenType = tt.exponent;
346 }
347
348 // Match *= or %=, disallowing *=> which can be valid in flow.
349 if (
350 nextChar === charCodes.equalsTo &&
351 input.charCodeAt(state.pos + 2) !== charCodes.greaterThan
352 ) {
353 width++;
354 tokenType = tt.assign;
355 }
356
357 finishOp(tokenType, width);
358}
359
360function readToken_pipe_amp(code) {
361 // '|&'
362 const nextChar = input.charCodeAt(state.pos + 1);
363
364 if (nextChar === code) {
365 if (input.charCodeAt(state.pos + 2) === charCodes.equalsTo) {
366 // ||= or &&=
367 finishOp(tt.assign, 3);
368 } else {
369 // || or &&
370 finishOp(code === charCodes.verticalBar ? tt.logicalOR : tt.logicalAND, 2);
371 }
372 return;
373 }
374
375 if (code === charCodes.verticalBar) {
376 // '|>'
377 if (nextChar === charCodes.greaterThan) {
378 finishOp(tt.pipeline, 2);
379 return;
380 } else if (nextChar === charCodes.rightCurlyBrace && isFlowEnabled) {
381 // '|}'
382 finishOp(tt.braceBarR, 2);
383 return;
384 }
385 }
386
387 if (nextChar === charCodes.equalsTo) {
388 finishOp(tt.assign, 2);
389 return;
390 }
391
392 finishOp(code === charCodes.verticalBar ? tt.bitwiseOR : tt.bitwiseAND, 1);
393}
394
395function readToken_caret() {
396 // '^'
397 const nextChar = input.charCodeAt(state.pos + 1);
398 if (nextChar === charCodes.equalsTo) {
399 finishOp(tt.assign, 2);
400 } else {
401 finishOp(tt.bitwiseXOR, 1);
402 }
403}
404
405function readToken_plus_min(code) {
406 // '+-'
407 const nextChar = input.charCodeAt(state.pos + 1);
408
409 if (nextChar === code) {
410 finishOp(tt.incDec, 2);
411 return;
412 }
413
414 if (nextChar === charCodes.equalsTo) {
415 finishOp(tt.assign, 2);
416 } else if (code === charCodes.plusSign) {
417 finishOp(tt.plus, 1);
418 } else {
419 finishOp(tt.minus, 1);
420 }
421}
422
423// '<>'
424function readToken_lt_gt(code) {
425 // Avoid right-shift for things like Array<Array<string>>.
426 if (code === charCodes.greaterThan && state.isType) {
427 finishOp(tt.greaterThan, 1);
428 return;
429 }
430 const nextChar = input.charCodeAt(state.pos + 1);
431
432 if (nextChar === code) {
433 const size =
434 code === charCodes.greaterThan && input.charCodeAt(state.pos + 2) === charCodes.greaterThan
435 ? 3
436 : 2;
437 if (input.charCodeAt(state.pos + size) === charCodes.equalsTo) {
438 finishOp(tt.assign, size + 1);
439 return;
440 }
441 finishOp(tt.bitShift, size);
442 return;
443 }
444
445 if (nextChar === charCodes.equalsTo) {
446 // <= | >=
447 finishOp(tt.relationalOrEqual, 2);
448 } else if (code === charCodes.lessThan) {
449 finishOp(tt.lessThan, 1);
450 } else {
451 finishOp(tt.greaterThan, 1);
452 }
453}
454
455function readToken_eq_excl(code) {
456 // '=!'
457 const nextChar = input.charCodeAt(state.pos + 1);
458 if (nextChar === charCodes.equalsTo) {
459 finishOp(tt.equality, input.charCodeAt(state.pos + 2) === charCodes.equalsTo ? 3 : 2);
460 return;
461 }
462 if (code === charCodes.equalsTo && nextChar === charCodes.greaterThan) {
463 // '=>'
464 state.pos += 2;
465 finishToken(tt.arrow);
466 return;
467 }
468 finishOp(code === charCodes.equalsTo ? tt.eq : tt.bang, 1);
469}
470
471function readToken_question() {
472 // '?'
473 const nextChar = input.charCodeAt(state.pos + 1);
474 const nextChar2 = input.charCodeAt(state.pos + 2);
475 if (nextChar === charCodes.questionMark && !state.isType) {
476 if (nextChar2 === charCodes.equalsTo) {
477 // '??='
478 finishOp(tt.assign, 3);
479 } else {
480 // '??'
481 finishOp(tt.nullishCoalescing, 2);
482 }
483 } else if (
484 nextChar === charCodes.dot &&
485 !(nextChar2 >= charCodes.digit0 && nextChar2 <= charCodes.digit9)
486 ) {
487 // '.' not followed by a number
488 state.pos += 2;
489 finishToken(tt.questionDot);
490 } else {
491 ++state.pos;
492 finishToken(tt.question);
493 }
494}
495
496export function getTokenFromCode(code) {
497 switch (code) {
498 case charCodes.numberSign:
499 ++state.pos;
500 finishToken(tt.hash);
501 return;
502
503 // The interpretation of a dot depends on whether it is followed
504 // by a digit or another two dots.
505
506 case charCodes.dot:
507 readToken_dot();
508 return;
509
510 // Punctuation tokens.
511 case charCodes.leftParenthesis:
512 ++state.pos;
513 finishToken(tt.parenL);
514 return;
515 case charCodes.rightParenthesis:
516 ++state.pos;
517 finishToken(tt.parenR);
518 return;
519 case charCodes.semicolon:
520 ++state.pos;
521 finishToken(tt.semi);
522 return;
523 case charCodes.comma:
524 ++state.pos;
525 finishToken(tt.comma);
526 return;
527 case charCodes.leftSquareBracket:
528 ++state.pos;
529 finishToken(tt.bracketL);
530 return;
531 case charCodes.rightSquareBracket:
532 ++state.pos;
533 finishToken(tt.bracketR);
534 return;
535
536 case charCodes.leftCurlyBrace:
537 if (isFlowEnabled && input.charCodeAt(state.pos + 1) === charCodes.verticalBar) {
538 finishOp(tt.braceBarL, 2);
539 } else {
540 ++state.pos;
541 finishToken(tt.braceL);
542 }
543 return;
544
545 case charCodes.rightCurlyBrace:
546 ++state.pos;
547 finishToken(tt.braceR);
548 return;
549
550 case charCodes.colon:
551 if (input.charCodeAt(state.pos + 1) === charCodes.colon) {
552 finishOp(tt.doubleColon, 2);
553 } else {
554 ++state.pos;
555 finishToken(tt.colon);
556 }
557 return;
558
559 case charCodes.questionMark:
560 readToken_question();
561 return;
562 case charCodes.atSign:
563 ++state.pos;
564 finishToken(tt.at);
565 return;
566
567 case charCodes.graveAccent:
568 ++state.pos;
569 finishToken(tt.backQuote);
570 return;
571
572 case charCodes.digit0: {
573 const nextChar = input.charCodeAt(state.pos + 1);
574 // '0x', '0X', '0o', '0O', '0b', '0B'
575 if (
576 nextChar === charCodes.lowercaseX ||
577 nextChar === charCodes.uppercaseX ||
578 nextChar === charCodes.lowercaseO ||
579 nextChar === charCodes.uppercaseO ||
580 nextChar === charCodes.lowercaseB ||
581 nextChar === charCodes.uppercaseB
582 ) {
583 readRadixNumber();
584 return;
585 }
586 }
587 // Anything else beginning with a digit is an integer, octal
588 // number, or float.
589 case charCodes.digit1:
590 case charCodes.digit2:
591 case charCodes.digit3:
592 case charCodes.digit4:
593 case charCodes.digit5:
594 case charCodes.digit6:
595 case charCodes.digit7:
596 case charCodes.digit8:
597 case charCodes.digit9:
598 readNumber(false);
599 return;
600
601 // Quotes produce strings.
602 case charCodes.quotationMark:
603 case charCodes.apostrophe:
604 readString(code);
605 return;
606
607 // Operators are parsed inline in tiny state machines. '=' (charCodes.equalsTo) is
608 // often referred to. `finishOp` simply skips the amount of
609 // characters it is given as second argument, and returns a token
610 // of the type given by its first argument.
611
612 case charCodes.slash:
613 readToken_slash();
614 return;
615
616 case charCodes.percentSign:
617 case charCodes.asterisk:
618 readToken_mult_modulo(code);
619 return;
620
621 case charCodes.verticalBar:
622 case charCodes.ampersand:
623 readToken_pipe_amp(code);
624 return;
625
626 case charCodes.caret:
627 readToken_caret();
628 return;
629
630 case charCodes.plusSign:
631 case charCodes.dash:
632 readToken_plus_min(code);
633 return;
634
635 case charCodes.lessThan:
636 case charCodes.greaterThan:
637 readToken_lt_gt(code);
638 return;
639
640 case charCodes.equalsTo:
641 case charCodes.exclamationMark:
642 readToken_eq_excl(code);
643 return;
644
645 case charCodes.tilde:
646 finishOp(tt.tilde, 1);
647 return;
648
649 default:
650 break;
651 }
652
653 unexpected(`Unexpected character '${String.fromCharCode(code)}'`, state.pos);
654}
655
656function finishOp(type, size) {
657 state.pos += size;
658 finishToken(type);
659}
660
661function readRegexp() {
662 const start = state.pos;
663 let escaped = false;
664 let inClass = false;
665 for (;;) {
666 if (state.pos >= input.length) {
667 unexpected("Unterminated regular expression", start);
668 return;
669 }
670 const code = input.charCodeAt(state.pos);
671 if (escaped) {
672 escaped = false;
673 } else {
674 if (code === charCodes.leftSquareBracket) {
675 inClass = true;
676 } else if (code === charCodes.rightSquareBracket && inClass) {
677 inClass = false;
678 } else if (code === charCodes.slash && !inClass) {
679 break;
680 }
681 escaped = code === charCodes.backslash;
682 }
683 ++state.pos;
684 }
685 ++state.pos;
686 // Need to use `skipWord` because '\uXXXX' sequences are allowed here (don't ask).
687 skipWord();
688
689 finishToken(tt.regexp);
690}
691
692// Read an integer. We allow any valid digit, including hex digits, plus numeric separators, and
693// stop at any other character.
694function readInt() {
695 while (true) {
696 const code = input.charCodeAt(state.pos);
697 if (
698 (code >= charCodes.digit0 && code <= charCodes.digit9) ||
699 (code >= charCodes.lowercaseA && code <= charCodes.lowercaseF) ||
700 (code >= charCodes.uppercaseA && code <= charCodes.uppercaseF) ||
701 code === charCodes.underscore
702 ) {
703 state.pos++;
704 } else {
705 break;
706 }
707 }
708}
709
710function readRadixNumber() {
711 let isBigInt = false;
712
713 state.pos += 2; // 0x
714 readInt();
715
716 if (input.charCodeAt(state.pos) === charCodes.lowercaseN) {
717 ++state.pos;
718 isBigInt = true;
719 }
720
721 if (isBigInt) {
722 finishToken(tt.bigint);
723 return;
724 }
725
726 finishToken(tt.num);
727}
728
729// Read an integer, octal integer, or floating-point number.
730function readNumber(startsWithDot) {
731 let isBigInt = false;
732
733 if (!startsWithDot) {
734 readInt();
735 }
736
737 let nextChar = input.charCodeAt(state.pos);
738 if (nextChar === charCodes.dot) {
739 ++state.pos;
740 readInt();
741 nextChar = input.charCodeAt(state.pos);
742 }
743
744 if (nextChar === charCodes.uppercaseE || nextChar === charCodes.lowercaseE) {
745 nextChar = input.charCodeAt(++state.pos);
746 if (nextChar === charCodes.plusSign || nextChar === charCodes.dash) {
747 ++state.pos;
748 }
749 readInt();
750 nextChar = input.charCodeAt(state.pos);
751 }
752
753 if (nextChar === charCodes.lowercaseN) {
754 ++state.pos;
755 isBigInt = true;
756 }
757
758 if (isBigInt) {
759 finishToken(tt.bigint);
760 return;
761 }
762 finishToken(tt.num);
763}
764
765function readString(quote) {
766 state.pos++;
767 for (;;) {
768 if (state.pos >= input.length) {
769 unexpected("Unterminated string constant");
770 return;
771 }
772 const ch = input.charCodeAt(state.pos);
773 if (ch === charCodes.backslash) {
774 state.pos++;
775 } else if (ch === quote) {
776 break;
777 }
778 state.pos++;
779 }
780 state.pos++;
781 finishToken(tt.string);
782}
783
784// Reads template string tokens.
785function readTmplToken() {
786 for (;;) {
787 if (state.pos >= input.length) {
788 unexpected("Unterminated template");
789 return;
790 }
791 const ch = input.charCodeAt(state.pos);
792 if (
793 ch === charCodes.graveAccent ||
794 (ch === charCodes.dollarSign && input.charCodeAt(state.pos + 1) === charCodes.leftCurlyBrace)
795 ) {
796 if (state.pos === state.start && match(tt.template)) {
797 if (ch === charCodes.dollarSign) {
798 state.pos += 2;
799 finishToken(tt.dollarBraceL);
800 return;
801 } else {
802 ++state.pos;
803 finishToken(tt.backQuote);
804 return;
805 }
806 }
807 finishToken(tt.template);
808 return;
809 }
810 if (ch === charCodes.backslash) {
811 state.pos++;
812 }
813 state.pos++;
814 }
815}
816
817// Skip to the end of the current word. Note that this is the same as the snippet at the end of
818// readWord, but calling skipWord from readWord seems to slightly hurt performance from some rough
819// measurements.
820export function skipWord() {
821 while (state.pos < input.length) {
822 const ch = input.charCodeAt(state.pos);
823 if (IS_IDENTIFIER_CHAR[ch]) {
824 state.pos++;
825 } else if (ch === charCodes.backslash) {
826 // \u
827 state.pos += 2;
828 if (input.charCodeAt(state.pos) === charCodes.leftCurlyBrace) {
829 while (
830 state.pos < input.length &&
831 input.charCodeAt(state.pos) !== charCodes.rightCurlyBrace
832 ) {
833 state.pos++;
834 }
835 state.pos++;
836 }
837 } else {
838 break;
839 }
840 }
841}