use std::{fmt, mem, ptr};
use oxc_span::Span;
use super::kind::Kind;
const START_SHIFT: usize = 0;
const END_SHIFT: usize = 32;
const KIND_SHIFT: usize = 64;
const IS_ON_NEW_LINE_SHIFT: usize = 72;
const ESCAPED_SHIFT: usize = 80;
const LONE_SURROGATES_SHIFT: usize = 88;
const HAS_SEPARATOR_SHIFT: usize = 96;
const START_MASK: u128 = 0xFFFF_FFFF; const END_MASK: u128 = 0xFFFF_FFFF; const KIND_MASK: u128 = 0xFF; const BOOL_MASK: u128 = 0xFF;
const _: () = {
const fn is_valid_shift(shift: usize) -> bool {
shift.is_multiple_of(8) && shift < u128::BITS as usize
}
assert!(is_valid_shift(IS_ON_NEW_LINE_SHIFT));
assert!(is_valid_shift(ESCAPED_SHIFT));
assert!(is_valid_shift(LONE_SURROGATES_SHIFT));
assert!(is_valid_shift(HAS_SEPARATOR_SHIFT));
};
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Token(u128);
impl Default for Token {
#[inline]
fn default() -> Self {
const _: () = assert!(Kind::Eof as u8 == 0);
Self(0)
}
}
impl fmt::Debug for Token {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Token")
.field("kind", &self.kind())
.field("start", &self.start())
.field("end", &self.end())
.field("is_on_new_line", &self.is_on_new_line())
.field("escaped", &self.escaped())
.field("lone_surrogates", &self.lone_surrogates())
.field("has_separator", &self.has_separator())
.finish()
}
}
impl Token {
#[inline]
pub(super) fn new_on_new_line() -> Self {
let mut token = Self::default();
token.set_is_on_new_line(true);
token
}
}
impl Token {
#[inline]
pub fn span(&self) -> Span {
Span::new(self.start(), self.end())
}
#[cfg(feature = "mutate_tokens")]
#[inline]
pub fn set_span(&mut self, span: Span) {
self.set_span_impl(span);
}
#[cfg(not(feature = "mutate_tokens"))]
#[inline]
#[allow(dead_code, clippy::allow_attributes)]
pub(super) fn set_span(&mut self, span: Span) {
self.set_span_impl(span);
}
#[inline]
fn set_span_impl(&mut self, span: Span) {
self.set_start(span.start);
self.set_end(span.end);
}
#[inline]
pub fn start(&self) -> u32 {
((self.0 >> START_SHIFT) & START_MASK) as u32
}
#[inline]
pub(super) fn set_start(&mut self, start: u32) {
self.0 &= !(START_MASK << START_SHIFT); self.0 |= u128::from(start) << START_SHIFT;
}
#[inline]
pub fn end(&self) -> u32 {
((self.0 >> END_SHIFT) & END_MASK) as u32
}
#[inline]
pub(super) fn set_end(&mut self, end: u32) {
let start = self.start();
debug_assert!(end >= start, "Token end ({end}) cannot be less than start ({start})");
self.0 &= !(END_MASK << END_SHIFT); self.0 |= u128::from(end) << END_SHIFT;
}
#[inline]
pub fn kind(&self) -> Kind {
unsafe { mem::transmute::<u8, Kind>(((self.0 >> KIND_SHIFT) & KIND_MASK) as u8) }
}
#[cfg(feature = "mutate_tokens")]
#[inline]
pub fn set_kind(&mut self, kind: Kind) {
self.set_kind_impl(kind);
}
#[cfg(not(feature = "mutate_tokens"))]
#[inline]
pub(super) fn set_kind(&mut self, kind: Kind) {
self.set_kind_impl(kind);
}
#[inline]
fn set_kind_impl(&mut self, kind: Kind) {
self.0 &= !(KIND_MASK << KIND_SHIFT); self.0 |= u128::from(kind as u8) << KIND_SHIFT;
}
#[inline]
pub fn is_on_new_line(&self) -> bool {
unsafe { self.read_bool(IS_ON_NEW_LINE_SHIFT) }
}
#[inline]
pub(super) fn set_is_on_new_line(&mut self, value: bool) {
self.0 &= !(BOOL_MASK << IS_ON_NEW_LINE_SHIFT); self.0 |= u128::from(value) << IS_ON_NEW_LINE_SHIFT;
}
#[inline]
pub fn escaped(&self) -> bool {
unsafe { self.read_bool(ESCAPED_SHIFT) }
}
#[inline]
pub(super) fn set_escaped(&mut self, escaped: bool) {
self.0 &= !(BOOL_MASK << ESCAPED_SHIFT); self.0 |= u128::from(escaped) << ESCAPED_SHIFT;
}
#[inline]
pub fn lone_surrogates(&self) -> bool {
unsafe { self.read_bool(LONE_SURROGATES_SHIFT) }
}
#[inline]
pub(super) fn set_lone_surrogates(&mut self, value: bool) {
self.0 &= !(BOOL_MASK << LONE_SURROGATES_SHIFT); self.0 |= u128::from(value) << LONE_SURROGATES_SHIFT;
}
#[inline]
pub fn has_separator(&self) -> bool {
unsafe { self.read_bool(HAS_SEPARATOR_SHIFT) }
}
#[inline]
pub(super) fn set_has_separator(&mut self, value: bool) {
self.0 &= !(BOOL_MASK << HAS_SEPARATOR_SHIFT); self.0 |= u128::from(value) << HAS_SEPARATOR_SHIFT;
}
#[expect(clippy::inline_always)]
#[inline(always)] unsafe fn read_bool(&self, shift: usize) -> bool {
let offset = if cfg!(target_endian = "little") { shift / 8 } else { 15 - (shift / 8) };
unsafe {
let field_ptr = ptr::from_ref(self).cast::<bool>().add(offset);
debug_assert!(*field_ptr.cast::<u8>() <= 1);
*field_ptr.as_ref().unwrap_unchecked()
}
}
}
#[cfg(test)]
mod test {
use super::{Kind, Span, Token};
const _: () = assert!(size_of::<Token>() == 16);
#[test]
fn default_token_values() {
let token = Token::default();
assert_eq!(token.start(), 0);
assert_eq!(token.end(), 0);
assert_eq!(token.kind(), Kind::Eof); assert!(!token.is_on_new_line());
assert!(!token.escaped());
assert!(!token.lone_surrogates());
assert!(!token.has_separator());
}
#[test]
fn new_on_new_line_token_values() {
let token = Token::new_on_new_line();
assert_eq!(token.start(), 0);
assert_eq!(token.end(), 0);
assert_eq!(token.kind(), Kind::Eof);
assert!(token.is_on_new_line());
assert!(!token.escaped());
assert!(!token.lone_surrogates());
assert!(!token.has_separator());
}
#[test]
fn token_creation_and_retrieval() {
let kind = Kind::Ident;
let start = 100u32;
let end = start + 5u32;
let is_on_new_line = true;
let escaped = false;
let lone_surrogates = true;
let has_separator = false;
let mut token = Token::default();
token.set_kind(kind);
token.set_start(start);
token.set_end(end);
token.set_is_on_new_line(is_on_new_line);
token.set_escaped(escaped);
token.set_lone_surrogates(lone_surrogates);
if has_separator {
token.set_has_separator(true);
}
assert_eq!(token.kind(), kind);
assert_eq!(token.start(), start);
assert_eq!(token.end(), end);
assert_eq!(token.is_on_new_line(), is_on_new_line);
assert_eq!(token.escaped(), escaped);
assert_eq!(token.lone_surrogates(), lone_surrogates);
assert_eq!(token.has_separator(), has_separator);
}
#[test]
fn token_setters() {
let mut token = Token::default();
token.set_kind(Kind::Ident);
token.set_span(Span::new(10, 15));
assert_eq!(token.start(), 10);
assert_eq!(token.end(), 15);
assert!(!token.escaped());
assert!(!token.is_on_new_line());
assert!(!token.lone_surrogates());
let mut token_for_set_end = Token::default();
token_for_set_end.set_kind(Kind::Ident);
token_for_set_end.set_start(10);
token_for_set_end.set_end(15);
assert_eq!(token_for_set_end.end(), 15);
token_for_set_end.set_end(30);
assert_eq!(token_for_set_end.start(), 10);
assert_eq!(token_for_set_end.end(), 30);
let mut token_with_flags = Token::default();
token_with_flags.set_kind(Kind::Str);
token_with_flags.set_start(30);
token_with_flags.set_end(33);
token_with_flags.set_is_on_new_line(true);
token_with_flags.set_escaped(true);
token_with_flags.set_lone_surrogates(true);
token_with_flags.set_has_separator(true);
token_with_flags.set_start(40);
assert_eq!(token_with_flags.start(), 40);
assert!(token_with_flags.is_on_new_line());
assert!(token_with_flags.escaped());
assert!(token_with_flags.lone_surrogates());
assert!(token_with_flags.has_separator());
let mut token_with_flags2 = Token::default();
token_with_flags2.set_kind(Kind::Str);
token_with_flags2.set_start(50);
token_with_flags2.set_end(52);
token_with_flags2.set_is_on_new_line(true);
token_with_flags2.set_lone_surrogates(true);
token_with_flags2.set_has_separator(true);
token_with_flags2.set_escaped(true);
assert_eq!(token_with_flags2.start(), 50);
assert!(token_with_flags2.is_on_new_line());
assert!(token_with_flags2.escaped());
assert!(token_with_flags2.lone_surrogates());
assert!(token_with_flags2.has_separator());
token_with_flags2.set_escaped(false);
assert!(!token_with_flags2.escaped());
assert!(token_with_flags2.is_on_new_line()); assert!(token_with_flags2.lone_surrogates()); assert!(token_with_flags2.has_separator());
let mut token_flags_test_newline = Token::default();
token_flags_test_newline.set_kind(Kind::Str);
token_flags_test_newline.set_start(60);
token_flags_test_newline.set_end(62);
token_flags_test_newline.set_escaped(true);
token_flags_test_newline.set_lone_surrogates(true);
token_flags_test_newline.set_has_separator(true);
token_flags_test_newline.set_is_on_new_line(true);
assert!(token_flags_test_newline.is_on_new_line());
assert_eq!(token_flags_test_newline.start(), 60);
assert!(token_flags_test_newline.escaped());
assert!(token_flags_test_newline.lone_surrogates());
assert!(token_flags_test_newline.has_separator());
token_flags_test_newline.set_is_on_new_line(false);
assert!(!token_flags_test_newline.is_on_new_line());
assert!(token_flags_test_newline.escaped());
assert!(token_flags_test_newline.lone_surrogates());
assert!(token_flags_test_newline.has_separator());
let mut token_flags_test_lone_surrogates = Token::default();
token_flags_test_lone_surrogates.set_kind(Kind::Str);
token_flags_test_lone_surrogates.set_start(70);
token_flags_test_lone_surrogates.set_end(72);
token_flags_test_lone_surrogates.set_is_on_new_line(true);
token_flags_test_lone_surrogates.set_escaped(true);
token_flags_test_lone_surrogates.set_has_separator(true);
token_flags_test_lone_surrogates.set_lone_surrogates(true);
assert!(token_flags_test_lone_surrogates.lone_surrogates());
assert_eq!(token_flags_test_lone_surrogates.start(), 70);
assert!(token_flags_test_lone_surrogates.is_on_new_line());
assert!(token_flags_test_lone_surrogates.escaped());
assert!(token_flags_test_lone_surrogates.has_separator());
token_flags_test_lone_surrogates.set_lone_surrogates(false);
assert!(!token_flags_test_lone_surrogates.lone_surrogates());
assert!(token_flags_test_lone_surrogates.is_on_new_line());
assert!(token_flags_test_lone_surrogates.escaped());
assert!(token_flags_test_lone_surrogates.has_separator());
}
#[test]
fn is_on_new_line() {
let mut token = Token::default();
assert!(!token.is_on_new_line());
token.set_is_on_new_line(true);
assert!(token.is_on_new_line());
token.set_is_on_new_line(false);
assert!(!token.is_on_new_line());
}
#[test]
fn escaped() {
let mut token = Token::default();
assert!(!token.escaped());
token.set_escaped(true);
assert!(token.escaped());
token.set_escaped(false);
assert!(!token.escaped());
}
#[test]
fn lone_surrogates() {
let mut token = Token::default();
assert!(!token.lone_surrogates());
token.set_lone_surrogates(true);
assert!(token.lone_surrogates());
token.set_lone_surrogates(false);
assert!(!token.lone_surrogates());
}
#[test]
fn has_separator() {
let mut token = Token::default();
assert!(!token.has_separator());
token.set_has_separator(true);
assert!(token.has_separator());
token.set_has_separator(false);
assert!(!token.has_separator());
}
}