use std::fmt;
use super::ast::{Expression, AST};
use super::lexer::Lexer;
use super::token::{Operator, Token};
type ExpResult = Result<Expression, ParseError>;
struct Validator;
impl Validator {
fn string_field(field_name: &str, right: &Expression) -> Result<(), ParseError> {
match right {
Expression::String(_) => Ok(()),
_ => Err(ParseError::from(format!(
"{} can only be compared to a string got: {:?}",
field_name, right
))),
}
}
fn date_field(field_name: &str, right: &Expression) -> Result<(), ParseError> {
match right {
Expression::Date(_) => Ok(()),
_ => Err(ParseError::from(format!(
"{} can only be compared to a string got: {:?}",
field_name, right
))),
}
}
fn number_field(field_name: &str, right: &Expression) -> Result<(), ParseError> {
match right {
Expression::Number(_) => Ok(()),
_ => Err(ParseError::from(format!(
"{} can only be compared to a string got: {:?}",
field_name, right
))),
}
}
fn boolean_field(field_name: &str, right: &Expression) -> Result<(), ParseError> {
match right {
Expression::Bool(_) => Ok(()),
_ => Err(ParseError::from(format!(
"{} can only be compared to a string got: {:?}",
field_name, right
))),
}
}
fn validate_comparison(left: &Expression, right: &Expression) -> Result<(), ParseError> {
match left {
Expression::String(field) => match field.as_ref() {
"title" => Validator::string_field(&field, right),
"context" => Validator::string_field(&field, right),
"body" => Validator::string_field(&field, right),
"notes" => Validator::string_field(&field, right),
"created_date" => Validator::date_field(&field, right),
"completed_date" => Validator::date_field(&field, right),
"priority" => Validator::number_field(&field, right),
"completed" => Validator::boolean_field(&field, right),
_ => Err(ParseError::from(format!("invalid field name: {}", field))),
},
_ => Err(ParseError::from(format!(
"invalid field expression: {:?}",
left
))),
}
}
fn validate_logical(left: &Expression, right: &Expression) -> Result<(), ParseError> {
match left {
Expression::Infix(_, _, _) | Expression::String(_) => match right {
Expression::Infix(_, _, _) | Expression::String(_) => Ok(()),
_ => Err(ParseError::new(
"logical operators can only compare other infix expressions or strings",
)),
},
_ => Err(ParseError::new(
"logical operators can only compare other infix expressions or strings",
)),
}
}
fn validate(infix: &Expression) -> Result<(), ParseError> {
match infix {
Expression::Infix(left, op, right) => match op {
Operator::AND | Operator::OR => Validator::validate_logical(left, right),
_ => Validator::validate_comparison(left, right),
},
_ => Err(ParseError::new("not an infix expression")),
}
}
}
pub struct Parser<'a> {
lexer: Lexer<'a>,
peek_token: Option<Token>,
}
#[derive(Debug)]
pub struct ParseError {
pos: usize,
ch: char,
msg: String,
}
impl ParseError {
pub fn new(msg: &str) -> ParseError {
ParseError {
pos: 0,
ch: char::from(0),
msg: msg.to_string(),
}
}
pub fn at(mut self, pos: usize) -> ParseError {
self.pos = pos;
self
}
pub fn bad_char(mut self, ch: char) -> ParseError {
self.ch = ch;
self
}
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Parsing Error: {} @ {}", self.msg, self.ch)
}
}
impl From<String> for ParseError {
fn from(input: String) -> ParseError {
ParseError::new(&input)
}
}
impl<'a> From<&'a str> for Parser<'a> {
fn from(input: &'a str) -> Parser {
let mut p = Parser {
lexer: Lexer::from(input),
peek_token: None,
};
p.next();
p
}
}
impl<'a> Iterator for Parser<'a> {
type Item = Token;
fn next(&mut self) -> Option<Token> {
let current_token = self.peek_token.clone();
self.peek_token = self.lexer.next();
current_token
}
}
#[derive(PartialEq, PartialOrd, Clone, Copy)]
enum Precedence {
LOWEST,
STRING,
ANDOR,
COMPARISON,
}
impl<'a> From<&'a Operator> for Precedence {
fn from(token: &Operator) -> Precedence {
match token {
Operator::AND => Precedence::ANDOR,
Operator::OR => Precedence::ANDOR,
_ => Precedence::COMPARISON,
}
}
}
impl<'a> From<&'a Token> for Precedence {
fn from(token: &Token) -> Precedence {
match token {
Token::Operator(op) => Precedence::from(op),
Token::Str(_) => Precedence::STRING,
_ => Precedence::LOWEST,
}
}
}
impl<'a> From<&'a Option<Token>> for Precedence {
fn from(token: &Option<Token>) -> Precedence {
match token {
Some(t) => Precedence::from(t),
_ => Precedence::LOWEST,
}
}
}
impl<'a> Parser<'a> {
pub fn parse(&mut self) -> Result<AST, ParseError> {
self.parse_expression(Precedence::LOWEST)
.map(|exp| AST { expression: exp })
}
fn parse_expression(&mut self, precedence: Precedence) -> ExpResult {
let mut left = match self.next() {
Some(Token::LP) => self.parse_grouped_expression()?,
Some(token) => match token {
Token::Operator(_) => {
return Err(ParseError::from(format!("no prefix func found: {}", token)))
}
_ => Expression::from(token),
},
None => return Err(ParseError::new("unexpected end of input")),
};
while self.peek_token.is_some()
&& (precedence < Precedence::from(&self.peek_token) || precedence == Precedence::STRING)
{
left = match self.peek_token {
Some(Token::Operator(_)) => self.parse_infix_exp(left),
Some(Token::Str(_)) => self.concat(left),
_ => break,
}?;
}
Ok(left)
}
fn concat(&mut self, left: Expression) -> ExpResult {
match left {
Expression::String(mut s) => {
let next_char = match self.next() {
Some(Token::Str(val)) => val,
_ => return Err(ParseError::new("expected a string or field")),
};
s.push_str(" ");
s.push_str(&next_char);
Ok(Expression::String(s))
}
_ => Err(ParseError::new(
"Expected an Expression::String. If using a multi-word string in comparison make sure to quote it.",
)),
}
}
fn parse_infix_exp(&mut self, left: Expression) -> ExpResult {
let op = match self.next() {
Some(Token::Operator(op)) => op,
Some(token) => {
return Err(ParseError::from(format!(
"{} is not a valid operator",
token
)))
}
None => return Err(ParseError::new("attempted infix found EOF")),
};
let precedence = Precedence::from(&op);
let right = self.parse_expression(precedence)?;
let exp = Expression::Infix(Box::new(left), op, Box::new(right));
Validator::validate(&exp)?;
Ok(exp)
}
fn parse_grouped_expression(&mut self) -> ExpResult {
let exp = self.parse_expression(Precedence::LOWEST)?;
match self.peek_token {
Some(Token::RP) => {
self.next();
Ok(exp)
}
Some(_) => Err(ParseError::new("unclosed group expression: missing )")),
None => Err(ParseError::new("unexpected EOF parsing group expression")),
}
}
}
#[cfg(test)]
pub mod test {
use super::*;
macro_rules! parser_test {
($name:ident, $query:expr, $ast:expr) => {
#[test]
fn $name() {
let mut parser = Parser::from($query);
match parser.parse() {
Ok(ast) => assert_eq!(ast, $ast),
Err(e) => {
println!("{}", e);
assert!(false);
}
}
}
};
}
parser_test!(
simple_parse,
"milk and cookies",
AST {
expression: Expression::Infix(
Box::new(Expression::String("milk".to_string())),
Operator::AND,
Box::new(Expression::String("cookies".to_string())),
),
}
);
parser_test!(
boolean_comparison,
"completed = false",
AST {
expression: Expression::Infix(
Box::new(Expression::String("completed".to_string())),
Operator::EQ,
Box::new(Expression::Bool(false)),
),
}
);
parser_test!(
simple_all_string_parse,
"milk -and cookies",
AST {
expression: Expression::String("milk and cookies".to_string()),
}
);
parser_test!(complex_parse, "(priority > 5 and title ^ \"take out the trash\") or (context = \"work\" and (priority >= 2 or (\"my little pony\")))", AST {
expression: Expression::Infix(
Box::new(Expression::Infix(
Box::new(Expression::Infix(
Box::new(Expression::from("priority")),
Operator::GT,
Box::new(Expression::from(5.0)),
)),
Operator::AND,
Box::new(Expression::Infix(
Box::new(Expression::from("title")),
Operator::LIKE,
Box::new(Expression::from("take out the trash")),
)),
)),
Operator::OR,
Box::new(Expression::Infix(
Box::new(Expression::Infix(
Box::new(Expression::from("context")),
Operator::EQ,
Box::new(Expression::from("work")),
)),
Operator::AND,
Box::new(Expression::Infix(
Box::new(Expression::Infix(
Box::new(Expression::from("priority")),
Operator::GTE,
Box::new(Expression::from(2.0)),
)),
Operator::OR,
Box::new(Expression::from("my little pony")),
)),
))
)
});
}