use oxc_ast::CommentKind;
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 kind = match byte {
b'(' => Kind::LParen,
b')' => Kind::RParen,
b';' => Kind::Semicolon,
_ => {
let byte_handlers = self.config.byte_handlers();
return unsafe { byte_handlers[byte as usize](self) };
}
};
let next = unsafe { self.source.position().add(1) };
self.source.set_position(next);
kind
}
}
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, UER, UER, ATR, PLS, COM, MIN, PRD, SLH, ZER, DIG, DIG, DIG, DIG, DIG, DIG, DIG, DIG, DIG, COL, UER, 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.whitespace_handler()
});
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!(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(CommentKind::HtmlClose))
});
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(CommentKind::Line)
}
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!(LSS(lexer) {
lexer.consume_char();
lexer.read_left_angle().unwrap_or_else(|| lexer.skip_single_line_comment(CommentKind::HtmlOpen))
});
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.as_bytes() {
b"wait" => Kind::Await,
b"sync" => Kind::Async,
b"bstract" => Kind::Abstract,
b"ccessor" => Kind::Accessor,
b"ny" => Kind::Any,
b"s" => Kind::As,
b"ssert" => Kind::Assert,
b"sserts" => Kind::Asserts,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_B(id_without_first_char) match id_without_first_char.as_bytes() {
b"reak" => Kind::Break,
b"oolean" => Kind::Boolean,
b"igint" => Kind::BigInt,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_C(id_without_first_char) match id_without_first_char.as_bytes() {
b"onst" => Kind::Const,
b"lass" => Kind::Class,
b"ontinue" => Kind::Continue,
b"atch" => Kind::Catch,
b"ase" => Kind::Case,
b"onstructor" => Kind::Constructor,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_D(id_without_first_char) match id_without_first_char.as_bytes() {
b"o" => Kind::Do,
b"elete" => Kind::Delete,
b"eclare" => Kind::Declare,
b"efault" => Kind::Default,
b"ebugger" => Kind::Debugger,
b"efer" => Kind::Defer,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_E(id_without_first_char) match id_without_first_char.as_bytes() {
b"lse" => Kind::Else,
b"num" => Kind::Enum,
b"xport" => Kind::Export,
b"xtends" => Kind::Extends,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_F(id_without_first_char) match id_without_first_char.as_bytes() {
b"unction" => Kind::Function,
b"alse" => Kind::False,
b"or" => Kind::For,
b"inally" => Kind::Finally,
b"rom" => Kind::From,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_G(id_without_first_char) match id_without_first_char.as_bytes() {
b"et" => Kind::Get,
b"lobal" => Kind::Global,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_I(id_without_first_char) match id_without_first_char.as_bytes() {
b"f" => Kind::If,
b"nstanceof" => Kind::Instanceof,
b"n" => Kind::In,
b"mplements" => Kind::Implements,
b"mport" => Kind::Import,
b"nfer" => Kind::Infer,
b"nterface" => Kind::Interface,
b"ntrinsic" => Kind::Intrinsic,
b"s" => Kind::Is,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_K(id_without_first_char) match id_without_first_char.as_bytes() {
b"eyof" => Kind::KeyOf,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_L(id_without_first_char) match id_without_first_char.as_bytes() {
b"et" => Kind::Let,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_M(id_without_first_char) match id_without_first_char.as_bytes() {
b"eta" => Kind::Meta,
b"odule" => Kind::Module,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_N(id_without_first_char) match id_without_first_char.as_bytes() {
b"ull" => Kind::Null,
b"ew" => Kind::New,
b"umber" => Kind::Number,
b"amespace" => Kind::Namespace,
b"ever" => Kind::Never,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_O(id_without_first_char) match id_without_first_char.as_bytes() {
b"f" => Kind::Of,
b"bject" => Kind::Object,
b"ut" => Kind::Out,
b"verride" => Kind::Override,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_P(id_without_first_char) match id_without_first_char.as_bytes() {
b"ackage" => Kind::Package,
b"rivate" => Kind::Private,
b"rotected" => Kind::Protected,
b"ublic" => Kind::Public,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_R(id_without_first_char) match id_without_first_char.as_bytes() {
b"eturn" => Kind::Return,
b"equire" => Kind::Require,
b"eadonly" => Kind::Readonly,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_S(id_without_first_char) match id_without_first_char.as_bytes() {
b"et" => Kind::Set,
b"uper" => Kind::Super,
b"witch" => Kind::Switch,
b"tatic" => Kind::Static,
b"ymbol" => Kind::Symbol,
b"tring" => Kind::String,
b"atisfies" => Kind::Satisfies,
b"ource" => Kind::Source,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_T(id_without_first_char) match id_without_first_char.as_bytes() {
b"his" => Kind::This,
b"rue" => Kind::True,
b"hrow" => Kind::Throw,
b"ry" => Kind::Try,
b"ypeof" => Kind::Typeof,
b"arget" => Kind::Target,
b"ype" => Kind::Type,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_U(id_without_first_char) match id_without_first_char.as_bytes() {
b"ndefined" => Kind::Undefined,
b"sing" => Kind::Using,
b"nique" => Kind::Unique,
b"nknown" => Kind::Unknown,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_V(id_without_first_char) match id_without_first_char.as_bytes() {
b"ar" => Kind::Var,
b"oid" => Kind::Void,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_W(id_without_first_char) match id_without_first_char.as_bytes() {
b"hile" => Kind::While,
b"ith" => Kind::With,
_ => Kind::Ident,
});
ascii_identifier_handler!(L_Y(id_without_first_char) match id_without_first_char.as_bytes() {
b"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!();
}