mod associated_whitespace_rules;
mod comment_style;
mod constants;
mod cow;
mod cursor;
#[cfg(feature = "dynamic-atoms")]
mod dyn_atom_registry;
mod empty_atom_set;
mod feature;
mod kind;
mod kindset;
mod pairwise;
mod private;
mod quote_style;
mod scan;
mod small_str_buf;
mod source_cursor;
mod syntax;
mod token;
mod whitespace_style;
pub type BasicLexer<'a> = Lexer<'a>;
use atom_set::DynAtomSet;
pub use associated_whitespace_rules::AssociatedWhitespaceRules;
pub use comment_style::CommentStyle;
pub use cow::CowStr;
pub type Cursor = source_tools::Cursor<Token>;
#[cfg(feature = "dynamic-atoms")]
pub use dyn_atom_registry::{Atom, DynAtomRegistry, RegisteredAtomSet};
pub use empty_atom_set::EmptyAtomSet;
pub use feature::Feature;
pub use kind::Kind;
pub use kindset::KindSet;
pub use pairwise::PairWise;
pub use quote_style::QuoteStyle;
pub use source_cursor::SourceCursor;
pub use source_tools::{LineIndex, SourceOffset, Span, ToSpan};
pub use token::Token;
pub use whitespace_style::Whitespace;
#[derive(Debug, Clone)]
pub struct Lexer<'a> {
source: &'a str,
offset: SourceOffset,
token: Token,
features: Feature,
atoms: &'static dyn DynAtomSet,
}
impl<'a> Lexer<'a> {
#[inline]
pub fn new(atoms: &'static dyn DynAtomSet, source: &'a str) -> Self {
Self { source, offset: SourceOffset::default(), token: Token::default(), features: Feature::default(), atoms }
}
#[inline]
pub fn new_with_features(atoms: &'static dyn DynAtomSet, source: &'a str, features: Feature) -> Self {
Self { source, features, offset: SourceOffset::default(), token: Token::default(), atoms }
}
#[inline(always)]
pub fn source(&self) -> &'a str {
self.source
}
pub fn at_end(&self) -> bool {
self.offset.0 as usize == self.source.len()
}
#[inline(always)]
pub const fn offset(&self) -> SourceOffset {
self.offset
}
#[inline(always)]
pub fn checkpoint(&self) -> Cursor {
Cursor::new(self.offset(), self.token)
}
pub fn rewind(&mut self, cursor: Cursor) {
debug_assert!(cursor.offset() <= self.offset());
self.offset = cursor.offset();
self.token = cursor.token();
}
pub fn hop(&mut self, cursor: Cursor) {
debug_assert!(cursor.offset().0 as usize >= (self.offset.0 + self.token.len()) as usize);
self.offset = cursor.offset();
self.token = cursor.token();
}
pub fn advance(&mut self) -> Token {
self.token = self.read_next_token(self.offset.0);
self.offset.0 += self.token.len();
self.token
}
}
impl<'a> Iterator for Lexer<'a> {
type Item = Cursor;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.offset.0 as usize >= self.source.len() {
return None;
}
let offset = self.offset;
let token = self.advance();
if token.kind() == Kind::Eof { None } else { Some(token.with_cursor(offset)) }
}
}
#[test]
fn size_test() {
assert_eq!(::std::mem::size_of::<Lexer>(), 48);
}
#[test]
fn test_smallstr_buf_overflow() {
let mut source = String::from("a");
source.extend(std::iter::repeat_n('\0', 86));
let lexer = Lexer::new(&EmptyAtomSet::ATOMS, &source);
for cursor in lexer {
let _ = cursor;
}
}
#[cfg(test)]
mod iterator_tests {
use super::*;
#[test]
fn test_lexer_iterator_basic() {
let lexer = Lexer::new(&EmptyAtomSet::ATOMS, "foo bar");
let cursors: Vec<_> = lexer.collect();
assert_eq!(cursors.len(), 3); assert_eq!(cursors[0], Kind::Ident);
assert_eq!(cursors[1], Kind::Whitespace);
assert_eq!(cursors[2], Kind::Ident);
}
#[test]
fn test_lexer_iterator_empty() {
let lexer = Lexer::new(&EmptyAtomSet::ATOMS, "");
let cursors: Vec<_> = lexer.collect();
assert_eq!(cursors.len(), 0);
}
#[test]
fn test_lexer_iterator_equivalence() {
let source = "width: 1px";
let lexer = Lexer::new(&EmptyAtomSet::ATOMS, source);
let cursors: Vec<_> = lexer.collect();
let mut lexer = Lexer::new(&EmptyAtomSet::ATOMS, source);
let mut manual_cursors = Vec::new();
while !lexer.at_end() {
let offset = lexer.offset();
let token = lexer.advance();
if token.kind() != Kind::Eof {
manual_cursors.push(token.with_cursor(offset));
}
}
assert_eq!(cursors.len(), manual_cursors.len());
for (c1, c2) in cursors.iter().zip(manual_cursors.iter()) {
assert_eq!(c1.token().kind(), c2.token().kind());
assert_eq!(c1.offset(), c2.offset());
}
}
#[test]
fn test_lexer_iterator_clone() {
let source = "foo bar baz";
let mut lexer = Lexer::new(&EmptyAtomSet::ATOMS, source);
let first = lexer.next();
assert!(first.is_some());
assert_eq!(first.unwrap(), Kind::Ident);
let lexer_clone = lexer.clone();
let cursors1: Vec<_> = lexer.collect();
let cursors2: Vec<_> = lexer_clone.collect();
assert_eq!(cursors1.len(), cursors2.len());
for (c1, c2) in cursors1.iter().zip(cursors2.iter()) {
assert_eq!(c1.token().kind(), c2.token().kind());
assert_eq!(c1.offset(), c2.offset());
}
}
}