use oxc_data_structures::assert_unchecked;
use crate::{
config::{LexerConfig as Config, NoTokensLexerConfig, RuntimeLexerConfig, TokensLexerConfig},
diagnostics,
};
use super::{Kind, Lexer};
impl<C: Config> Lexer<'_, C> {
#[expect(clippy::inline_always)]
#[inline(always)]
pub(super) unsafe fn handle_byte(&mut self, byte: u8) -> Kind {
let byte_handlers = self.config.byte_handlers();
unsafe { byte_handlers[byte as usize](self) }
}
}
pub type ByteHandler<C> = unsafe fn(&mut Lexer<'_, C>) -> Kind;
pub type ByteHandlers<C> = [ByteHandler<C>; 256];
#[rustfmt::skip]
macro_rules! byte_handlers {
() => {
[
ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, SPS, LIN, ISP, ISP, LIN, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, SPS, EXL, QOD, HAS, IDT, PRC, AMP, QOS, PNO, PNC, ATR, PLS, COM, MIN, PRD, SLH, ZER, DIG, DIG, DIG, DIG, DIG, DIG, DIG, DIG, DIG, COL, SEM, LSS, EQL, GTR, QST, AT_, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, BTO, ESC, BTC, CRT, IDT, TPL, L_A, L_B, L_C, L_D, L_E, L_F, L_G, IDT, L_I, IDT, L_K, L_L, L_M, L_N, L_O, L_P, IDT, L_R, L_S, L_T, L_U, L_V, L_W, IDT, L_Y, IDT, BEO, PIP, BEC, TLD, ERR, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, ]
};
}
pub mod byte_handler_tables {
use super::*;
pub static NO_TOKENS: ByteHandlers<NoTokensLexerConfig> = byte_handlers!();
pub static WITH_TOKENS: ByteHandlers<TokensLexerConfig> = byte_handlers!();
pub static RUNTIME_TOKENS: ByteHandlers<RuntimeLexerConfig> = byte_handlers!();
}
macro_rules! ascii_byte_handler {
($id:ident($lex:ident) $body:expr) => {
#[expect(non_snake_case)]
fn $id<C: Config>($lex: &mut Lexer<'_, C>) -> Kind {
unsafe {
assert_unchecked!(!$lex.source.is_eof());
assert_unchecked!($lex.source.peek_byte_unchecked() < 128);
}
$body
}
};
}
macro_rules! ascii_identifier_handler {
($id:ident($str:ident) $body:expr) => {
#[expect(non_snake_case)]
fn $id<C: Config>(lexer: &mut Lexer<'_, C>) -> Kind {
let $str = unsafe { lexer.identifier_name_handler() };
$body
}
};
}
ascii_byte_handler!(ERR(lexer) {
let c = lexer.consume_char();
lexer.error(diagnostics::invalid_character(c, lexer.unterminated_range()));
Kind::Undetermined
});
ascii_byte_handler!(SPS(lexer) {
lexer.consume_char();
Kind::Skip
});
ascii_byte_handler!(ISP(lexer) {
lexer.consume_char();
lexer.trivia_builder.add_irregular_whitespace(lexer.token.start(), lexer.offset());
Kind::Skip
});
ascii_byte_handler!(LIN(lexer) {
lexer.consume_char();
lexer.line_break_handler()
});
ascii_byte_handler!(EXL(lexer) {
lexer.consume_char();
match lexer.peek_byte() {
Some(b'=') => {
lexer.consume_char();
if lexer.peek_byte() == Some(b'=') {
lexer.consume_char();
Kind::Neq2
} else {
Kind::Neq
}
}
_ => Kind::Bang
}
});
ascii_byte_handler!(QOD(lexer) {
unsafe { lexer.read_string_literal_double_quote() }
});
ascii_byte_handler!(QOS(lexer) {
unsafe { lexer.read_string_literal_single_quote() }
});
ascii_byte_handler!(HAS(lexer) {
lexer.consume_char();
lexer.private_identifier()
});
ascii_identifier_handler!(IDT(_id_without_first_char) {
Kind::Ident
});
ascii_byte_handler!(PRC(lexer) {
lexer.consume_char();
if lexer.next_ascii_byte_eq(b'=') {
Kind::PercentEq
} else {
Kind::Percent
}
});
ascii_byte_handler!(AMP(lexer) {
lexer.consume_char();
if lexer.next_ascii_byte_eq(b'&') {
if lexer.next_ascii_byte_eq(b'=') {
Kind::Amp2Eq
} else {
Kind::Amp2
}
} else if lexer.next_ascii_byte_eq(b'=') {
Kind::AmpEq
} else {
Kind::Amp
}
});
ascii_byte_handler!(PNO(lexer) {
lexer.consume_char();
Kind::LParen
});
ascii_byte_handler!(PNC(lexer) {
lexer.consume_char();
Kind::RParen
});
ascii_byte_handler!(ATR(lexer) {
lexer.consume_char();
if lexer.next_ascii_byte_eq(b'*') {
if lexer.next_ascii_byte_eq(b'=') {
Kind::Star2Eq
} else {
Kind::Star2
}
} else if lexer.next_ascii_byte_eq(b'=') {
Kind::StarEq
} else {
Kind::Star
}
});
ascii_byte_handler!(PLS(lexer) {
lexer.consume_char();
if lexer.next_ascii_byte_eq(b'+') {
Kind::Plus2
} else if lexer.next_ascii_byte_eq(b'=') {
Kind::PlusEq
} else {
Kind::Plus
}
});
ascii_byte_handler!(COM(lexer) {
lexer.consume_char();
Kind::Comma
});
ascii_byte_handler!(MIN(lexer) {
lexer.consume_char();
lexer.read_minus().unwrap_or_else(|| lexer.skip_single_line_comment())
});
ascii_byte_handler!(PRD(lexer) {
lexer.consume_char();
lexer.read_dot()
});
ascii_byte_handler!(SLH(lexer) {
lexer.consume_char();
match lexer.peek_byte() {
Some(b'/') => {
lexer.consume_char();
lexer.skip_single_line_comment()
}
Some(b'*') => {
lexer.consume_char();
lexer.skip_multi_line_comment()
}
_ => {
if lexer.next_ascii_byte_eq(b'=') {
Kind::SlashEq
} else {
Kind::Slash
}
}
}
});
ascii_byte_handler!(ZER(lexer) {
lexer.consume_char();
lexer.read_zero()
});
ascii_byte_handler!(DIG(lexer) {
lexer.consume_char();
lexer.decimal_literal_after_first_digit()
});
ascii_byte_handler!(COL(lexer) {
lexer.consume_char();
Kind::Colon
});
ascii_byte_handler!(SEM(lexer) {
lexer.consume_char();
Kind::Semicolon
});
ascii_byte_handler!(LSS(lexer) {
lexer.consume_char();
lexer.read_left_angle().unwrap_or_else(|| lexer.skip_single_line_comment())
});
ascii_byte_handler!(EQL(lexer) {
lexer.consume_char();
if lexer.next_ascii_byte_eq(b'=') {
if lexer.next_ascii_byte_eq(b'=') {
Kind::Eq3
} else {
Kind::Eq2
}
} else if lexer.next_ascii_byte_eq(b'>') {
Kind::Arrow
} else {
Kind::Eq
}
});
ascii_byte_handler!(GTR(lexer) {
lexer.consume_char();
Kind::RAngle
});
ascii_byte_handler!(QST(lexer) {
lexer.consume_char();
match lexer.peek_byte() {
Some(b'?') => {
lexer.consume_char();
if lexer.peek_byte() == Some(b'=') {
lexer.consume_char();
Kind::Question2Eq
} else {
Kind::Question2
}
}
Some(b'.') => {
if lexer.peek_2_bytes().is_none_or(|bytes| !bytes[1].is_ascii_digit()) {
lexer.consume_char();
Kind::QuestionDot
} else {
Kind::Question
}
}
_ => Kind::Question,
}
});
ascii_byte_handler!(AT_(lexer) {
lexer.consume_char();
Kind::At
});
ascii_byte_handler!(BTO(lexer) {
lexer.consume_char();
Kind::LBrack
});
ascii_byte_handler!(ESC(lexer) {
lexer.identifier_backslash_handler()
});
ascii_byte_handler!(BTC(lexer) {
lexer.consume_char();
Kind::RBrack
});
ascii_byte_handler!(CRT(lexer) {
lexer.consume_char();
if lexer.next_ascii_byte_eq(b'=') {
Kind::CaretEq
} else {
Kind::Caret
}
});
ascii_byte_handler!(TPL(lexer) {
lexer.consume_char();
lexer.read_template_literal(Kind::TemplateHead, Kind::NoSubstitutionTemplate)
});
ascii_byte_handler!(BEO(lexer) {
lexer.consume_char();
Kind::LCurly
});
ascii_byte_handler!(PIP(lexer) {
lexer.consume_char();
match lexer.peek_byte() {
Some(b'|') => {
lexer.consume_char();
if lexer.next_ascii_byte_eq(b'=') {
Kind::Pipe2Eq
} else {
Kind::Pipe2
}
}
Some(b'=') => {
lexer.consume_char();
Kind::PipeEq
}
_ => Kind::Pipe
}
});
ascii_byte_handler!(BEC(lexer) {
lexer.consume_char();
Kind::RCurly
});
ascii_byte_handler!(TLD(lexer) {
lexer.consume_char();
Kind::Tilde
});
ascii_identifier_handler!(L_A(id_without_first_char) match id_without_first_char {
"wait" => Kind::Await,
"sync" => Kind::Async,
"bstract" => Kind::Abstract,
"ccessor" => Kind::Accessor,
"ny" => Kind::Any,
"s" => Kind::As,
"ssert" => Kind::Assert,
"sserts" => Kind::Asserts,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_B(id_without_first_char) match id_without_first_char {
"reak" => Kind::Break,
"oolean" => Kind::Boolean,
"igint" => Kind::BigInt,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_C(id_without_first_char) match id_without_first_char {
"onst" => Kind::Const,
"lass" => Kind::Class,
"ontinue" => Kind::Continue,
"atch" => Kind::Catch,
"ase" => Kind::Case,
"onstructor" => Kind::Constructor,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_D(id_without_first_char) match id_without_first_char {
"o" => Kind::Do,
"elete" => Kind::Delete,
"eclare" => Kind::Declare,
"efault" => Kind::Default,
"ebugger" => Kind::Debugger,
"efer" => Kind::Defer,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_E(id_without_first_char) match id_without_first_char {
"lse" => Kind::Else,
"num" => Kind::Enum,
"xport" => Kind::Export,
"xtends" => Kind::Extends,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_F(id_without_first_char) match id_without_first_char {
"unction" => Kind::Function,
"alse" => Kind::False,
"or" => Kind::For,
"inally" => Kind::Finally,
"rom" => Kind::From,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_G(id_without_first_char) match id_without_first_char {
"et" => Kind::Get,
"lobal" => Kind::Global,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_I(id_without_first_char) match id_without_first_char {
"f" => Kind::If,
"nstanceof" => Kind::Instanceof,
"n" => Kind::In,
"mplements" => Kind::Implements,
"mport" => Kind::Import,
"nfer" => Kind::Infer,
"nterface" => Kind::Interface,
"ntrinsic" => Kind::Intrinsic,
"s" => Kind::Is,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_K(id_without_first_char) match id_without_first_char {
"eyof" => Kind::KeyOf,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_L(id_without_first_char) match id_without_first_char {
"et" => Kind::Let,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_M(id_without_first_char) match id_without_first_char {
"eta" => Kind::Meta,
"odule" => Kind::Module,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_N(id_without_first_char) match id_without_first_char {
"ull" => Kind::Null,
"ew" => Kind::New,
"umber" => Kind::Number,
"amespace" => Kind::Namespace,
"ever" => Kind::Never,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_O(id_without_first_char) match id_without_first_char {
"f" => Kind::Of,
"bject" => Kind::Object,
"ut" => Kind::Out,
"verride" => Kind::Override,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_P(id_without_first_char) match id_without_first_char {
"ackage" => Kind::Package,
"rivate" => Kind::Private,
"rotected" => Kind::Protected,
"ublic" => Kind::Public,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_R(id_without_first_char) match id_without_first_char {
"eturn" => Kind::Return,
"equire" => Kind::Require,
"eadonly" => Kind::Readonly,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_S(id_without_first_char) match id_without_first_char {
"et" => Kind::Set,
"uper" => Kind::Super,
"witch" => Kind::Switch,
"tatic" => Kind::Static,
"ymbol" => Kind::Symbol,
"tring" => Kind::String,
"atisfies" => Kind::Satisfies,
"ource" => Kind::Source,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_T(id_without_first_char) match id_without_first_char {
"his" => Kind::This,
"rue" => Kind::True,
"hrow" => Kind::Throw,
"ry" => Kind::Try,
"ypeof" => Kind::Typeof,
"arget" => Kind::Target,
"ype" => Kind::Type,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_U(id_without_first_char) match id_without_first_char {
"ndefined" => Kind::Undefined,
"sing" => Kind::Using,
"nique" => Kind::Unique,
"nknown" => Kind::Unknown,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_V(id_without_first_char) match id_without_first_char {
"ar" => Kind::Var,
"oid" => Kind::Void,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_W(id_without_first_char) match id_without_first_char {
"hile" => Kind::While,
"ith" => Kind::With,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_Y(id_without_first_char) match id_without_first_char {
"ield" => Kind::Yield,
_ => Kind::Ident,
});
#[expect(non_snake_case)]
fn UNI<C: Config>(lexer: &mut Lexer<'_, C>) -> Kind {
lexer.unicode_char_handler()
}
#[expect(non_snake_case)]
fn UER<C: Config>(_lexer: &mut Lexer<'_, C>) -> Kind {
unreachable!();
}