#[cfg(test)]
mod tests;
use self::error::Error;
use self::token::lexeme::comment::Comment;
use self::token::lexeme::identifier::Identifier;
use self::token::lexeme::literal::integer::Integer as IntegerLiteral;
use self::token::lexeme::literal::string::String as StringLiteral;
use self::token::lexeme::symbol::Symbol;
use self::token::lexeme::Lexeme;
use self::token::location::Location;
use self::token::Token;
pub mod error;
pub mod token;
pub struct Lexer {
input: String,
offset: u32,
location: Location,
peeked: Option<Token>,
}
impl Lexer {
pub fn new(mut input: String) -> Self {
input.push('\n');
Self {
input,
offset: 0,
location: Location::default(),
peeked: None,
}
}
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Result<Token, Error> {
if let Some(peeked) = self.peeked.take() {
return Ok(peeked);
}
while self.offset
< self
.input
.len()
.try_into()
.map_err(|_| Error::InvalidLexeme {
location: self.location,
sequence: Default::default(),
})?
{
let input = &self.input[(self.offset as usize)..];
if input.starts_with(|character| char::is_ascii_whitespace(&character)) {
if input.starts_with('\n') {
self.location.line += 1;
self.location.column = 1;
} else if !input.starts_with('\r') {
self.location.column += 1;
}
self.offset += 1;
continue;
}
if let Some(token) = Comment::parse(input) {
self.offset += token.length;
self.location
.shift_down(token.location.line, token.location.column);
continue;
}
if let Some(mut token) = StringLiteral::parse(input) {
token.location = self.location;
self.offset += token.length;
self.location.shift_right(token.length);
return Ok(token);
}
if let Some(mut token) = IntegerLiteral::parse(input) {
token.location = self.location;
self.offset += token.length;
self.location.shift_right(token.length);
return Ok(token);
}
if let Some(mut token) = Identifier::parse(input) {
token.location = self.location;
self.offset += token.length;
self.location.shift_right(token.length);
return Ok(token);
}
if let Some(mut token) = Symbol::parse(input) {
token.location = self.location;
self.offset += token.length;
self.location.shift_right(token.length);
return Ok(token);
}
let end = self.input[(self.offset as usize)..]
.find(char::is_whitespace)
.unwrap_or(self.input.len());
return Err(Error::InvalidLexeme {
location: self.location,
sequence: self.input[(self.offset as usize)..(self.offset as usize) + end]
.to_owned(),
});
}
Ok(Token::new(self.location, Lexeme::EndOfFile, 0))
}
pub fn peek(&mut self) -> Result<Token, Error> {
match self.peeked {
Some(ref peeked) => Ok(peeked.clone()),
None => {
let peeked = self.next()?;
self.peeked = Some(peeked.clone());
Ok(peeked)
}
}
}
}