use std::{error::Error, fmt, iter::Peekable, slice::Iter};
use crate::interpreter::{
ArithmeticOperator, Expression, Literal, LogicalOperator, Operator, Token, Unary,
};
pub struct Parser<'i> {
token_iter: Peekable<Iter<'i, Token>>,
position: u32,
}
#[derive(Debug, PartialEq)]
pub enum ParserError {
InvalidOperatorConversion(
Token,
),
InvalidPrimaryToken(
Token,
u32,
),
UnexpectedEndOfExpression,
UnterminatedGroup(
Expression,
u32,
),
}
impl<'i> Parser<'i> {
pub fn new(tokens: &'i [Token]) -> Self {
Self {
token_iter: tokens.iter().peekable(),
position: 0,
}
}
pub fn parse(&mut self) -> Result<Expression, ParserError> {
self.expression()
}
fn expression(&mut self) -> Result<Expression, ParserError> {
self.equality()
}
fn equality(&mut self) -> Result<Expression, ParserError> {
let equivalence_tokens = [&&Token::EqualEqual, &&Token::BangEqual];
let mut expr = self.comparison()?;
while let Some(eq_op) = self
.token_iter
.peek()
.filter(|v| equivalence_tokens.contains(v))
.map(|v| Self::try_op_from_token(v))
.transpose()?
{
self.consume_token();
let right = self.comparison()?;
expr = Expression::Binary(Box::new(expr), eq_op, Box::new(right));
}
Ok(expr)
}
fn comparison(&mut self) -> Result<Expression, ParserError> {
let comparison_tokens = [
&&Token::GreaterThan,
&&Token::GreaterThanOrEqualTo,
&&Token::LessThan,
&&Token::LessThanOrEqualTo,
];
let mut expr = self.term()?;
while let Some(comp_op) = self
.token_iter
.peek()
.filter(|v| comparison_tokens.contains(v))
.map(|v| Self::try_op_from_token(v))
.transpose()?
{
self.consume_token();
let right = self.comparison()?;
expr = Expression::Binary(Box::new(expr), comp_op, Box::new(right));
}
Ok(expr)
}
fn term(&mut self) -> Result<Expression, ParserError> {
let arithmetic_tokens = [&&Token::Minus, &&Token::Plus];
let mut expr = self.factor()?;
while let Some(math_op) = self
.token_iter
.peek()
.filter(|v| arithmetic_tokens.contains(v))
.map(|v| Self::try_op_from_token(v))
.transpose()?
{
self.consume_token();
let right = self.comparison()?;
expr = Expression::Binary(Box::new(expr), math_op, Box::new(right));
}
Ok(expr)
}
fn factor(&mut self) -> Result<Expression, ParserError> {
let arithmetic_tokens = [&&Token::Slash, &&Token::Star];
let mut expr = self.unary()?;
while let Some(math_op) = self
.token_iter
.peek()
.filter(|v| arithmetic_tokens.contains(v))
.map(|v| Self::try_op_from_token(v))
.transpose()?
{
self.consume_token();
let right = self.comparison()?;
expr = Expression::Binary(Box::new(expr), math_op, Box::new(right));
}
Ok(expr)
}
fn unary(&mut self) -> Result<Expression, ParserError> {
match self.token_iter.peek() {
Some(&&Token::Bang) => {
self.consume_token();
Ok(Expression::Unary(Unary::Not(Box::new(self.unary()?))))
}
Some(&&Token::Minus) => {
self.consume_token();
Ok(Expression::Unary(Unary::Negation(Box::new(self.unary()?))))
}
_ => self.primary(),
}
}
fn primary(&mut self) -> Result<Expression, ParserError> {
let start = self.position;
match self.consume_token() {
Some(&Token::Number(value)) => Ok(Expression::Literal(Literal::Number(value))),
Some(&Token::String(ref string)) => {
Ok(Expression::Literal(Literal::String(string.clone())))
}
Some(&Token::Identifier(ref identifier)) => match identifier.as_str() {
"true" => Ok(Expression::Literal(Literal::True)),
"false" => Ok(Expression::Literal(Literal::False)),
"null" => Ok(Expression::Literal(Literal::Null)),
_ => Ok(Expression::Identifier(identifier.clone())),
},
Some(&Token::LeftParen) => {
let expr = self.expression()?;
if let Some(Token::RightParen) = self.token_iter.peek() {
self.consume_token();
Ok(Expression::Grouping(Box::new(expr)))
} else {
Err(ParserError::UnterminatedGroup(expr, start))
}
}
Some(unexpected) => Err(ParserError::InvalidPrimaryToken(unexpected.clone(), start)),
None => Err(ParserError::UnexpectedEndOfExpression),
}
}
fn consume_token(&mut self) -> Option<&Token> {
self.position += 1;
self.token_iter.next()
}
fn try_op_from_token(src: &Token) -> Result<Operator, ParserError> {
match src {
Token::EqualEqual => Ok(Operator::Logical(LogicalOperator::EqualTo)),
Token::BangEqual => Ok(Operator::Logical(LogicalOperator::NotEqualTo)),
Token::GreaterThan => Ok(Operator::Logical(LogicalOperator::GreaterThan)),
Token::GreaterThanOrEqualTo => {
Ok(Operator::Logical(LogicalOperator::GreaterThanOrEqualTo))
}
Token::LessThan => Ok(Operator::Logical(LogicalOperator::LessThan)),
Token::LessThanOrEqualTo => Ok(Operator::Logical(LogicalOperator::LessThanOrEqualTo)),
Token::Plus => Ok(Operator::Arithmetic(ArithmeticOperator::Plus)),
Token::Minus => Ok(Operator::Arithmetic(ArithmeticOperator::Minus)),
Token::Star => Ok(Operator::Arithmetic(ArithmeticOperator::Star)),
Token::Slash => Ok(Operator::Arithmetic(ArithmeticOperator::Slash)),
_ => Err(ParserError::InvalidOperatorConversion(src.clone())),
}
}
}
impl Error for ParserError {}
impl fmt::Display for ParserError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::InvalidOperatorConversion(token) => {
write!(f, "Could not convert Token '{:?}' to Operator", token)
}
Self::InvalidPrimaryToken(token, start) => {
write!(
f,
"Invalid token '{:?}' starting at location {}",
token, start
)
}
Self::UnexpectedEndOfExpression => {
write!(f, "Expression ended unexpectedly")
}
Self::UnterminatedGroup(expr, start) => {
write!(
f,
"Unterminated grouping expression '{}' starting at location {}",
expr, start
)
}
}
}
}
#[cfg(test)]
mod tests {
use std::error::Error;
use crate::interpreter::{
lexer::Lexer,
parser::{Parser, ParserError},
ArithmeticOperator, Expression, Literal, Operator, Token, Unary,
};
type TestResult = Result<(), Box<dyn Error>>;
#[test]
fn output_test() -> TestResult {
let expr_str = "-123 * (45.67)";
let mut lexer = Lexer::new(expr_str);
let tokens = lexer.scan()?;
let mut parser = Parser::new(&tokens);
let expr = parser.parse()?;
eprintln!("Expression '{}' \"pretty\"-printed:\n{}", expr_str, expr);
let pretty_expr = format!("{}", expr);
assert_eq!(pretty_expr, "(* (- 123.0) (group 45.67))");
Ok(())
}
#[test]
fn invalid_operator_conversion() -> TestResult {
let valid_token = Token::Plus;
let invalid_token = Token::LeftBrace;
assert_eq!(
Parser::try_op_from_token(&valid_token),
Ok(Operator::Arithmetic(ArithmeticOperator::Plus))
);
assert_eq!(
Parser::try_op_from_token(&invalid_token),
Err(ParserError::InvalidOperatorConversion(invalid_token))
);
Ok(())
}
#[test]
fn unterm_group() -> TestResult {
let valid_tokens = vec![
Token::Number(1.0),
Token::Plus,
Token::LeftParen,
Token::Number(2.0),
Token::RightParen,
];
let invalid_tokens = vec![
Token::Number(1.0),
Token::Plus,
Token::LeftParen,
Token::Number(2.0),
Token::Star,
Token::Number(5.0),
];
let mut valid_parser = Parser::new(&valid_tokens);
assert_eq!(
valid_parser.parse(),
Ok(Expression::Binary(
Box::new(Expression::Literal(Literal::Number(1.0))),
Operator::Arithmetic(ArithmeticOperator::Plus),
Box::new(Expression::Grouping(Box::new(Expression::Literal(
Literal::Number(2.0)
))))
))
);
let mut invalid_parser = Parser::new(&invalid_tokens);
assert_eq!(
invalid_parser.parse(),
Err(ParserError::UnterminatedGroup(
Expression::Binary(
Box::new(Expression::Literal(Literal::Number(2.0))),
Operator::Arithmetic(ArithmeticOperator::Star),
Box::new(Expression::Literal(Literal::Number(5.0)))
),
2,
))
);
Ok(())
}
#[test]
fn invalid_primary_token() -> TestResult {
let valid_tokens = vec![Token::Minus, Token::Number(1.0)];
let invalid_tokens = vec![Token::Minus, Token::Plus];
let mut valid_parser = Parser::new(&valid_tokens);
assert_eq!(
valid_parser.parse(),
Ok(Expression::Unary(Unary::Negation(Box::new(
Expression::Literal(Literal::Number(1.0))
))))
);
let mut invalid_parser = Parser::new(&invalid_tokens);
assert_eq!(
invalid_parser.parse(),
Err(ParserError::InvalidPrimaryToken(Token::Plus, 1))
);
Ok(())
}
#[test]
fn unexpected_eoe() -> TestResult {
let no_tokens: Vec<Token> = Vec::new();
let mut invalid_parser = Parser::new(&no_tokens);
assert_eq!(
invalid_parser.parse(),
Err(ParserError::UnexpectedEndOfExpression)
);
Ok(())
}
}