use std::fmt::Formatter;
use std::fmt::{Debug, Display};
use std::sync::LazyLock;
pub const TOKEN_INVALID_TYPE: i32 = 0;
pub const TOKEN_EPSILON: i32 = -2;
pub const TOKEN_MIN_USER_TOKEN_TYPE: i32 = 1;
pub const TOKEN_EOF: i32 = crate::int_stream::EOF;
pub const TOKEN_DEFAULT_CHANNEL: i32 = 0;
pub const TOKEN_HIDDEN_CHANNEL: i32 = 1;
pub const HIDDEN: i32 = TOKEN_HIDDEN_CHANNEL;
#[allow(missing_docs)]
pub trait Token: Debug + Display {
fn get_text(&self) -> &str;
fn get_token_type(&self) -> i32;
fn get_line(&self) -> u32;
fn get_char_position_in_line(&self) -> i32;
fn get_channel(&self) -> i32;
fn get_token_index(&self) -> isize;
fn get_start_index(&self) -> isize;
fn get_stop_index(&self) -> isize;
fn set_text(&mut self, _text: String);
fn set_type(&mut self, _ttype: i32);
fn set_line(&mut self, _line: u32);
fn set_char_position_in_line(&mut self, _pos: i32);
fn set_channel(&mut self, _channel: i32);
fn set_token_index(&mut self, _v: isize);
}
pub type OwningToken = TokenImpl<'static, String>;
pub type CommonToken<'input> = TokenImpl<'input, &'input str>;
pub trait TextType<'input>: AsRef<str> + Debug + Display {
fn set_text(&mut self, _text: String);
}
impl<'input> TextType<'input> for &'input str {
fn set_text(&mut self, _text: String) {
panic!("Cannot set text of &str, it is immutable");
}
}
impl<'input> TextType<'input> for String {
fn set_text(&mut self, text: String) {
*self = text;
}
}
const TOKEN_TYPE_BITS: u32 = 22;
const TOKEN_CHANNEL_BITS: u32 = 32 - TOKEN_TYPE_BITS;
const TOKEN_TYPE_MASK: i32 = (1 << TOKEN_TYPE_BITS) - 1;
const TOKEN_CHANNEL_MASK: i32 = !TOKEN_TYPE_MASK;
const TOKEN_TYPE_MAX: i32 = (TOKEN_TYPE_MASK as u32 >> 1) as i32;
const TOKEN_TYPE_MIN: i32 = -TOKEN_TYPE_MAX;
const TOKEN_CHANNEL_MAX: i32 = 1i32 << (TOKEN_CHANNEL_BITS - 1);
const TOKEN_CHANNEL_MIN: i32 = -TOKEN_CHANNEL_MAX;
#[derive(Clone, Debug)]
#[allow(missing_docs)]
pub struct TokenImpl<'input, T: TextType<'input>> {
type_and_channel: i32,
pub start: i32,
pub stop: i32,
pub token_index: i32,
pub line: u32,
pub column: i32,
pub text: T,
_input: std::marker::PhantomData<&'input str>,
}
impl<'input, T: TextType<'input>> TokenImpl<'input, T> {
#[allow(clippy::too_many_arguments)]
pub fn new(
token_type: i32,
channel: i32,
start: i32,
stop: i32,
token_index: i32,
line: u32,
column: i32,
text: T,
) -> Self {
Self {
type_and_channel: (channel << TOKEN_TYPE_BITS) | (token_type & TOKEN_TYPE_MASK),
start,
stop,
token_index,
line,
column,
text,
_input: std::marker::PhantomData,
}
}
}
impl<'input, T: TextType<'input>> Display for TokenImpl<'input, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let txt = self.get_text();
let txt = txt.replace("\n", "\\n");
let txt = txt.replace("\r", "\\r");
let txt = txt.replace("\t", "\\t");
f.write_fmt(format_args!(
"[@{},{}:{}='{}',<{}>{},{}:{}]",
self.get_token_index(),
self.start,
self.stop,
txt,
self.get_token_type(),
if self.get_channel() > 0 {
",channel=".to_string() + self.get_channel().to_string().as_str()
} else {
String::new()
},
self.line,
self.column
))
}
}
impl<'input, T: TextType<'input>> Token for TokenImpl<'input, T> {
#[inline(always)]
fn get_token_type(&self) -> i32 {
(self.type_and_channel << TOKEN_CHANNEL_BITS) >> TOKEN_CHANNEL_BITS
}
#[inline(always)]
fn get_channel(&self) -> i32 {
self.type_and_channel >> TOKEN_TYPE_BITS
}
#[inline(always)]
fn get_start_index(&self) -> isize {
self.start as isize
}
#[inline(always)]
fn get_stop_index(&self) -> isize {
self.stop as isize
}
#[inline(always)]
fn get_line(&self) -> u32 {
self.line
}
#[inline(always)]
fn get_char_position_in_line(&self) -> i32 {
self.column
}
#[inline(always)]
fn get_text(&self) -> &str {
if self.get_token_type() == TOKEN_EOF {
"<EOF>"
} else {
self.text.as_ref()
}
}
#[inline(always)]
fn get_token_index(&self) -> isize {
self.token_index as isize
}
fn set_token_index(&mut self, _v: isize) {
self.token_index = _v as i32;
}
fn set_text(&mut self, text: String) {
TextType::set_text(&mut self.text, text);
}
#[inline]
fn set_type(&mut self, ttype: i32) {
debug_assert!(
(TOKEN_TYPE_MIN..=TOKEN_TYPE_MAX).contains(&ttype),
"Token type {} is out of range ({}..={})",
ttype,
TOKEN_TYPE_MIN,
TOKEN_TYPE_MAX
);
self.type_and_channel =
(self.type_and_channel & !(TOKEN_TYPE_MASK)) | (ttype & TOKEN_TYPE_MASK);
}
fn set_line(&mut self, _line: u32) {
self.line = _line;
}
fn set_char_position_in_line(&mut self, _pos: i32) {
self.column = _pos;
}
#[inline]
fn set_channel(&mut self, channel: i32) {
debug_assert!(
(TOKEN_CHANNEL_MIN..=TOKEN_CHANNEL_MAX).contains(&channel),
"Token channel {} is out of range ({}..={})",
channel,
TOKEN_CHANNEL_MIN,
TOKEN_CHANNEL_MAX
);
self.type_and_channel =
(self.type_and_channel & !(TOKEN_CHANNEL_MASK)) | (channel << TOKEN_TYPE_BITS);
}
}
impl Default for &'_ CommonToken<'_> {
fn default() -> Self {
&INVALID_COMMON
}
}
impl From<&dyn Token> for OwningToken {
fn from(value: &dyn Token) -> Self {
OwningToken::new(
value.get_token_type(),
value.get_channel(),
value.get_start_index() as i32,
value.get_stop_index() as i32,
value.get_token_index() as i32,
value.get_line(),
value.get_char_position_in_line(),
value.get_text().to_string(),
)
}
}
pub(crate) static INVALID_COMMON: LazyLock<Box<CommonToken<'static>>> = LazyLock::new(|| {
Box::new(CommonToken::new(
TOKEN_INVALID_TYPE,
0,
-1,
-1,
-1,
0,
0,
"<invalid>",
))
});