use smol_str::SmolStr;
use std::ops::{Deref, DerefMut};
use crate::{
error::ParseError,
state::State,
token::{Comment, Token, TokenType, Trivia},
utils::can_be_identifier,
};
#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Lexer<'a> {
pub(crate) input: &'a str,
pub(crate) chars: Vec<char>,
pub(crate) errors: Vec<ParseError>,
pub(crate) state: State,
}
impl<'a> Lexer<'a> {
#[inline]
pub fn new(input: &'a str) -> Self {
Self::default().with_input(input)
}
#[inline]
pub fn with_input(mut self, input: &'a str) -> Self {
self.set_input(input);
self
}
#[inline]
pub fn set_input(&mut self, input: &'a str) {
self.input = input;
self.chars = input.chars().collect();
self.last_trivia = self.skip_trivia();
}
#[inline]
pub fn save_state(&self) -> State {
self.state.clone()
}
#[inline]
pub fn set_state(&mut self, state: State) {
self.state = state;
}
pub fn next_token(&mut self) -> Token {
if !self.errors.is_empty() {
let error = self.errors.remove(0);
let start = error.start();
return TokenType::Error(error).into_token(
start,
self.lexer_position,
Vec::new(),
Vec::new(),
);
}
let start = self.lexer_position;
let token_type = TokenType::try_lex(self).unwrap_or(TokenType::EndOfFile);
let trivia = self.skip_trivia();
let leading_trivia = self.last_trivia.clone();
let trailing_trivia = trivia.clone();
self.last_trivia = trivia;
token_type.into_token(start, self.lexer_position, leading_trivia, trailing_trivia)
}
#[inline]
pub fn current_char(&self) -> Option<char> {
self.chars.get(self.position).copied()
}
#[inline]
pub fn next_char(&self) -> Option<char> {
self.chars.get(self.position + 1).copied()
}
#[inline]
pub fn consume(&mut self, character: char) -> bool {
if self.current_char() == Some(character) {
self.increment_position_by_char(character);
true
} else {
false
}
}
#[inline]
pub fn consume_with_next(&mut self, character: char) -> bool {
if self.next_char() == Some(character) {
let current_char = self.current_char().unwrap();
self.increment_position_by_char(current_char);
self.increment_position_by_char(character);
true
} else {
false
}
}
pub fn consume_identifier(&mut self) -> SmolStr {
let start = self.byte_position;
while let Some(character) = self.current_char() {
if can_be_identifier(character) {
self.increment_position_by_char(character);
} else {
break;
}
}
self.input[start..self.byte_position].into()
}
pub fn skip_trivia(&mut self) -> Vec<Trivia> {
let mut trivia = Vec::new();
loop {
let spaces = self.skip_whitespace();
if !spaces.is_empty() {
trivia.push(Trivia::Spaces(spaces));
} else if self.current_char() == Some('-') && self.consume_with_next('-') {
trivia.push(Trivia::Comment(Comment::try_lex(self).unwrap()));
} else {
break;
}
}
trivia
}
pub fn skip_whitespace(&mut self) -> SmolStr {
let start = self.byte_position;
while let Some(character) = self.current_char() {
if character.is_whitespace() {
self.increment_position_by_char(character);
} else {
break;
}
}
(start != self.byte_position)
.then(|| self.input[start..self.byte_position].into())
.unwrap_or_default()
}
}
impl Deref for Lexer<'_> {
type Target = State;
fn deref(&self) -> &Self::Target {
&self.state
}
}
impl DerefMut for Lexer<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.state
}
}
pub trait Lexable: Sized {
fn try_lex(lexer: &mut Lexer) -> Option<Self>;
}