/* lexer/src/lib.rs * Eryn Wells */ use std::iter::Peekable; mod error; pub use error::Error; #[derive(Debug, Eq, PartialEq)] pub enum Token { LeftParen, RightParen, Id(String), } #[derive(Debug, Eq, PartialEq)] enum Resume { Here, AtNext } #[derive(Debug, Eq, PartialEq)] enum IterationResult { Finish, Continue, Emit(Token, Resume), Error(Error), } pub struct Lexer where T: Iterator { input: Peekable, } impl Lexer where T: Iterator { pub fn new(input: T) -> Lexer { Lexer { input: input.peekable() } } fn emit(&self, token: Token, resume: Resume) -> IterationResult { IterationResult::Emit(token, resume) } fn fail(&self, msg: String) -> IterationResult { IterationResult::Error(Error::new(msg)) } } impl Iterator for Lexer where T: Iterator { type Item = Result; fn next(&mut self) -> Option { let mut buffer = String::new(); loop { let peek = self.input.peek().map(char::clone); let result = if buffer.is_empty() { match peek { Some('(') => self.emit(Token::LeftParen, Resume::AtNext), Some(')') => self.emit(Token::RightParen, Resume::AtNext), Some(c) if c.is_whitespace() => IterationResult::Continue, Some(c) if c.is_alphabetic() => { buffer.push(c); IterationResult::Continue }, Some(c) => self.fail(format!("Invalid character: {}", c)), // We found EOF and there's no pending string, so just finish. None => IterationResult::Finish, } } else { match peek { Some(c) if c.is_alphabetic() => { buffer.push(c); IterationResult::Continue } Some(c) if c == '(' || c == ')' || c.is_whitespace() => self.emit(Token::Id(buffer.clone()), Resume::Here), Some(c) => self.fail(format!("Invalid character: {}", c)), // Found EOF. Emit what we have and finish. // Note: the Resume argument doesn't matter in this case since the input // iterator will always be None from here on. None => self.emit(Token::Id(buffer.clone()), Resume::Here), } }; match result { IterationResult::Finish => break, IterationResult::Continue => self.input.next(), IterationResult::Emit(token, resume) => { if resume == Resume::AtNext { self.input.next(); } return Some(Ok(token)) }, IterationResult::Error(err) => return Some(Err(err)), }; } None } }