|
@@ -0,0 +1,180 @@
|
|
|
+use std::str::FromStr;
|
|
|
+
|
|
|
+// src/main.rs
|
|
|
+use pest::{Parser, Span};
|
|
|
+use pest_derive::Parser;
|
|
|
+
|
|
|
+#[derive(Parser)]
|
|
|
+#[grammar = "src/filter_expr/expr.pest"] // relative path to your .pest file
|
|
|
+struct QueryParser;
|
|
|
+
|
|
|
+#[derive(Clone, PartialEq, Eq, Debug, Copy)]
|
|
|
+pub enum ExprOp {
|
|
|
+ And,
|
|
|
+ Or,
|
|
|
+ Not,
|
|
|
+ Eq,
|
|
|
+ NotEq,
|
|
|
+ Gt,
|
|
|
+ Gte,
|
|
|
+ Lt,
|
|
|
+ Lte,
|
|
|
+ Add,
|
|
|
+ Sub,
|
|
|
+ Mul,
|
|
|
+ Div,
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Clone, PartialEq, Eq, Debug, Copy)]
|
|
|
+pub enum Order {
|
|
|
+ Ascending,
|
|
|
+ Descending,
|
|
|
+}
|
|
|
+
|
|
|
+impl FromStr for Order {
|
|
|
+ type Err = ();
|
|
|
+
|
|
|
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
+ match s {
|
|
|
+ "asc" => Ok(Order::Ascending),
|
|
|
+ "desc" => Ok(Order::Descending),
|
|
|
+ _ => Err(()),
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl From<&str> for ExprOp {
|
|
|
+ fn from(s: &str) -> Self {
|
|
|
+ match s.to_lowercase().as_str() {
|
|
|
+ "+" => ExprOp::Add,
|
|
|
+ "-" => ExprOp::Sub,
|
|
|
+ "*" => ExprOp::Mul,
|
|
|
+ "/" => ExprOp::Div,
|
|
|
+ "&&" | "and" => ExprOp::And,
|
|
|
+ "||" | "or" => ExprOp::Or,
|
|
|
+ "!" | "not" => ExprOp::Not,
|
|
|
+ "==" | "=" => ExprOp::Eq,
|
|
|
+ "!=" => ExprOp::NotEq,
|
|
|
+ ">" => ExprOp::Gt,
|
|
|
+ ">=" => ExprOp::Gte,
|
|
|
+ "<" => ExprOp::Lt,
|
|
|
+ "<=" => ExprOp::Lte,
|
|
|
+ x => panic!("{} is not a valid Operation", x),
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Debug)]
|
|
|
+enum Expr {
|
|
|
+ Not(Box<Expr>),
|
|
|
+ Op {
|
|
|
+ op: ExprOp,
|
|
|
+ left: Box<Expr>,
|
|
|
+ right: Box<Expr>,
|
|
|
+ },
|
|
|
+ Variable(Vec<String>),
|
|
|
+ Ident(String),
|
|
|
+ Number(i128),
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Debug)]
|
|
|
+pub struct Query {
|
|
|
+ where_clause: Option<Expr>,
|
|
|
+ limit_clause: Option<i128>,
|
|
|
+ order_by_clause: Option<(Expr, Order)>,
|
|
|
+}
|
|
|
+
|
|
|
+fn parse_expr(pair: pest::iterators::Pair<Rule>) -> Expr {
|
|
|
+ match pair.as_rule() {
|
|
|
+ Rule::logical_expr
|
|
|
+ | Rule::logical_term
|
|
|
+ | Rule::comparison
|
|
|
+ | Rule::sum
|
|
|
+ | Rule::factor
|
|
|
+ | Rule::product => {
|
|
|
+ let mut inner = pair.into_inner();
|
|
|
+ let mut last_expr = None;
|
|
|
+
|
|
|
+ while let Some(first) = inner.next() {
|
|
|
+ let (first_expr, next) = if let Some(first_expr) = last_expr {
|
|
|
+ (first_expr, Some(first))
|
|
|
+ } else {
|
|
|
+ (parse_expr(first), inner.next())
|
|
|
+ };
|
|
|
+ last_expr = Some(if let Some(op_pair) = next {
|
|
|
+ let op = op_pair.as_str().into();
|
|
|
+ let second = inner.next().unwrap();
|
|
|
+ let second_expr = parse_expr(second);
|
|
|
+ Expr::Op {
|
|
|
+ op,
|
|
|
+ left: Box::new(first_expr),
|
|
|
+ right: Box::new(second_expr),
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ first_expr
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ last_expr.unwrap()
|
|
|
+ }
|
|
|
+ Rule::not_expr => {
|
|
|
+ let mut inner = pair.into_inner();
|
|
|
+ Expr::Not(Box::new(parse_expr(inner.next().unwrap())))
|
|
|
+ }
|
|
|
+ Rule::variable => {
|
|
|
+ let mut inner = pair.into_inner();
|
|
|
+ let mut first = vec![inner.next().unwrap().as_str().to_owned()];
|
|
|
+ while let Some(Expr::Ident(ident)) = inner.next().map(parse_expr) {
|
|
|
+ first.push(ident);
|
|
|
+ }
|
|
|
+ Expr::Variable(first)
|
|
|
+ }
|
|
|
+ Rule::number => Expr::Number(pair.as_str().parse().unwrap()),
|
|
|
+ Rule::ident => Expr::Ident(pair.as_str().to_owned()),
|
|
|
+ x => {
|
|
|
+ panic!("Unexpected rule: {:?} {:?}", x, pair);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+pub fn parse_query(query: &str) -> Query {
|
|
|
+ let pairs = QueryParser::parse(Rule::query, query).unwrap();
|
|
|
+
|
|
|
+ let mut where_clause = None;
|
|
|
+ let mut limit_clause = None;
|
|
|
+ let mut order_by_clause = None;
|
|
|
+
|
|
|
+ for pair in pairs {
|
|
|
+ match pair.as_rule() {
|
|
|
+ Rule::EOI => {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ Rule::where_clause => {
|
|
|
+ where_clause = Some(parse_expr(pair.into_inner().next().unwrap()));
|
|
|
+ }
|
|
|
+ Rule::limit_clause => {
|
|
|
+ limit_clause = Some(match parse_expr(pair.into_inner().next().unwrap()) {
|
|
|
+ Expr::Number(v) => v,
|
|
|
+ _ => unreachable!(),
|
|
|
+ });
|
|
|
+ }
|
|
|
+ Rule::order_by_clause => {
|
|
|
+ let mut iter = pair.into_inner();
|
|
|
+ let expr = parse_expr(iter.next().unwrap());
|
|
|
+ let order = if let Some(order_by_text) = iter.next() {
|
|
|
+ order_by_text.as_str().to_lowercase().parse().unwrap()
|
|
|
+ } else {
|
|
|
+ Order::Ascending
|
|
|
+ };
|
|
|
+ order_by_clause = Some((expr, order));
|
|
|
+ }
|
|
|
+ _ => unreachable!(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Query {
|
|
|
+ where_clause,
|
|
|
+ limit_clause,
|
|
|
+ order_by_clause,
|
|
|
+ }
|
|
|
+}
|