use crate::template::{BoolValue, MatchOp, TextValue};
use crate::template_parser::structs::operators::{
ArithmeticUnaryOp, CompareOp, ListInOp, LogicOp, Paren,
};
use crate::template_parser::{ArithmeticExpr, ArithmeticOp, LogicExpr, MaybeValue, TextExpr};
use crate::{Atomic, AtomicStream, Operator, Sign, VariableChain};
use proc_macro2::fallback::unforce;
use std::fmt::Debug;
use crate::template_parser::error::TemplateParseError;
use crate::template_parser::structs::atomics::{
AtomicTrait, GenericAtomic, GenericAtomicStream, MySqlAtomic,
};
use crate::template_parser::structs::operators::ConnectOp;
use tracing::{debug, error};
pub type ParseResult<T> = std::result::Result<T, TemplateParseError>;
#[derive(Debug, Clone, PartialEq)]
pub enum GenericExpr {
Atomic(GenericAtomic),
ArithmeticExpr {
left: Box<GenericExpr>,
op: ArithmeticOp,
right: Box<GenericExpr>,
},
AnnotatedArithmeticExpr {
unary_op: ArithmeticUnaryOp,
expr: Box<GenericExpr>,
},
CompareExpr {
left: Box<GenericExpr>,
op: CompareOp,
right: Box<GenericExpr>,
},
Not(Box<GenericExpr>),
LogicExpr {
left: Box<GenericExpr>,
op: LogicOp,
right: Box<GenericExpr>,
},
CommaExpr {
left: Box<GenericExpr>,
op: ConnectOp,
right: Box<GenericExpr>,
},
NestedExpr(Box<GenericExpr>),
FnCallExpr {
name: VariableChain,
args: Box<GenericExpr>, },
ListInExpr {
left: Box<GenericExpr>,
op: ListInOp,
right: Box<GenericExpr>,
},
}
impl GenericExpr {
pub fn parse_str<T>(input: &str) -> ParseResult<GenericExpr>
where
T: AtomicTrait + Clone + PartialEq + Debug + Into<GenericAtomic>,
{
let stream = GenericAtomicStream::parse::<T>(input)?;
let (remaining, parsed) = Self::parse(stream.atomics)?;
Ok(parsed)
}
fn is_inner_comma_expr(&self) -> bool {
match self {
GenericExpr::CommaExpr { .. } => true,
GenericExpr::Atomic(_) => true,
GenericExpr::NestedExpr(inner) => inner.is_inner_comma_expr(),
_ => false,
}
}
fn is_nested_comma_expr(&self) -> bool {
match self {
GenericExpr::NestedExpr(inner) => inner.is_inner_comma_expr(),
_ => false,
}
}
fn is_unary_op(arithmetic_op: ArithmeticOp, prev: &Option<GenericAtomic>) -> bool {
match arithmetic_op {
ArithmeticOp::Add | ArithmeticOp::Sub => {}
_ => return false,
}
match prev {
None => true,
Some(atomic) => match atomic {
GenericAtomic::Operator(_) => true,
_ => false,
},
}
}
fn is_unary_op_sign(sign: &Sign, prev: &Option<GenericAtomic>) -> bool {
match sign {
Sign::Plus | Sign::Minus => {}
_ => return false,
}
match prev {
None => true,
Some(atomic) => match atomic {
GenericAtomic::Operator(_) => true,
_ => false,
},
}
}
fn reduce_recursive(
operator: &Operator,
operands: &mut Vec<GenericExpr>,
operators: &mut Vec<Operator>,
) -> ParseResult<()> {
while let Some(top) = operators.last() {
if let Operator::Paren(Paren::Left) | Operator::FnCall(_) = top {
return Ok(());
}
if precedence(top) < precedence(&operator) {
return Ok(());
}
Self::reduce(operands, operators)?;
}
Ok(())
}
fn consume_token(
token: GenericAtomic,
index: usize,
prev: &Option<GenericAtomic>,
operands: &mut Vec<GenericExpr>,
operators: &mut Vec<Operator>,
) -> ParseResult<Option<GenericExpr>> {
match token {
GenericAtomic::Keyword(_) => Ok(Some(GenericExpr::Atomic(token.clone()))),
GenericAtomic::Null
| GenericAtomic::Number(_)
| GenericAtomic::Text(_)
| GenericAtomic::Bool(_)
| GenericAtomic::Maybe(_) => {
debug!("GenericExpr::parse() push operands: {:?}", &token);
operands.push(GenericExpr::Atomic(token.clone()));
Ok(None)
}
GenericAtomic::Sign(sign) => {
if let Sign::Star = sign {
if index == 0 || &Some(GenericAtomic::Keyword("SELECT")) == prev {
return Ok(Some(GenericExpr::Atomic(GenericAtomic::Sign(sign))));
}
operators.push(Operator::Arithmetic(ArithmeticOp::Add));
}
if let Sign::Plus = sign {
if index == 0 || Self::is_unary_op_sign(&sign, &prev) {
operators.push(Operator::ArithmeticUnary(ArithmeticUnaryOp::Add));
}
} else if let Sign::Minus = sign {
if index == 0 || Self::is_unary_op_sign(&sign, &prev) {
operators.push(Operator::ArithmeticUnary(ArithmeticUnaryOp::Sub));
}
} else {
operands.push(GenericExpr::Atomic(GenericAtomic::Sign(sign.clone())));
}
Ok(None)
}
GenericAtomic::Operator(operator) => {
match operator {
Operator::Paren(Paren::Left) => {
if let Some(GenericExpr::Atomic(GenericAtomic::Maybe(
MaybeValue::VariableChain(v),
))) = operands.last()
{
operators.push(Operator::FnCall(v.clone()));
} else {
operators.push(Operator::Paren(Paren::Left));
}
Ok(None)
}
Operator::Paren(Paren::Right) => {
while let Some(top) = operators.last() {
match top {
Operator::Paren(Paren::Left) | Operator::FnCall(_) => break,
_ => Self::reduce(operands, operators)?,
}
}
debug!("GenericExpr::parse() pop left paren or FnCallOp");
if let Some(op) = operators.pop() {
match op {
Operator::Paren(Paren::Left) | Operator::FnCall(_) => {
Self::reduce(operands, operators)?;
Ok(None)
}
_ => return Err("Mismatched parentheses".into()),
}
} else {
return Err("Mismatched parentheses".into());
}
}
Operator::Arithmetic(arithmetic_op) => {
let is_unary = Self::is_unary_op(arithmetic_op.clone(), &prev);
if is_unary {
match arithmetic_op {
ArithmeticOp::Add => operators
.push(Operator::ArithmeticUnary(ArithmeticUnaryOp::Add)),
ArithmeticOp::Sub => operators
.push(Operator::ArithmeticUnary(ArithmeticUnaryOp::Sub)),
_ => unreachable!(),
}
} else {
Self::reduce_recursive(&operator, operands, operators)?;
operators.push(operator.clone());
}
debug!(
"GenericExpr::parse() operators len[{}]: {:?}",
operators.len(),
operators
);
Ok(None)
}
_ => {
Self::reduce_recursive(&operator, operands, operators)?;
operators.push(operator.clone());
debug!(
"GenericExpr::parse() operators len[{}]: {:?}",
operators.len(),
operators
);
Ok(None)
}
}
}
}
}
pub fn parse(
mut atomics: Vec<GenericAtomic>,
) -> ParseResult<(Vec<GenericAtomic>, GenericExpr)> {
debug!("GenericExpr::parse({:?})", atomics);
let mut operands: Vec<GenericExpr> = Vec::new(); let mut operators: Vec<Operator> = Vec::new(); let mut prev: Option<GenericAtomic> = None;
for token in atomics.clone() {
let current = token.clone();
match token {
GenericAtomic::Keyword(_) => {
return Ok((atomics, GenericExpr::Atomic(token.clone())));
}
GenericAtomic::Null
| GenericAtomic::Number(_)
| GenericAtomic::Text(_)
| GenericAtomic::Bool(_)
| GenericAtomic::Maybe(_) => {
debug!("GenericExpr::parse() push atomic: {:?}", &token);
operands.push(GenericExpr::Atomic(token.clone()));
}
GenericAtomic::Sign(sign) => {
if let Sign::Star = sign {}
operands.push(GenericExpr::Atomic(GenericAtomic::Sign(sign.clone())));
}
GenericAtomic::Operator(operator) => {
match operator {
Operator::Paren(Paren::Left) => {
if let Some(GenericExpr::Atomic(GenericAtomic::Maybe(
MaybeValue::VariableChain(v),
))) = operands.last()
{
operators.push(Operator::FnCall(v.clone()));
} else {
operators.push(Operator::Paren(Paren::Left));
}
}
Operator::Paren(Paren::Right) => {
while let Some(top) = operators.last() {
match top {
Operator::Paren(Paren::Left) | Operator::FnCall(_) => break,
_ => Self::reduce(&mut operands, &mut operators)?,
}
}
debug!("GenericExpr::parse() pop left paren or FnCallOp");
if let Some(op) = operators.pop() {
match op {
Operator::Paren(Paren::Left) | Operator::FnCall(_) => {
Self::reduce(&mut operands, &mut operators)?
}
_ => return Err("Mismatched parentheses".into()),
}
} else {
return Err("Mismatched parentheses".into());
}
}
Operator::Arithmetic(arithmetic_op) => {
let is_unary = Self::is_unary_op(arithmetic_op.clone(), &prev);
if is_unary {
match arithmetic_op {
ArithmeticOp::Add => operators
.push(Operator::ArithmeticUnary(ArithmeticUnaryOp::Add)),
ArithmeticOp::Sub => operators
.push(Operator::ArithmeticUnary(ArithmeticUnaryOp::Sub)),
_ => unreachable!(),
}
} else {
Self::reduce_recursive(&operator, &mut operands, &mut operators)?;
operators.push(operator.clone());
}
debug!(
"GenericExpr::parse() operators len[{}]: {:?}",
operators.len(),
operators
);
}
_ => {
Self::reduce_recursive(&operator, &mut operands, &mut operators)?;
operators.push(operator.clone());
debug!(
"GenericExpr::parse() operators len[{}]: {:?}",
operators.len(),
operators
);
}
}
}
}
prev = Some(current);
}
while let Some(op) = operators.last() {
if let Operator::Paren(Paren::Left) | Operator::FnCall(_) = op {
return Err("Mismatched parentheses".into());
}
debug!("GenericExpr::parse remaining operator: {:?}", &operators);
Self::reduce(&mut operands, &mut operators)?;
}
if operands.len() != 1 {
return Err("Invalid expression".into());
}
let expr = operands.pop().unwrap();
Ok((Vec::new(), expr))
}
fn reduce(
operands: &mut Vec<GenericExpr>,
operators: &mut Vec<Operator>,
) -> Result<(), String> {
if let Some(op) = operators.pop() {
debug!(
"GenericExpr::reduce op: ({:?}) remaining len[{:?}]",
op,
operators.len()
);
match op {
Operator::ArithmeticUnary(unary_op) => match unary_op {
ArithmeticUnaryOp::Add => {
let operand = operands
.pop()
.ok_or("Missing operand for UnaryPlus".to_string())?;
operands.push(GenericExpr::AnnotatedArithmeticExpr {
unary_op: ArithmeticUnaryOp::Add,
expr: Box::new(operand),
});
}
ArithmeticUnaryOp::Sub => {
let operand = operands
.pop()
.ok_or("Missing operand for UnaryMinus".to_string())?;
operands.push(GenericExpr::AnnotatedArithmeticExpr {
unary_op: ArithmeticUnaryOp::Sub,
expr: Box::new(operand),
});
}
},
Operator::Arithmetic(arithmetic_op) => {
let right = operands.pop().ok_or("Missing right operand".to_string())?;
let left = operands.pop().ok_or("Missing left operand".to_string())?;
operands.push(GenericExpr::ArithmeticExpr {
left: Box::new(left),
op: arithmetic_op,
right: Box::new(right),
});
}
Operator::Compare(compare_op) => {
let right = operands.pop().ok_or("Missing right operand".to_string())?;
let left = operands.pop().ok_or("Missing left operand".to_string())?;
operands.push(GenericExpr::CompareExpr {
left: Box::new(left),
op: compare_op,
right: Box::new(right),
});
}
Operator::Logic(logic_op) => match logic_op {
LogicOp::Not => {
let operand = operands
.pop()
.ok_or("Missing operand for Not".to_string())?;
operands.push(GenericExpr::Not(Box::new(operand)));
}
_ => {
let right = operands.pop().ok_or("Missing right operand".to_string())?;
let left = operands.pop().ok_or("Missing left operand".to_string())?;
operands.push(GenericExpr::LogicExpr {
left: Box::new(left),
op: logic_op,
right: Box::new(right),
});
}
},
Operator::ListInOp(list_in_op) => {
let right = operands.pop().ok_or("Missing right operand".to_string())?;
if !right.is_inner_comma_expr() {
return Err("right operand must be comma expr".to_string());
}
let left = operands.pop().ok_or("Missing left operand".to_string())?;
operands.push(GenericExpr::ListInExpr {
left: Box::new(left),
op: list_in_op,
right: Box::new(right),
});
}
Operator::Connect(connect_op) => {
let right = operands.pop().ok_or("Missing right operand".to_string())?;
let left = operands.pop().ok_or("Missing left operand".to_string())?;
operands.push(GenericExpr::CommaExpr {
left: Box::new(left),
op: connect_op,
right: Box::new(right),
});
}
Operator::FnCall(variable_chain) => {
let args = operands.pop().ok_or("Missing fn call args".to_string())?;
if !args.is_inner_comma_expr() {
return Err("Function arguments must be a comma expression".to_string());
}
let fn_call = GenericExpr::FnCallExpr {
name: variable_chain,
args: Box::new(args),
};
operands.push(fn_call);
}
Operator::Paren(p) => match p {
Paren::Left => {
let inner = operands.pop().ok_or("Missing right operand".to_string())?;
operands.push(GenericExpr::NestedExpr(Box::new(inner)));
}
Paren::Right => return Err("Unexpected parenthesis in reduce".to_string()),
},
}
}
Ok(())
}
}
fn precedence(op: &Operator) -> u8 {
match op {
Operator::ArithmeticUnary(_) => 8, Operator::Arithmetic(arithmetic_op) => match arithmetic_op {
ArithmeticOp::Mul | ArithmeticOp::Div | ArithmeticOp::Mod => 7,
ArithmeticOp::Add | ArithmeticOp::Sub => 6,
},
Operator::Compare(_) => 5,
Operator::ListInOp(_) => 5,
Operator::Logic(logic_op) => match logic_op {
LogicOp::Not => 4,
LogicOp::And => 3,
LogicOp::Or => 2,
},
Operator::Connect(_) => 1,
Operator::Paren(_) | Operator::FnCall(_) => 0, }
}