use std::io;
use std::iter::Peekable;
use source_span::{
Position,
Span,
Metrics
};
use crate::Located;
use super::{token, Token, Result, Error};
pub struct Lexer<R: Iterator<Item=io::Result<char>>, M: Metrics> {
decoder: Peekable<R>,
location: Span,
metrics: M
}
fn is_separator(c: char) -> bool {
c == '.' || c == ';' || c == ':' || c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']' || c == ','
}
impl<R: Iterator<Item = io::Result<char>>, M: Metrics> Lexer<R, M> {
pub fn new(source: R, cursor: Position, metrics: M) -> Lexer<R, M> {
Lexer {
decoder: source.peekable(),
location: cursor.into(),
metrics
}
}
pub fn location(&self) -> Span {
self.location
}
pub fn set_location(&mut self, location: Span) {
self.location = location
}
fn peek_char(&mut self) -> Result<Option<char>> {
match self.decoder.peek() {
Some(Ok(c)) => {
Ok(Some(*c))
},
Some(Err(_)) => {
Ok(Some(self.consume()?)) },
None => Ok(None)
}
}
fn consume(&mut self) -> Result<char> {
match self.decoder.next() {
Some(Ok(c)) => {
self.location.push(c, &self.metrics);
Ok(c)
},
Some(Err(e)) => {
self.location.clear();
Err(Error::IO(e).at(self.location.clone()))
},
None => {
self.location.clear();
Err(Error::IO(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "unexpected enf of stream")).at(self.location.clone()))
}
}
}
fn skip_whitespaces(&mut self) -> Result<()> {
loop {
match self.peek_char()? {
Some(';') => self.skip_line()?,
Some('\n') => {
self.consume()?;
}
Some(c) if c.is_whitespace() => {
self.consume()?;
},
_ => break
}
}
Ok(())
}
fn skip_line(&mut self) -> Result<()> {
loop {
match self.peek_char()? {
Some('\n') => {
self.consume()?;
break
}
_ => {
self.consume()?;
}
}
}
Ok(())
}
fn read_ident(&mut self) -> Result<Located<Token>> {
let mut name = String::new();
name.push(self.consume()?);
loop {
match self.peek_char()? {
Some(c) if !c.is_whitespace() && !is_separator(c) => {
name.push(self.consume()?);
},
_ => break
}
}
let location = self.location;
self.location.clear();
Ok(Token::Ident(name.to_string()).at(location))
}
fn read_string(&mut self) -> Result<Located<Token>> {
let mut string = String::new();
let mut escape = false;
loop {
if escape {
match self.consume()? {
'n' => string.push('\n'),
c => string.push(c)
}
escape = false;
} else {
match self.consume()? {
'\\' => {
escape = true;
},
'"' => {
break
},
c => {
string.push(c)
}
}
}
}
let location = self.location;
self.location.clear();
Ok(Token::Litteral(token::Litteral::String(string)).at(location))
}
fn read_token(&mut self) -> Result<Option<Located<Token>>> {
self.skip_whitespaces()?;
self.location.clear();
match self.peek_char()? {
Some(c) => {
match c {
'(' => {
self.consume()?;
let location = self.location;
self.location.clear();
Ok(Some(Token::Begin.at(location)))
},
')' => {
self.consume()?;
let location = self.location;
self.location.clear();
Ok(Some(Token::End.at(location)))
},
'"' => {
self.consume()?;
Ok(Some(self.read_string()?))
}
_ => {
Ok(Some(self.read_ident()?))
}
}
},
None => Ok(None)
}
}
}
impl<R: Iterator<Item = io::Result<char>>, M: Metrics> Iterator for Lexer<R, M> {
type Item = Result<Located<Token>>;
fn next(&mut self) -> Option<Result<Located<Token>>> {
self.read_token().transpose()
}
}