use std::fmt::{self, Display};
use oxc_data_structures::fieldless_enum;
fieldless_enum! {
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
#[repr(u8)]
#[non_exhaustive]
pub enum Kind {
#[default]
Eof = 0,
Undetermined,
Skip, HashbangComment,
Ident,
Await,
Break,
Case,
Catch,
Class,
Const,
Continue,
Debugger,
Default,
Delete,
Do,
Else,
Enum,
Export,
Extends,
Finally,
For,
Function,
If,
Import,
In,
Instanceof,
New,
Return,
Super,
Switch,
This,
Throw,
Try,
Typeof,
Var,
Void,
While,
With,
Async,
From,
Get,
Meta, Of,
Set,
Target, Accessor, Source, Defer, Abstract,
As,
Asserts,
Assert,
Any,
Boolean,
Constructor,
Declare,
Infer,
Intrinsic,
Is,
KeyOf,
Module,
Namespace,
Never,
Out,
Readonly,
Require,
Number, Object,
Satisfies,
String, Symbol,
Type,
Undefined,
Unique,
Using,
Unknown,
Global,
BigInt, Override,
Implements,
Interface,
Let,
Package,
Private,
Protected,
Public,
Static,
Yield,
True,
False,
Null,
Amp, Amp2,
Amp2Eq,
AmpEq,
Bang, Caret,
CaretEq,
Colon,
Comma,
Dot,
Dot3, Eq,
Eq2,
Eq3,
GtEq, LAngle,
LBrack,
LCurly,
LParen,
LtEq, Minus,
Minus2,
MinusEq,
Neq,
Neq2,
Percent,
PercentEq,
Pipe,
Pipe2,
Pipe2Eq,
PipeEq,
Plus,
Plus2,
PlusEq,
Question,
Question2,
Question2Eq,
QuestionDot,
RAngle,
RBrack,
RCurly,
RParen,
Semicolon,
ShiftLeft, ShiftLeftEq, ShiftRight, ShiftRight3, ShiftRight3Eq, ShiftRightEq, Slash,
SlashEq,
Star,
Star2,
Star2Eq,
StarEq,
Tilde,
Arrow,
Decimal,
Float,
Binary,
Octal,
Hex,
PositiveExponential,
NegativeExponential,
DecimalBigInt,
BinaryBigInt,
OctalBigInt,
HexBigInt,
Str,
RegExp,
NoSubstitutionTemplate,
TemplateHead,
TemplateMiddle,
TemplateTail,
PrivateIdentifier,
JSXText,
At,
}
}
#[allow(clippy::enum_glob_use, clippy::allow_attributes)]
use Kind::*;
impl Kind {
#[inline]
pub const fn is_eof(self) -> bool {
matches!(self, Eof)
}
#[rustfmt::skip]
#[inline]
pub const fn is_number(self) -> bool {
matches!(
self,
Decimal | Float | Binary | Octal | Hex | PositiveExponential | NegativeExponential
| DecimalBigInt | BinaryBigInt | OctalBigInt | HexBigInt
)
}
#[inline] pub fn matches_number_byte(self, b: u8) -> bool {
match self {
Decimal => b.is_ascii_digit(),
Binary => matches!(b, b'0'..=b'1'),
Octal => matches!(b, b'0'..=b'7'),
Hex => b.is_ascii_hexdigit(),
_ => unreachable!(),
}
}
#[inline]
pub const fn is_identifier_reference(
self,
is_yield_context: bool,
is_await_context: bool,
) -> bool {
self.is_identifier()
|| (!is_yield_context && matches!(self, Yield))
|| (!is_await_context && matches!(self, Await))
}
#[inline]
pub const fn is_binding_identifier(self) -> bool {
self.is_identifier() || matches!(self, Yield | Await)
}
#[inline]
pub const fn is_label_identifier(self, is_yield_context: bool, is_await_context: bool) -> bool {
self.is_identifier()
|| (!is_yield_context && matches!(self, Yield))
|| (!is_await_context && matches!(self, Await))
}
#[inline]
pub const fn is_identifier(self) -> bool {
self.is_identifier_name() && !self.is_reserved_keyword()
}
#[inline]
pub const fn is_ts_identifier(self, is_yield_context: bool, is_await_context: bool) -> bool {
self.is_identifier_reference(is_yield_context, is_await_context)
&& !self.is_strict_mode_contextual_keyword()
&& !self.is_contextual_keyword()
}
#[inline]
pub const fn is_identifier_name(self) -> bool {
matches!(self, Ident) || matches!(self as u8, x if x >= Await as u8 && x <= Null as u8)
}
#[inline]
pub const fn is_after_let(self) -> bool {
!matches!(self, In | Instanceof)
&& (matches!(self, LCurly | LBrack | Ident) || self.is_any_keyword())
}
#[inline]
pub const fn is_literal(self) -> bool {
matches!(self, Null | True | False | Str | RegExp) || self.is_number()
}
#[inline]
pub const fn is_after_await_or_yield(self) -> bool {
!self.is_binary_operator() && (self.is_literal() || self.is_identifier_name())
}
#[inline]
pub const fn is_literal_property_name(self) -> bool {
self.is_identifier_name() || matches!(self, Str) || self.is_number()
}
#[inline]
pub const fn is_identifier_or_keyword(self) -> bool {
self.is_literal_property_name() || matches!(self, PrivateIdentifier)
}
#[rustfmt::skip]
#[inline]
pub const fn is_assignment_operator(self) -> bool {
matches!(
self,
Eq | PlusEq | MinusEq | StarEq | SlashEq | PercentEq | ShiftLeftEq | ShiftRightEq
| ShiftRight3Eq | Pipe2Eq | Amp2Eq | PipeEq | CaretEq | AmpEq | Question2Eq | Star2Eq
)
}
#[rustfmt::skip]
#[inline]
pub const fn is_binary_operator(self) -> bool {
matches!(
self,
Eq2 | Neq | Eq3 | Neq2 | LAngle | LtEq | RAngle | GtEq | ShiftLeft | ShiftRight | ShiftRight3
| Plus | Minus | Star | Slash | Percent | Pipe | Caret | Amp | In | Instanceof | Star2
)
}
#[inline]
pub const fn is_logical_operator(self) -> bool {
matches!(self, Pipe2 | Amp2 | Question2)
}
#[inline]
pub const fn is_unary_operator(self) -> bool {
matches!(self, Minus | Plus | Bang | Tilde | Typeof | Void | Delete)
}
#[inline]
pub const fn is_update_operator(self) -> bool {
matches!(self, Plus2 | Minus2)
}
#[inline]
pub const fn is_any_keyword(self) -> bool {
self.is_reserved_keyword()
|| self.is_contextual_keyword()
|| self.is_strict_mode_contextual_keyword()
|| self.is_future_reserved_keyword()
}
#[rustfmt::skip]
#[inline]
pub const fn is_reserved_keyword(self) -> bool {
matches!(
self,
Await | Break | Case | Catch | Class | Const | Continue | Debugger | Default
| Delete | Do | Else | Enum | Export | Extends | False | Finally | For | Function | If
| Import | In | Instanceof | New | Null | Return | Super | Switch | This | Throw
| True | Try | Typeof | Var | Void | While | With | Yield
)
}
#[rustfmt::skip]
#[inline]
pub const fn is_strict_mode_contextual_keyword(self) -> bool {
matches!(self, Let | Static | Implements | Interface | Package | Private | Protected | Public)
}
#[rustfmt::skip]
#[inline]
pub const fn is_contextual_keyword(self) -> bool {
matches!(
self,
Async | From | Get | Meta | Of | Set | Target | Accessor | Abstract | As | Asserts | Assert
| Any | Boolean | Constructor | Declare | Infer | Intrinsic | Is | KeyOf | Module | Namespace
| Never | Out | Readonly | Require | Number | Object | Satisfies | String | Symbol | Type
| Undefined | Unique | Unknown | Using | Global | BigInt | Override | Source | Defer
)
}
#[rustfmt::skip]
#[inline]
pub const fn is_future_reserved_keyword(self) -> bool {
matches!(self, Implements | Interface | Package | Private | Protected | Public | Static)
}
#[inline]
pub const fn is_template_start_of_tagged_template(self) -> bool {
matches!(self, NoSubstitutionTemplate | TemplateHead)
}
#[rustfmt::skip]
#[inline]
pub const fn is_modifier_kind(self) -> bool {
matches!(
self,
Abstract | Accessor | Async | Const | Declare
| In | Out | Public | Private | Protected | Readonly | Static | Override
| Default | Export
)
}
#[inline]
pub const fn is_binding_identifier_or_private_identifier_or_pattern(self) -> bool {
matches!(self, LCurly | LBrack | PrivateIdentifier) || self.is_binding_identifier()
}
#[cold]
pub fn match_keyword(s: &str) -> Self {
let len = s.len();
if len <= 1 || len >= 12 || !unsafe { s.as_bytes().get_unchecked(0) }.is_ascii_lowercase() {
return Ident;
}
Self::match_keyword_impl(s)
}
fn match_keyword_impl(s: &str) -> Self {
match s {
"as" => As,
"do" => Do,
"if" => If,
"in" => In,
"is" => Is,
"of" => Of,
"any" => Any,
"for" => For,
"get" => Get,
"let" => Let,
"new" => New,
"out" => Out,
"set" => Set,
"try" => Try,
"var" => Var,
"case" => Case,
"else" => Else,
"enum" => Enum,
"from" => From,
"meta" => Meta,
"null" => Null,
"this" => This,
"true" => True,
"type" => Type,
"void" => Void,
"with" => With,
"async" => Async,
"await" => Await,
"break" => Break,
"catch" => Catch,
"class" => Class,
"const" => Const,
"false" => False,
"infer" => Infer,
"keyof" => KeyOf,
"never" => Never,
"super" => Super,
"throw" => Throw,
"using" => Using,
"while" => While,
"yield" => Yield,
"defer" => Defer,
"assert" => Assert,
"bigint" => BigInt,
"delete" => Delete,
"export" => Export,
"global" => Global,
"import" => Import,
"module" => Module,
"number" => Number,
"object" => Object,
"public" => Public,
"return" => Return,
"static" => Static,
"string" => String,
"switch" => Switch,
"symbol" => Symbol,
"target" => Target,
"typeof" => Typeof,
"unique" => Unique,
"source" => Source,
"asserts" => Asserts,
"boolean" => Boolean,
"declare" => Declare,
"default" => Default,
"extends" => Extends,
"finally" => Finally,
"package" => Package,
"private" => Private,
"require" => Require,
"unknown" => Unknown,
"abstract" => Abstract,
"accessor" => Accessor,
"continue" => Continue,
"debugger" => Debugger,
"function" => Function,
"override" => Override,
"readonly" => Readonly,
"interface" => Interface,
"intrinsic" => Intrinsic,
"namespace" => Namespace,
"protected" => Protected,
"satisfies" => Satisfies,
"undefined" => Undefined,
"implements" => Implements,
"instanceof" => Instanceof,
"constructor" => Constructor,
_ => Ident,
}
}
pub fn to_str(self) -> &'static str {
#[expect(clippy::match_same_arms)]
match self {
Undetermined => "Unknown",
Eof => "EOF",
Skip => "Skipped",
HashbangComment => "#!",
Ident => "Identifier",
Await => "await",
Break => "break",
Case => "case",
Catch => "catch",
Class => "class",
Const => "const",
Continue => "continue",
Debugger => "debugger",
Default => "default",
Delete => "delete",
Do => "do",
Else => "else",
Enum => "enum",
Export => "export",
Extends => "extends",
Finally => "finally",
For => "for",
Function => "function",
Using => "using",
If => "if",
Import => "import",
In => "in",
Instanceof => "instanceof",
New => "new",
Return => "return",
Super => "super",
Switch => "switch",
This => "this",
Throw => "throw",
Try => "try",
Typeof => "typeof",
Var => "var",
Void => "void",
While => "while",
With => "with",
As => "as",
Async => "async",
From => "from",
Get => "get",
Meta => "meta",
Of => "of",
Set => "set",
Asserts => "asserts",
Accessor => "accessor",
Abstract => "abstract",
Readonly => "readonly",
Declare => "declare",
Override => "override",
Type => "type",
Target => "target",
Source => "source",
Defer => "defer",
Implements => "implements",
Interface => "interface",
Package => "package",
Private => "private",
Protected => "protected",
Public => "public",
Static => "static",
Let => "let",
Yield => "yield",
Amp => "&",
Amp2 => "&&",
Amp2Eq => "&&=",
AmpEq => "&=",
Bang => "!",
Caret => "^",
CaretEq => "^=",
Colon => ":",
Comma => ",",
Dot => ".",
Dot3 => "...",
Eq => "=",
Eq2 => "==",
Eq3 => "===",
GtEq => ">=",
LAngle => "<",
LBrack => "[",
LCurly => "{",
LParen => "(",
LtEq => "<=",
Minus => "-",
Minus2 => "--",
MinusEq => "-=",
Neq => "!=",
Neq2 => "!==",
Percent => "%",
PercentEq => "%=",
Pipe => "|",
Pipe2 => "||",
Pipe2Eq => "||=",
PipeEq => "|=",
Plus => "+",
Plus2 => "++",
PlusEq => "+=",
Question => "?",
Question2 => "??",
Question2Eq => "??=",
QuestionDot => "?.",
RAngle => ">",
RBrack => "]",
RCurly => "}",
RParen => ")",
Semicolon => ";",
ShiftLeft => "<<",
ShiftLeftEq => "<<=",
ShiftRight => ">>",
ShiftRight3 => ">>>",
ShiftRight3Eq => ">>>=",
ShiftRightEq => ">>=",
Slash => "/",
SlashEq => "/=",
Star => "*",
Star2 => "**",
Star2Eq => "**=",
StarEq => "*=",
Tilde => "~",
Arrow => "=>",
Null => "null",
True => "true",
False => "false",
Decimal => "decimal",
Float | PositiveExponential | NegativeExponential => "float",
Binary => "binary",
Octal => "octal",
Hex => "hex",
DecimalBigInt => "decimal bigint",
BinaryBigInt => "binary bigint",
OctalBigInt => "octal bigint",
HexBigInt => "hex bigint",
Str | String => "string",
RegExp => "/regexp/",
NoSubstitutionTemplate => "${}",
TemplateHead => "${",
TemplateMiddle => "${expr}",
TemplateTail => "}",
PrivateIdentifier => "#identifier",
JSXText => "jsx",
At => "@",
Assert => "assert",
Any => "any",
Boolean => "boolean",
Constructor => "constructor",
Infer => "infer",
Intrinsic => "intrinsic",
Is => "is",
KeyOf => "keyof",
Module => "module",
Namespace => "namaespace",
Never => "never",
Out => "out",
Require => "require",
Number => "number",
Object => "object",
Satisfies => "satisfies",
Symbol => "symbol",
Undefined => "undefined",
Unique => "unique",
Unknown => "unknown",
Global => "global",
BigInt => "bigint",
}
}
}
impl Display for Kind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.to_str().fmt(f)
}
}