use logos::Logos;
use super::Span;
use super::parser::ParserToken;
use super::tree::SyntaxKind;
pub mod v1;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TokenSet(u128);
impl TokenSet {
pub const EMPTY: Self = Self(0);
pub const fn new(tokens: &[u8]) -> Self {
let mut bits = 0u128;
let mut i = 0;
while i < tokens.len() {
bits |= Self::mask(tokens[i]);
i += 1;
}
Self(bits)
}
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
pub const fn without(self, other: Self) -> Self {
Self(self.0 & !other.0)
}
pub const fn contains(&self, token: u8) -> bool {
self.0 & Self::mask(token) != 0
}
pub const fn count(&self) -> usize {
self.0.count_ones() as usize
}
pub fn iter(&self) -> impl Iterator<Item = u8> + use<> {
let mut bits = self.0;
std::iter::from_fn(move || {
if bits == 0 {
return None;
}
let token = u8::try_from(bits.trailing_zeros())
.expect("the maximum token value should be less than 128");
bits ^= bits & bits.overflowing_neg().0;
Some(token)
})
}
const fn mask(token: u8) -> u128 {
1u128 << (token as usize)
}
}
#[derive(Logos, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum PreambleToken {
#[regex(r"[ \t\r\n]+")]
Whitespace,
#[regex(r"#[^\r\n]*")]
Comment,
#[token("version")]
VersionKeyword,
#[regex("[^ \t\r\n#]")]
Any,
MAX,
}
const _: () = assert!(PreambleToken::MAX as u8 <= 128);
impl ParserToken<'_> for PreambleToken {
fn into_syntax(self) -> SyntaxKind {
match self {
Self::Whitespace => SyntaxKind::Whitespace,
Self::Comment => SyntaxKind::Comment,
Self::VersionKeyword => SyntaxKind::VersionKeyword,
Self::Any | Self::MAX => unreachable!(),
}
}
fn into_raw(self) -> u8 {
self as u8
}
fn from_raw(token: u8) -> Self {
assert!(token < Self::MAX as u8, "invalid token value");
unsafe { std::mem::transmute(token) }
}
fn describe(self) -> &'static str {
match self {
Self::Whitespace => "whitespace",
Self::Comment => "comment",
Self::VersionKeyword => "`version` keyword",
Self::Any | Self::MAX => unreachable!(),
}
}
fn is_trivia(self) -> bool {
matches!(self, Self::Whitespace | Self::Comment)
}
}
#[derive(Logos, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum VersionStatementToken {
#[regex(r"[ \t\r\n]+")]
Whitespace,
#[regex(r"#[^\r\n]*")]
Comment,
#[regex(r"[a-zA-Z0-9][a-zA-Z0-9.\-]*")]
Version,
MAX,
}
const _: () = assert!(VersionStatementToken::MAX as u8 <= 128);
impl ParserToken<'_> for VersionStatementToken {
fn into_syntax(self) -> SyntaxKind {
match self {
Self::Whitespace => SyntaxKind::Whitespace,
Self::Comment => SyntaxKind::Comment,
Self::Version => SyntaxKind::Version,
Self::MAX => unreachable!(),
}
}
fn into_raw(self) -> u8 {
self as u8
}
fn from_raw(token: u8) -> Self {
assert!(token < Self::MAX as u8, "invalid token value");
unsafe { std::mem::transmute(token) }
}
fn describe(self) -> &'static str {
match self {
Self::Whitespace => "whitespace",
Self::Comment => "comment",
Self::Version => "version",
Self::MAX => unreachable!(),
}
}
fn is_trivia(self) -> bool {
matches!(self, Self::Whitespace | Self::Comment)
}
}
pub type LexerResult<T> = Result<T, ()>;
#[derive(Debug, Clone, Copy)]
struct Peeked<T> {
result: LexerResult<T>,
span: Span,
offset: usize,
}
#[allow(missing_debug_implementations)]
#[derive(Clone)]
pub struct Lexer<'a, T>
where
T: Logos<'a, Extras = ()>,
{
lexer: logos::Lexer<'a, T>,
peeked: Option<Peeked<T>>,
}
impl<'a, T> Lexer<'a, T>
where
T: Logos<'a, Source = str, Error = (), Extras = ()> + Copy,
{
pub fn new(source: &'a str) -> Self
where
T::Extras: Default,
{
Self {
lexer: T::lexer(source),
peeked: None,
}
}
pub fn source(&self, span: Span) -> &'a str {
&self.lexer.source()[span.start()..span.end()]
}
pub fn source_len(&self) -> usize {
self.lexer.source().len()
}
pub fn span(&self) -> Span {
self.lexer.span().into()
}
pub fn peek(&mut self) -> Option<(LexerResult<T>, Span)> {
if self.peeked.is_none() {
let offset = self.lexer.span().start;
self.peeked = self.lexer.next().map(|r| Peeked {
result: r,
span: self.lexer.span().into(),
offset,
});
}
self.peeked.map(|p| (p.result, p.span))
}
pub fn morph<T2>(self) -> Lexer<'a, T2>
where
T2: Logos<'a, Source = str, Error = (), Extras = ()> + Copy,
{
let lexer = match self.peeked {
Some(peeked) => {
let mut lexer = T2::lexer(self.lexer.source());
if peeked.offset > 0 {
lexer.bump(peeked.offset);
lexer.next();
}
lexer
}
None => self.lexer.morph(),
};
Lexer {
lexer,
peeked: None,
}
}
pub fn consume_remainder(&mut self) -> Option<Span> {
if let Some(peeked) = self.peeked.take() {
self.lexer = T::lexer(self.lexer.source());
if peeked.offset > 0 {
self.lexer.bump(peeked.offset);
self.lexer.next();
}
}
self.lexer.next();
self.lexer.bump(self.lexer.remainder().len());
let span = self.lexer.span();
assert!(self.next().is_none(), "lexer should be completed");
if span.is_empty() {
None
} else {
Some(span.into())
}
}
}
impl<'a, T> Iterator for Lexer<'a, T>
where
T: Logos<'a, Error = (), Extras = ()> + Copy,
{
type Item = (LexerResult<T>, Span);
fn next(&mut self) -> Option<Self::Item> {
if let Some(peeked) = self.peeked.take() {
return Some((peeked.result, peeked.span));
}
self.lexer.next().map(|r| (r, self.lexer.span().into()))
}
}
#[cfg(test)]
mod test {
use pretty_assertions::assert_eq;
use super::*;
pub(crate) fn map<T>(
(t, s): (LexerResult<T>, Span),
) -> (LexerResult<T>, std::ops::Range<usize>) {
(t, s.start()..s.end())
}
#[test]
fn test_version_1_0() {
let mut lexer = Lexer::<PreambleToken>::new(
"
# Test for 1.0 documents
version 1.0",
);
assert_eq!(
lexer.next().map(map).unwrap(),
(Ok(PreambleToken::Whitespace), 0..1)
);
assert_eq!(
lexer.next().map(map).unwrap(),
(Ok(PreambleToken::Comment), 1..25),
);
assert_eq!(
lexer.next().map(map).unwrap(),
(Ok(PreambleToken::Whitespace), 25..26),
);
assert_eq!(
lexer.next().map(map).unwrap(),
(Ok(PreambleToken::VersionKeyword), 26..33),
);
let mut lexer: Lexer<'_, VersionStatementToken> = lexer.morph();
assert_eq!(
lexer.next().map(map).unwrap(),
(Ok(VersionStatementToken::Whitespace), 33..34),
);
assert_eq!(
lexer.next().map(map).unwrap(),
(Ok(VersionStatementToken::Version), 34..37)
);
}
#[test]
fn test_version_1_1() {
let mut lexer = Lexer::<PreambleToken>::new(
"
# Test for 1.1 documents
version 1.1",
);
assert_eq!(
lexer.next().map(map).unwrap(),
(Ok(PreambleToken::Whitespace), 0..1)
);
assert_eq!(
lexer.next().map(map).unwrap(),
(Ok(PreambleToken::Comment), 1..25)
);
assert_eq!(
lexer.next().map(map).unwrap(),
(Ok(PreambleToken::Whitespace), 25..26)
);
assert_eq!(
lexer.next().map(map).unwrap(),
(Ok(PreambleToken::VersionKeyword), 26..33)
);
let mut lexer: Lexer<'_, VersionStatementToken> = lexer.morph();
assert_eq!(
lexer.next().map(map).unwrap(),
(Ok(VersionStatementToken::Whitespace), 33..34)
);
assert_eq!(
lexer.next().map(map).unwrap(),
(Ok(VersionStatementToken::Version), 34..37)
);
}
#[test]
fn test_version_draft3() {
let mut lexer = Lexer::<PreambleToken>::new(
"
# Test for draft-3 documents
version draft-3",
);
assert_eq!(
lexer.next().map(map).unwrap(),
(Ok(PreambleToken::Whitespace), 0..1)
);
assert_eq!(
lexer.next().map(map).unwrap(),
(Ok(PreambleToken::Comment), 1..29)
);
assert_eq!(
lexer.next().map(map).unwrap(),
(Ok(PreambleToken::Whitespace), 29..30)
);
assert_eq!(
lexer.next().map(map).unwrap(),
(Ok(PreambleToken::VersionKeyword), 30..37)
);
let mut lexer: Lexer<'_, VersionStatementToken> = lexer.morph();
assert_eq!(
lexer.next().map(map).unwrap(),
(Ok(VersionStatementToken::Whitespace), 37..38)
);
assert_eq!(
lexer.next().map(map).unwrap(),
(Ok(VersionStatementToken::Version), 38..45)
);
}
}