use std::{ops::Range, sync::Arc};
use crate::TokenKind;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TokenSpan {
pub start: usize,
pub end: usize,
}
impl TokenSpan {
pub const fn new(start: usize, end: usize) -> Self {
Self { start, end }
}
pub fn try_new(start: usize, end: usize) -> Result<Self, TokenSpanError> {
if end < start {
return Err(TokenSpanError::EndBeforeStart { start, end });
}
Ok(Self { start, end })
}
pub const fn len(self) -> usize {
self.end.saturating_sub(self.start)
}
pub const fn is_empty(self) -> bool {
self.len() == 0
}
pub const fn range(self) -> Range<usize> {
self.start..self.end
}
pub const fn contains(self, offset: usize) -> bool {
self.start <= offset && offset < self.end
}
pub const fn touches(self, offset: usize) -> bool {
self.start <= offset && offset <= self.end
}
pub const fn overlaps(self, other: Self) -> bool {
!self.is_empty() && !other.is_empty() && self.start < other.end && other.start < self.end
}
pub const fn cover(self, other: Self) -> Self {
Self { start: min_usize(self.start, other.start), end: max_usize(self.end, other.end) }
}
}
const fn min_usize(left: usize, right: usize) -> usize {
if left <= right { left } else { right }
}
const fn max_usize(left: usize, right: usize) -> usize {
if left >= right { left } else { right }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TokenSpanError {
EndBeforeStart {
start: usize,
end: usize,
},
EmptySpanNotAllowed {
kind: TokenKind,
at: usize,
},
}
impl std::fmt::Display for TokenSpanError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::EndBeforeStart { start, end } => {
write!(f, "token span invariant violated: end ({end}) < start ({start})")
}
Self::EmptySpanNotAllowed { kind, at } => {
write!(f, "empty span not allowed for token kind {kind:?} at byte {at}")
}
}
}
}
impl std::error::Error for TokenSpanError {}
#[inline]
const fn allows_empty_span(kind: TokenKind) -> bool {
matches!(kind, TokenKind::Eof | TokenKind::Unknown)
}
#[inline]
fn validate_non_empty_span(
kind: TokenKind,
start: usize,
is_empty: bool,
) -> Result<(), TokenSpanError> {
if is_empty && !allows_empty_span(kind) {
return Err(TokenSpanError::EmptySpanNotAllowed { kind, at: start });
}
Ok(())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TokenRef<'src> {
pub kind: TokenKind,
pub text: &'src str,
pub start: usize,
pub end: usize,
}
impl<'src> TokenRef<'src> {
pub fn new(kind: TokenKind, text: &'src str, start: usize, end: usize) -> Self {
Self { kind, text, start, end }
}
pub fn try_new(
kind: TokenKind,
text: &'src str,
start: usize,
end: usize,
) -> Result<Self, TokenSpanError> {
let span = TokenSpan::try_new(start, end)?;
Ok(Self { kind, text, start: span.start, end: span.end })
}
pub fn new_checked(
kind: TokenKind,
text: &'src str,
start: usize,
end: usize,
) -> Result<Self, TokenSpanError> {
let token = Self::try_new(kind, text, start, end)?;
validate_non_empty_span(token.kind, token.start, token.is_empty())?;
Ok(token)
}
pub fn len(self) -> usize {
TokenSpan::new(self.start, self.end).len()
}
pub fn is_empty(self) -> bool {
self.len() == 0
}
pub fn span(self) -> (usize, usize) {
(self.start, self.end)
}
pub fn display_name(self) -> &'static str {
self.kind.display_name()
}
pub fn to_owned_token(self) -> Token {
Token::new(self.kind, self.text, self.start, self.end)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Token {
pub kind: TokenKind,
pub text: Arc<str>,
pub start: usize,
pub end: usize,
}
impl Token {
pub fn new(kind: TokenKind, text: impl Into<Arc<str>>, start: usize, end: usize) -> Self {
Token { kind, text: text.into(), start, end }
}
pub fn try_new(
kind: TokenKind,
text: impl Into<Arc<str>>,
start: usize,
end: usize,
) -> Result<Self, TokenSpanError> {
let span = TokenSpan::try_new(start, end)?;
Ok(Self { kind, text: text.into(), start: span.start, end: span.end })
}
pub fn new_checked(
kind: TokenKind,
text: impl Into<Arc<str>>,
start: usize,
end: usize,
) -> Result<Self, TokenSpanError> {
let token = Self::try_new(kind, text, start, end)?;
validate_non_empty_span(token.kind, token.start, token.is_empty())?;
Ok(token)
}
pub fn eof_at(pos: usize) -> Self {
Self::new(TokenKind::Eof, "", pos, pos)
}
pub fn unknown_at(text: impl Into<Arc<str>>, start: usize, end: usize) -> Self {
let bounded_end = end.max(start);
Self::new(TokenKind::Unknown, text, start, bounded_end)
}
pub fn span(&self) -> TokenSpan {
TokenSpan::new(self.start, self.end)
}
pub fn range(&self) -> Range<usize> {
self.span().range()
}
pub fn with_span(&self, start: usize, end: usize) -> Result<Self, TokenSpanError> {
Self::new_checked(self.kind, self.text.clone(), start, end)
}
pub fn with_kind(&self, kind: TokenKind) -> Self {
Self::new(kind, self.text.clone(), self.start, self.end)
}
pub fn len(&self) -> usize {
self.span().len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn display_name(&self) -> &'static str {
self.kind.display_name()
}
pub fn as_ref_token(&self) -> TokenRef<'_> {
TokenRef { kind: self.kind, text: self.text.as_ref(), start: self.start, end: self.end }
}
}
impl From<TokenRef<'_>> for Token {
fn from(value: TokenRef<'_>) -> Self {
value.to_owned_token()
}
}