use std::ops::Range;
use super::{Logos, Source};
use super::internal::LexerInternal;
pub type Lexicon<Lexer> = [Option<fn(&mut Lexer)>; 256];
pub struct Lexer<Token: Logos, Source> {
pub source: Source,
pub token: Token,
pub extras: Token::Extras,
token_start: usize,
token_end: usize,
}
macro_rules! unroll {
($( $code:tt )*) => (
$( $code )*
$( $code )*
$( $code )*
$( $code )*
loop {
$( $code )*
}
)
}
impl<'source, Token, Source> Lexer<Token, Source>
where
Token: self::Logos,
Source: self::Source<'source>,
{
pub fn new(source: Source) -> Self {
let mut lex = Lexer {
source,
token: Token::ERROR,
extras: Default::default(),
token_start: 0,
token_end: 0,
};
lex.advance();
lex
}
pub fn advance(&mut self) {
let mut ch;
self.extras.on_advance();
unroll! {
ch = self.read();
if let Some(handler) = Token::lexicon()[ch as usize] {
self.token_start = self.token_end;
return handler(self);
}
self.extras.on_whitespace(ch);
self.bump();
}
}
pub fn range(&self) -> Range<usize> {
self.token_start .. self.token_end
}
pub fn slice(&self) -> Source::Slice {
unsafe { self.source.slice_unchecked(self.range()) }
}
}
pub trait Extras: Sized + Default {
fn on_advance(&mut self) {}
fn on_whitespace(&mut self, _byte: u8) {}
}
impl Extras for () { }
#[doc(hidden)]
impl<'source, Token, Source> LexerInternal for Lexer<Token, Source>
where
Token: self::Logos,
Source: self::Source<'source>,
{
fn read(&self) -> u8 {
unsafe { self.source.read(self.token_end) }
}
fn next(&mut self) -> u8 {
self.bump();
self.read()
}
fn bump(&mut self) {
debug_assert!(self.token_end + 1 <= self.source.len(), "Bumping out of bounds!");
self.token_end += 1;
}
}