hermes_parser/token_kinds.rs
1//! Token kinds, ported from include/hermes/Parser/TokenKinds.def.
2//!
3//! Every variant appears in the EXACT order of TokenKinds.def so that
4//! `ord(kind) == kind as u16` and the range-marker predicates stay plain
5//! integer comparisons — identical to the C++ lexer.
6
7/// JavaScript token kinds.
8///
9/// Ported from `include/hermes/Parser/TokenKinds.def`. The discriminant
10/// of each variant matches C++ `ord(TokenKind::name)`.
11///
12/// Each variant's doc quotes its `.def` entry verbatim: the macro name gives
13/// the category (`RESWORD`, `PUNCTUATOR`, `BINOP` with its precedence,
14/// `TEMPLATE`, `IDENT_OP`) and the string gives the source spelling.
15/// `RANGE_MARKER` variants are sentinels bounding a run of real kinds; the
16/// lexer never produces them.
17#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
18#[repr(u16)]
19#[allow(non_camel_case_types)]
20pub enum TokenKind {
21 /// `TOK(none, "<none>")` — no token; a freshly constructed `Token`.
22 none,
23 /// `TOK(identifier, "identifier")` — an identifier or a contextual
24 /// keyword the lexer did not reclassify.
25 identifier,
26 /// `TOK(private_identifier, "private identifier")` — a `#name` private
27 /// class element name (the `#` is not part of the identifier text).
28 private_identifier,
29
30 /// Exclusive lower bound of the reserved-word range
31 /// (`RANGE_MARKER(_first_resword)`); never produced by the lexer.
32 _first_resword,
33 /// `RESWORD(function)`
34 rw_function,
35 /// `RESWORD(for)`
36 rw_for,
37 /// `RESWORD(if)`
38 rw_if,
39 /// `RESWORD(in)`
40 rw_in,
41 /// `RESWORD(var)`
42 rw_var,
43 /// `RESWORD(break)`
44 rw_break,
45 /// `RESWORD(continue)`
46 rw_continue,
47 /// `RESWORD(return)`
48 rw_return,
49 /// `RESWORD(switch)`
50 rw_switch,
51 /// `RESWORD(this)`
52 rw_this,
53
54 /// `RESWORD(true)`
55 rw_true,
56 /// `RESWORD(false)`
57 rw_false,
58 /// `RESWORD(null)`
59 rw_null,
60 /// `RESWORD(case)`
61 rw_case,
62 /// `RESWORD(catch)`
63 rw_catch,
64 /// `RESWORD(const)`
65 rw_const,
66 /// `RESWORD(debugger)`
67 rw_debugger,
68 /// `RESWORD(default)`
69 rw_default,
70 /// `RESWORD(delete)`
71 rw_delete,
72 /// `RESWORD(do)`
73 rw_do,
74 /// `RESWORD(else)`
75 rw_else,
76 /// `RESWORD(finally)`
77 rw_finally,
78 /// `RESWORD(instanceof)`
79 rw_instanceof,
80 /// `RESWORD(new)`
81 rw_new,
82 /// `RESWORD(throw)`
83 rw_throw,
84 /// `RESWORD(try)`
85 rw_try,
86 /// `RESWORD(typeof)`
87 rw_typeof,
88 /// `RESWORD(void)`
89 rw_void,
90 /// `RESWORD(while)`
91 rw_while,
92 /// `RESWORD(with)`
93 rw_with,
94
95 /// `RESWORD(export)`
96 rw_export,
97 /// `RESWORD(import)`
98 rw_import,
99
100 /// `RESWORD(class)`
101 rw_class,
102 /// `RESWORD(static)`
103 rw_static,
104 /// `RESWORD(extends)`
105 rw_extends,
106 /// `RESWORD(super)`
107 rw_super,
108
109 // Future reserved words
110 /// `RESWORD(enum)`
111 rw_enum,
112
113 // Strict mode future reserved words
114 /// `RESWORD(implements)`
115 rw_implements,
116 /// `RESWORD(interface)`
117 rw_interface,
118 /// `RESWORD(package)`
119 rw_package,
120 /// `RESWORD(private)`
121 rw_private,
122 /// `RESWORD(protected)`
123 rw_protected,
124 /// `RESWORD(public)`
125 rw_public,
126 /// `RESWORD(yield)`
127 rw_yield,
128 /// Exclusive upper bound of the reserved-word range
129 /// (`RANGE_MARKER(_last_resword)`); never produced by the lexer.
130 _last_resword,
131
132 /// `PUNCTUATOR(l_brace, "{")`
133 l_brace,
134 /// `PUNCTUATOR_FLOW(l_bracepipe, "{|")` — opens a Flow exact object
135 /// type; only scanned as one token in `GrammarContext::Type`.
136 l_bracepipe,
137 /// `PUNCTUATOR(r_brace, "}")`
138 r_brace,
139 /// `PUNCTUATOR_FLOW(piper_brace, "|}")` — closes a Flow exact object
140 /// type; only scanned as one token in `GrammarContext::Type`.
141 piper_brace,
142 /// `PUNCTUATOR(l_paren, "(")`
143 l_paren,
144 /// `PUNCTUATOR(r_paren, ")")`
145 r_paren,
146 /// `PUNCTUATOR(l_square, "[")`
147 l_square,
148 /// `PUNCTUATOR(r_square, "]")`
149 r_square,
150 /// `PUNCTUATOR(period, ".")`
151 period,
152 /// `PUNCTUATOR(questiondot, "?.")`
153 questiondot,
154 /// `PUNCTUATOR(dotdotdot, "...")`
155 dotdotdot,
156 /// `PUNCTUATOR(semi, ";")`
157 semi,
158 /// `PUNCTUATOR(comma, ",")`
159 comma,
160 /// `PUNCTUATOR(plusplus, "++")`
161 plusplus,
162 /// `PUNCTUATOR(minusminus, "--")`
163 minusminus,
164 /// Exclusive lower bound of the binary-operator range
165 /// (`RANGE_MARKER(_first_binary)`); never produced by the lexer.
166 _first_binary,
167 /// `BINOP(starstar, "**", 12)`
168 starstar,
169 /// `BINOP(star, "*", 11)`
170 star,
171 /// `BINOP(percent, "%", 11)`
172 percent,
173 /// `BINOP(slash, "/", 11)`
174 slash,
175 /// `BINOP(plus, "+", 10)`
176 plus,
177 /// `BINOP(minus, "-", 10)`
178 minus,
179 /// `BINOP(lessless, "<<", 9)`
180 lessless,
181 /// `BINOP(greatergreater, ">>", 9)`
182 greatergreater,
183 /// `BINOP(greatergreatergreater, ">>>", 9)`
184 greatergreatergreater,
185 /// `BINOP(less, "<", 8)`
186 less,
187 /// `BINOP(greater, ">", 8)`
188 greater,
189 /// `BINOP(lessequal, "<=", 8)`
190 lessequal,
191 /// `BINOP(greaterequal, ">=", 8)`
192 greaterequal,
193 /// `BINOP(equalequal, "==", 7)`
194 equalequal,
195 /// `BINOP(exclaimequal, "!=", 7)`
196 exclaimequal,
197 /// `BINOP(equalequalequal, "===", 7)`
198 equalequalequal,
199 /// `BINOP(exclaimequalequal, "!==", 7)`
200 exclaimequalequal,
201 /// `BINOP(amp, "&", 6)`
202 amp,
203 /// `BINOP(caret, "^", 5)`
204 caret,
205 /// `BINOP(pipe, "|", 4)`
206 pipe,
207 /// `BINOP(ampamp, "&&", 3)`
208 ampamp,
209 /// `BINOP(pipepipe, "||", 2)`
210 pipepipe,
211 /// `BINOP(questionquestion, "??", 1)`
212 questionquestion,
213 /// Exclusive upper bound of the binary-operator range
214 /// (`RANGE_MARKER(_last_binary)`); never produced by the lexer.
215 _last_binary,
216 /// `PUNCTUATOR(exclaim, "!")`
217 exclaim,
218 /// `PUNCTUATOR(tilde, "~")`
219 tilde,
220 /// `PUNCTUATOR(question, "?")`
221 question,
222 /// `PUNCTUATOR(colon, ":")`
223 colon,
224 /// `PUNCTUATOR(equal, "=")`
225 equal,
226 /// `PUNCTUATOR(plusequal, "+=")`
227 plusequal,
228 /// `PUNCTUATOR(minusequal, "-=")`
229 minusequal,
230 /// `PUNCTUATOR(starequal, "*=")`
231 starequal,
232 /// `PUNCTUATOR(starstarequal, "**=")`
233 starstarequal,
234 /// `PUNCTUATOR(percentequal, "%=")`
235 percentequal,
236 /// `PUNCTUATOR(slashequal, "/=")`
237 slashequal,
238 /// `PUNCTUATOR(lesslessequal, "<<=")`
239 lesslessequal,
240 /// `PUNCTUATOR(greatergreaterequal, ">>=")`
241 greatergreaterequal,
242 /// `PUNCTUATOR(greatergreatergreaterequal, ">>>=")`
243 greatergreatergreaterequal,
244 /// `PUNCTUATOR(ampequal, "&=")`
245 ampequal,
246 /// `PUNCTUATOR(pipeequal, "|=")`
247 pipeequal,
248 /// `PUNCTUATOR(ampampequal, "&&=")`
249 ampampequal,
250 /// `PUNCTUATOR(pipepipeequal, "||=")`
251 pipepipeequal,
252 /// `PUNCTUATOR(questionquestionequal, "\?\?=")`
253 questionquestionequal,
254 /// `PUNCTUATOR(caretequal, "^=")`
255 caretequal,
256 /// `PUNCTUATOR(equalgreater, "=>")`
257 equalgreater,
258 /// `PUNCTUATOR(at, "@")`
259 at,
260
261 /// `IDENT_OP(as_operator, "as", 8)` — the contextual `as` cast operator.
262 /// The lexer scans `as` as an `identifier`; the parser reclassifies it
263 /// with `convert_cur_token_to_ident_op` where a cast is allowed.
264 as_operator,
265
266 /// `TOK(numeric_literal, "number")` — the `f64` value is read back with
267 /// `Token::get_numeric_literal`.
268 numeric_literal,
269 /// `TOK(string_literal, "string")` — `Token::get_string_literal` returns
270 /// the cooked value, with escapes already decoded.
271 string_literal,
272 /// `TOK(regexp_literal, "regexp")` — `Token::get_regexp_literal` returns
273 /// the body and flags; the pattern itself is not validated by the lexer.
274 regexp_literal,
275 /// `TOK(jsx_text, "JSX text")` — a run of literal JSX child text, only
276 /// produced by `JSLexer::advance_in_jsx_child`.
277 jsx_text,
278 /// `TOK(bigint_literal, "bigint")` — a `123n` literal; the value is kept
279 /// as text (`Token::get_bigint_literal`), never converted to `f64`.
280 bigint_literal,
281
282 /// `TEMPLATE(no_substitution_template, "template literal")` — a whole
283 /// template with no substitutions.
284 no_substitution_template,
285 /// `TEMPLATE(template_head, "template literal start")` — the chunk up to
286 /// the first `${`.
287 template_head,
288 /// `TEMPLATE(template_middle, "template literal resume")` — a chunk
289 /// between two substitutions.
290 template_middle,
291 /// `TEMPLATE(template_tail, "template literal end")` — the chunk after
292 /// the last substitution.
293 template_tail,
294
295 /// `TOK(eof, "<eof>")` — end of input; scanning again keeps returning it.
296 eof,
297 /// One past the last real token kind (`RANGE_MARKER(_last_token)`); its
298 /// ordinal plus one is [`NUM_JS_TOKENS`], the size of the token tables.
299 _last_token,
300}
301
302/// Number of token kinds = ord(_last_token) + 1.
303pub const NUM_JS_TOKENS: usize = TokenKind::_last_token as usize + 1;
304
305/// Human-readable names for each token kind, in `.def` order.
306/// Mirrors C++ `tokenKindStr`.
307const TOKEN_NAMES: [&str; NUM_JS_TOKENS] = [
308 // TOK(none, "<none>")
309 "<none>",
310 // TOK(identifier, "identifier")
311 "identifier",
312 // TOK(private_identifier, "private identifier")
313 "private identifier",
314
315 // RANGE_MARKER(_first_resword)
316 "<_first_resword>",
317 // RESWORD(function)
318 "function",
319 // RESWORD(for)
320 "for",
321 // RESWORD(if)
322 "if",
323 // RESWORD(in)
324 "in",
325 // RESWORD(var)
326 "var",
327 // RESWORD(break)
328 "break",
329 // RESWORD(continue)
330 "continue",
331 // RESWORD(return)
332 "return",
333 // RESWORD(switch)
334 "switch",
335 // RESWORD(this)
336 "this",
337
338 // RESWORD(true)
339 "true",
340 // RESWORD(false)
341 "false",
342 // RESWORD(null)
343 "null",
344 // RESWORD(case)
345 "case",
346 // RESWORD(catch)
347 "catch",
348 // RESWORD(const)
349 "const",
350 // RESWORD(debugger)
351 "debugger",
352 // RESWORD(default)
353 "default",
354 // RESWORD(delete)
355 "delete",
356 // RESWORD(do)
357 "do",
358 // RESWORD(else)
359 "else",
360 // RESWORD(finally)
361 "finally",
362 // RESWORD(instanceof)
363 "instanceof",
364 // RESWORD(new)
365 "new",
366 // RESWORD(throw)
367 "throw",
368 // RESWORD(try)
369 "try",
370 // RESWORD(typeof)
371 "typeof",
372 // RESWORD(void)
373 "void",
374 // RESWORD(while)
375 "while",
376 // RESWORD(with)
377 "with",
378
379 // RESWORD(export)
380 "export",
381 // RESWORD(import)
382 "import",
383
384 // RESWORD(class)
385 "class",
386 // RESWORD(static)
387 "static",
388 // RESWORD(extends)
389 "extends",
390 // RESWORD(super)
391 "super",
392
393 // Future reserved words
394 // RESWORD(enum)
395 "enum",
396
397 // Strict mode future reserved words
398 // RESWORD(implements)
399 "implements",
400 // RESWORD(interface)
401 "interface",
402 // RESWORD(package)
403 "package",
404 // RESWORD(private)
405 "private",
406 // RESWORD(protected)
407 "protected",
408 // RESWORD(public)
409 "public",
410 // RESWORD(yield)
411 "yield",
412 // RANGE_MARKER(_last_resword)
413 "<_last_resword>",
414
415 // PUNCTUATOR(l_brace, "{")
416 "{",
417 // PUNCTUATOR_FLOW(l_bracepipe, "{|")
418 "{|",
419 // PUNCTUATOR(r_brace, "}")
420 "}",
421 // PUNCTUATOR_FLOW(piper_brace, "|}")
422 "|}",
423 // PUNCTUATOR(l_paren, "(")
424 "(",
425 // PUNCTUATOR(r_paren, ")")
426 ")",
427 // PUNCTUATOR(l_square, "[")
428 "[",
429 // PUNCTUATOR(r_square, "]")
430 "]",
431 // PUNCTUATOR(period, ".")
432 ".",
433 // PUNCTUATOR(questiondot, "?.")
434 "?.",
435 // PUNCTUATOR(dotdotdot, "...")
436 "...",
437 // PUNCTUATOR(semi, ";")
438 ";",
439 // PUNCTUATOR(comma, ",")
440 ",",
441 // PUNCTUATOR(plusplus, "++")
442 "++",
443 // PUNCTUATOR(minusminus, "--")
444 "--",
445 // RANGE_MARKER(_first_binary)
446 "<_first_binary>",
447 // BINOP(starstar, "**", 12)
448 "**",
449 // BINOP(star, "*", 11)
450 "*",
451 // BINOP(percent, "%", 11)
452 "%",
453 // BINOP(slash, "/", 11)
454 "/",
455 // BINOP(plus, "+", 10)
456 "+",
457 // BINOP(minus, "-", 10)
458 "-",
459 // BINOP(lessless, "<<", 9)
460 "<<",
461 // BINOP(greatergreater, ">>", 9)
462 ">>",
463 // BINOP(greatergreatergreater, ">>>", 9)
464 ">>>",
465 // BINOP(less, "<", 8)
466 "<",
467 // BINOP(greater, ">", 8)
468 ">",
469 // BINOP(lessequal, "<=", 8)
470 "<=",
471 // BINOP(greaterequal, ">=", 8)
472 ">=",
473 // BINOP(equalequal, "==", 7)
474 "==",
475 // BINOP(exclaimequal, "!=", 7)
476 "!=",
477 // BINOP(equalequalequal, "===", 7)
478 "===",
479 // BINOP(exclaimequalequal, "!==", 7)
480 "!==",
481 // BINOP(amp, "&", 6)
482 "&",
483 // BINOP(caret, "^", 5)
484 "^",
485 // BINOP(pipe, "|", 4)
486 "|",
487 // BINOP(ampamp, "&&", 3)
488 "&&",
489 // BINOP(pipepipe, "||", 2)
490 "||",
491 // BINOP(questionquestion, "??", 1)
492 "??",
493 // RANGE_MARKER(_last_binary)
494 "<_last_binary>",
495 // PUNCTUATOR(exclaim, "!")
496 "!",
497 // PUNCTUATOR(tilde, "~")
498 "~",
499 // PUNCTUATOR(question, "?")
500 "?",
501 // PUNCTUATOR(colon, ":")
502 ":",
503 // PUNCTUATOR(equal, "=")
504 "=",
505 // PUNCTUATOR(plusequal, "+=")
506 "+=",
507 // PUNCTUATOR(minusequal, "-=")
508 "-=",
509 // PUNCTUATOR(starequal, "*=")
510 "*=",
511 // PUNCTUATOR(starstarequal, "**=")
512 "**=",
513 // PUNCTUATOR(percentequal, "%=")
514 "%=",
515 // PUNCTUATOR(slashequal, "/=")
516 "/=",
517 // PUNCTUATOR(lesslessequal, "<<=")
518 "<<=",
519 // PUNCTUATOR(greatergreaterequal, ">>=")
520 ">>=",
521 // PUNCTUATOR(greatergreatergreaterequal, ">>>=")
522 ">>>=",
523 // PUNCTUATOR(ampequal, "&=")
524 "&=",
525 // PUNCTUATOR(pipeequal, "|=")
526 "|=",
527 // PUNCTUATOR(ampampequal, "&&=")
528 "&&=",
529 // PUNCTUATOR(pipepipeequal, "||=")
530 "||=",
531 // PUNCTUATOR(questionquestionequal, "\?\?=")
532 "??=",
533 // PUNCTUATOR(caretequal, "^=")
534 "^=",
535 // PUNCTUATOR(equalgreater, "=>")
536 "=>",
537 // PUNCTUATOR(at, "@")
538 "@",
539
540 // IDENT_OP(as_operator, "as", 8)
541 "as",
542
543 // TOK(numeric_literal, "number")
544 "number",
545 // TOK(string_literal, "string")
546 "string",
547 // TOK(regexp_literal, "regexp")
548 "regexp",
549 // TOK(jsx_text, "JSX text")
550 "JSX text",
551 // TOK(bigint_literal, "bigint")
552 "bigint",
553
554 // TEMPLATE(no_substitution_template, "template literal")
555 "template literal",
556 // TEMPLATE(template_head, "template literal start")
557 "template literal start",
558 // TEMPLATE(template_middle, "template literal resume")
559 "template literal resume",
560 // TEMPLATE(template_tail, "template literal end")
561 "template literal end",
562
563 // TOK(eof, "<eof>")
564 "<eof>",
565 // RANGE_MARKER(_last_token)
566 "<_last_token>",
567];
568
569/// The `.def` variant name for each token kind, in `.def` order. This is the
570/// `#name` string the C++ `tokenVariantName` switch returns (e.g. `l_brace`,
571/// `starstar`, `rw_function`, `eof`), as used by the `js-lexer-dump` oracle —
572/// distinct from `TOKEN_NAMES` (the human-readable spelling, e.g. "{").
573const TOKEN_VARIANT_NAMES: [&str; NUM_JS_TOKENS] = [
574 "none",
575 "identifier",
576 "private_identifier",
577 "_first_resword",
578 "rw_function",
579 "rw_for",
580 "rw_if",
581 "rw_in",
582 "rw_var",
583 "rw_break",
584 "rw_continue",
585 "rw_return",
586 "rw_switch",
587 "rw_this",
588 "rw_true",
589 "rw_false",
590 "rw_null",
591 "rw_case",
592 "rw_catch",
593 "rw_const",
594 "rw_debugger",
595 "rw_default",
596 "rw_delete",
597 "rw_do",
598 "rw_else",
599 "rw_finally",
600 "rw_instanceof",
601 "rw_new",
602 "rw_throw",
603 "rw_try",
604 "rw_typeof",
605 "rw_void",
606 "rw_while",
607 "rw_with",
608 "rw_export",
609 "rw_import",
610 "rw_class",
611 "rw_static",
612 "rw_extends",
613 "rw_super",
614 "rw_enum",
615 "rw_implements",
616 "rw_interface",
617 "rw_package",
618 "rw_private",
619 "rw_protected",
620 "rw_public",
621 "rw_yield",
622 "_last_resword",
623 "l_brace",
624 "l_bracepipe",
625 "r_brace",
626 "piper_brace",
627 "l_paren",
628 "r_paren",
629 "l_square",
630 "r_square",
631 "period",
632 "questiondot",
633 "dotdotdot",
634 "semi",
635 "comma",
636 "plusplus",
637 "minusminus",
638 "_first_binary",
639 "starstar",
640 "star",
641 "percent",
642 "slash",
643 "plus",
644 "minus",
645 "lessless",
646 "greatergreater",
647 "greatergreatergreater",
648 "less",
649 "greater",
650 "lessequal",
651 "greaterequal",
652 "equalequal",
653 "exclaimequal",
654 "equalequalequal",
655 "exclaimequalequal",
656 "amp",
657 "caret",
658 "pipe",
659 "ampamp",
660 "pipepipe",
661 "questionquestion",
662 "_last_binary",
663 "exclaim",
664 "tilde",
665 "question",
666 "colon",
667 "equal",
668 "plusequal",
669 "minusequal",
670 "starequal",
671 "starstarequal",
672 "percentequal",
673 "slashequal",
674 "lesslessequal",
675 "greatergreaterequal",
676 "greatergreatergreaterequal",
677 "ampequal",
678 "pipeequal",
679 "ampampequal",
680 "pipepipeequal",
681 "questionquestionequal",
682 "caretequal",
683 "equalgreater",
684 "at",
685 "as_operator",
686 "numeric_literal",
687 "string_literal",
688 "regexp_literal",
689 "jsx_text",
690 "bigint_literal",
691 "no_substitution_template",
692 "template_head",
693 "template_middle",
694 "template_tail",
695 "eof",
696 "_last_token",
697];
698
699/// \return the `.def` variant name of `kind` (e.g. `l_brace`, `eof`), matching
700/// the C++ `tokenVariantName` switch used by `js-lexer-dump`.
701#[inline]
702pub fn variant_name(kind: TokenKind) -> &'static str {
703 TOKEN_VARIANT_NAMES[kind as usize]
704}
705
706/// Binary-operator precedences in `.def` order (0 = not a binary operator).
707/// Mirrors C++ `BINOP` precedence field.
708///
709/// IDENT_OP precedences (e.g. `as_operator`) are stored here for completeness
710/// but are not exposed by `binop_precedence`, which is gated to the binary
711/// marker range; the parser layer will read them directly from this table.
712const TOKEN_PREC: [u8; NUM_JS_TOKENS] = [
713 // none
714 0,
715 // identifier
716 0,
717 // private_identifier
718 0,
719
720 // _first_resword
721 0,
722 // rw_function .. rw_yield (44 entries)
723 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // function..this
724 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // true..do
725 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // else..with
726 0, 0, // export, import
727 0, 0, 0, 0, // class, static, extends, super
728 0, // enum
729 0, 0, 0, 0, 0, 0, 0, // implements..yield
730 // _last_resword
731 0,
732
733 // l_brace .. minusminus (15 entries)
734 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
735 // _first_binary
736 0,
737 // BINOP precedences (23 entries)
738 12, // starstar
739 11, // star
740 11, // percent
741 11, // slash
742 10, // plus
743 10, // minus
744 9, // lessless
745 9, // greatergreater
746 9, // greatergreatergreater
747 8, // less
748 8, // greater
749 8, // lessequal
750 8, // greaterequal
751 7, // equalequal
752 7, // exclaimequal
753 7, // equalequalequal
754 7, // exclaimequalequal
755 6, // amp
756 5, // caret
757 4, // pipe
758 3, // ampamp
759 2, // pipepipe
760 1, // questionquestion
761 // _last_binary
762 0,
763 // exclaim .. at (22 entries)
764 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
765
766 // IDENT_OP(as_operator, "as", 8)
767 8,
768
769 // numeric_literal .. bigint_literal (5 entries)
770 0, 0, 0, 0, 0,
771 // template tokens (4 entries)
772 0, 0, 0, 0,
773 // eof
774 0,
775 // _last_token
776 0,
777];
778
779/// \return the integer ordinal of `kind` (matches C++ `ord`).
780#[inline]
781pub const fn ord(kind: TokenKind) -> i32 {
782 kind as u16 as i32
783}
784
785/// \return the human-readable name of `kind` (matches C++ `tokenKindStr`).
786#[inline]
787pub fn token_kind_str(kind: TokenKind) -> &'static str {
788 TOKEN_NAMES[kind as usize]
789}
790
791/// \return the human-readable name for the token kind with the given ordinal.
792/// Used by the lexer to pre-intern reserved words by ordinal without needing to
793/// reconstruct a `TokenKind` value (which would require an unsafe transmute).
794#[inline]
795pub fn token_kind_str_by_ord(ord: i32) -> &'static str {
796 TOKEN_NAMES[ord as usize]
797}
798
799/// \return the binary-operator precedence of `kind`, or None if not a binary op.
800#[inline]
801pub fn binop_precedence(kind: TokenKind) -> Option<u8> {
802 if kind.in_binary_range() {
803 Some(TOKEN_PREC[kind as usize])
804 } else {
805 None
806 }
807}
808
809impl TokenKind {
810 /// True if this is a reserved word (strictly between the range markers).
811 #[inline]
812 pub fn is_res_word(self) -> bool {
813 (self as u16) > (TokenKind::_first_resword as u16)
814 && (self as u16) < (TokenKind::_last_resword as u16)
815 }
816
817 /// True if `self` is strictly inside the binary-operator range markers.
818 #[inline]
819 fn in_binary_range(self) -> bool {
820 (self as u16) > (TokenKind::_first_binary as u16)
821 && (self as u16) < (TokenKind::_last_binary as u16)
822 }
823
824 /// True if `self` is a punctuator (mirrors C++ `isPunctuatorDbg`).
825 ///
826 /// The contiguous punctuator run in TokenKinds.def spans `l_brace` through
827 /// `at`; the `_first_binary` and `_last_binary` range markers sit inside
828 /// that run but are NOT true punctuators, so we exclude them explicitly.
829 #[inline]
830 pub fn is_punctuator(self) -> bool {
831 let v = self as u16;
832 v >= (TokenKind::l_brace as u16)
833 && v <= (TokenKind::at as u16)
834 && v != (TokenKind::_first_binary as u16)
835 && v != (TokenKind::_last_binary as u16)
836 }
837}
838
839/// Recognise a reserved word by its bytes (mirrors `matchReservedWord` in
840/// JSLexer.cpp). Returns `TokenKind::identifier` if `bytes` is not a reserved
841/// word. Pure: no strict-mode filtering (that lives in lexer-core's
842/// `scanReservedWord`).
843pub fn match_reserved_word(bytes: &[u8]) -> TokenKind {
844 match bytes {
845 // RESWORD(function)
846 b"function" => TokenKind::rw_function,
847 // RESWORD(for)
848 b"for" => TokenKind::rw_for,
849 // RESWORD(if)
850 b"if" => TokenKind::rw_if,
851 // RESWORD(in)
852 b"in" => TokenKind::rw_in,
853 // RESWORD(var)
854 b"var" => TokenKind::rw_var,
855 // RESWORD(break)
856 b"break" => TokenKind::rw_break,
857 // RESWORD(continue)
858 b"continue" => TokenKind::rw_continue,
859 // RESWORD(return)
860 b"return" => TokenKind::rw_return,
861 // RESWORD(switch)
862 b"switch" => TokenKind::rw_switch,
863 // RESWORD(this)
864 b"this" => TokenKind::rw_this,
865
866 // RESWORD(true)
867 b"true" => TokenKind::rw_true,
868 // RESWORD(false)
869 b"false" => TokenKind::rw_false,
870 // RESWORD(null)
871 b"null" => TokenKind::rw_null,
872 // RESWORD(case)
873 b"case" => TokenKind::rw_case,
874 // RESWORD(catch)
875 b"catch" => TokenKind::rw_catch,
876 // RESWORD(const)
877 b"const" => TokenKind::rw_const,
878 // RESWORD(debugger)
879 b"debugger" => TokenKind::rw_debugger,
880 // RESWORD(default)
881 b"default" => TokenKind::rw_default,
882 // RESWORD(delete)
883 b"delete" => TokenKind::rw_delete,
884 // RESWORD(do)
885 b"do" => TokenKind::rw_do,
886 // RESWORD(else)
887 b"else" => TokenKind::rw_else,
888 // RESWORD(finally)
889 b"finally" => TokenKind::rw_finally,
890 // RESWORD(instanceof)
891 b"instanceof" => TokenKind::rw_instanceof,
892 // RESWORD(new)
893 b"new" => TokenKind::rw_new,
894 // RESWORD(throw)
895 b"throw" => TokenKind::rw_throw,
896 // RESWORD(try)
897 b"try" => TokenKind::rw_try,
898 // RESWORD(typeof)
899 b"typeof" => TokenKind::rw_typeof,
900 // RESWORD(void)
901 b"void" => TokenKind::rw_void,
902 // RESWORD(while)
903 b"while" => TokenKind::rw_while,
904 // RESWORD(with)
905 b"with" => TokenKind::rw_with,
906
907 // RESWORD(export)
908 b"export" => TokenKind::rw_export,
909 // RESWORD(import)
910 b"import" => TokenKind::rw_import,
911
912 // RESWORD(class)
913 b"class" => TokenKind::rw_class,
914 // RESWORD(static)
915 b"static" => TokenKind::rw_static,
916 // RESWORD(extends)
917 b"extends" => TokenKind::rw_extends,
918 // RESWORD(super)
919 b"super" => TokenKind::rw_super,
920
921 // Future reserved words
922 // RESWORD(enum)
923 b"enum" => TokenKind::rw_enum,
924
925 // Strict mode future reserved words
926 // RESWORD(implements)
927 b"implements" => TokenKind::rw_implements,
928 // RESWORD(interface)
929 b"interface" => TokenKind::rw_interface,
930 // RESWORD(package)
931 b"package" => TokenKind::rw_package,
932 // RESWORD(private)
933 b"private" => TokenKind::rw_private,
934 // RESWORD(protected)
935 b"protected" => TokenKind::rw_protected,
936 // RESWORD(public)
937 b"public" => TokenKind::rw_public,
938 // RESWORD(yield)
939 b"yield" => TokenKind::rw_yield,
940
941 _ => TokenKind::identifier,
942 }
943}
944
945#[cfg(test)]
946mod tests {
947 use super::*;
948
949 #[test]
950 fn ordinals_and_count() {
951 assert_eq!(TokenKind::none as u16, 0);
952 assert_eq!(TokenKind::identifier as u16, 1);
953 assert_eq!(TokenKind::private_identifier as u16, 2);
954 assert_eq!(TokenKind::_last_token as u16, 122);
955 assert_eq!(NUM_JS_TOKENS, 123);
956 assert_eq!(ord(TokenKind::eof), TokenKind::eof as u16 as i32);
957 }
958
959 #[test]
960 fn resword_range() {
961 assert!(TokenKind::rw_function.is_res_word());
962 assert!(TokenKind::rw_yield.is_res_word());
963 assert!(!TokenKind::identifier.is_res_word());
964 assert!(!TokenKind::l_brace.is_res_word());
965 assert!(!TokenKind::_first_resword.is_res_word());
966 assert!(!TokenKind::_last_resword.is_res_word());
967 }
968
969 #[test]
970 fn names_match_def() {
971 assert_eq!(token_kind_str(TokenKind::none), "<none>");
972 assert_eq!(token_kind_str(TokenKind::identifier), "identifier");
973 assert_eq!(token_kind_str(TokenKind::private_identifier), "private identifier");
974 assert_eq!(token_kind_str(TokenKind::rw_function), "function");
975 assert_eq!(token_kind_str(TokenKind::l_brace), "{");
976 assert_eq!(token_kind_str(TokenKind::starstar), "**");
977 assert_eq!(token_kind_str(TokenKind::as_operator), "as");
978 assert_eq!(token_kind_str(TokenKind::eof), "<eof>");
979 assert_eq!(token_kind_str(TokenKind::_first_resword), "<_first_resword>");
980 }
981
982 #[test]
983 fn token_kind_str_by_ord_keywords() {
984 assert_eq!(token_kind_str_by_ord(ord(TokenKind::rw_function)), "function");
985 assert_eq!(token_kind_str_by_ord(ord(TokenKind::rw_yield)), "yield");
986 assert_eq!(
987 token_kind_str_by_ord(ord(TokenKind::identifier)),
988 "identifier"
989 );
990 }
991
992 #[test]
993 fn variant_names_match_def() {
994 assert_eq!(variant_name(TokenKind::none), "none");
995 assert_eq!(variant_name(TokenKind::l_brace), "l_brace");
996 assert_eq!(variant_name(TokenKind::starstar), "starstar");
997 assert_eq!(variant_name(TokenKind::rw_function), "rw_function");
998 assert_eq!(variant_name(TokenKind::questionquestionequal), "questionquestionequal");
999 assert_eq!(variant_name(TokenKind::eof), "eof");
1000 assert_eq!(variant_name(TokenKind::_last_token), "_last_token");
1001 }
1002
1003 #[test]
1004 fn binop_precedence_table() {
1005 assert_eq!(binop_precedence(TokenKind::starstar), Some(12));
1006 assert_eq!(binop_precedence(TokenKind::star), Some(11));
1007 assert_eq!(binop_precedence(TokenKind::plus), Some(10));
1008 assert_eq!(binop_precedence(TokenKind::lessless), Some(9));
1009 assert_eq!(binop_precedence(TokenKind::less), Some(8));
1010 assert_eq!(binop_precedence(TokenKind::equalequal), Some(7));
1011 assert_eq!(binop_precedence(TokenKind::amp), Some(6));
1012 assert_eq!(binop_precedence(TokenKind::caret), Some(5));
1013 assert_eq!(binop_precedence(TokenKind::pipe), Some(4));
1014 assert_eq!(binop_precedence(TokenKind::ampamp), Some(3));
1015 assert_eq!(binop_precedence(TokenKind::pipepipe), Some(2));
1016 assert_eq!(binop_precedence(TokenKind::questionquestion), Some(1));
1017 assert_eq!(binop_precedence(TokenKind::l_brace), None);
1018 assert_eq!(binop_precedence(TokenKind::eof), None);
1019 // Range markers inside the binary span and the IDENT_OP `as` have no binop precedence.
1020 assert_eq!(binop_precedence(TokenKind::_first_binary), None);
1021 assert_eq!(binop_precedence(TokenKind::_last_binary), None);
1022 assert_eq!(binop_precedence(TokenKind::as_operator), None);
1023 }
1024
1025 #[test]
1026 fn punctuator_predicate() {
1027 assert!(TokenKind::l_brace.is_punctuator());
1028 assert!(TokenKind::starstar.is_punctuator());
1029 assert!(TokenKind::at.is_punctuator());
1030 assert!(!TokenKind::identifier.is_punctuator());
1031 assert!(!TokenKind::rw_function.is_punctuator());
1032 assert!(!TokenKind::numeric_literal.is_punctuator());
1033 assert!(!TokenKind::as_operator.is_punctuator());
1034 // Range markers that fall inside the punctuator span must NOT be punctuators.
1035 assert!(!TokenKind::_first_binary.is_punctuator());
1036 assert!(!TokenKind::_last_binary.is_punctuator());
1037 // PUNCTUATOR_FLOW tokens are treated as punctuators here (C++ isPunctuatorDbg
1038 // returns false for them because it only defines PUNCTUATOR).
1039 assert!(TokenKind::l_bracepipe.is_punctuator());
1040 assert!(TokenKind::piper_brace.is_punctuator());
1041 }
1042
1043 #[test]
1044 fn reserved_words() {
1045 assert_eq!(match_reserved_word(b"function"), TokenKind::rw_function);
1046 assert_eq!(match_reserved_word(b"yield"), TokenKind::rw_yield);
1047 assert_eq!(match_reserved_word(b"static"), TokenKind::rw_static);
1048 assert_eq!(match_reserved_word(b"extends"), TokenKind::rw_extends);
1049 assert_eq!(match_reserved_word(b"fora"), TokenKind::identifier);
1050 assert_eq!(match_reserved_word(b"Function"), TokenKind::identifier);
1051 assert_eq!(match_reserved_word(b""), TokenKind::identifier);
1052 assert_eq!(match_reserved_word(b"let"), TokenKind::identifier);
1053 }
1054}