pub mod frame;
pub mod short_circuit;
use std::{fmt::Display, sync::Arc};
use crate::{
expr::frame::ExprFrame,
predicate::{self, Predicate},
};
use frame::MapPredicateRef;
use recursion::CollapsibleExt;
use self::short_circuit::ShortCircuit;
#[derive(Debug, PartialEq, Eq)]
pub enum Expr<Predicate = predicate::Predicate> {
Not(Box<Self>),
And(Box<Self>, Box<Self>),
Or(Box<Self>, Box<Self>),
Predicate(Predicate),
Literal(bool),
}
impl<P: std::fmt::Debug> Display for Expr<P> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expr::Not(e) => f.write_str(&format!("!{e}")),
Expr::And(a, b) => f.write_str(&format!("{a} && {b}")),
Expr::Or(a, b) => f.write_str(&format!("{a} || {b}")),
Expr::Predicate(p) => write!(f, "{:?}", &p),
Expr::Literal(x) => f.write_str(&x.to_string()),
}
}
}
impl<A, B, C> Expr<Predicate<A, B, C>> {
pub fn name_predicate(x: A) -> Self {
Self::Predicate(Predicate::Name(Arc::new(x)))
}
pub fn meta_predicate(x: B) -> Self {
Self::Predicate(Predicate::Metadata(Arc::new(x)))
}
pub fn content_predicate(x: C) -> Self {
Self::Predicate(Predicate::Content(x))
}
}
impl<Name, Meta, Content, Structured> Expr<Predicate<Name, Meta, Content, Structured>> {
pub fn contains_structured_predicates(&self) -> bool {
MapPredicateRef(self).collapse_frames(|e| match e {
ExprFrame::Predicate(Predicate::Structured(_)) => true,
ExprFrame::And(a, b) | ExprFrame::Or(a, b) => a || b,
ExprFrame::Not(a) => a,
ExprFrame::Predicate(_) | ExprFrame::Literal(_) => false,
})
}
pub fn contains_content_predicates(&self) -> bool {
MapPredicateRef(self).collapse_frames(|e| match e {
ExprFrame::Predicate(Predicate::Content(_)) => true,
ExprFrame::And(a, b) | ExprFrame::Or(a, b) => a || b,
ExprFrame::Not(a) => a,
ExprFrame::Predicate(_) | ExprFrame::Literal(_) => false,
})
}
}
impl<P> Expr<P> {
pub fn map_predicate_ref<'a, B>(&'a self, f: impl Fn(&'a P) -> B) -> Expr<B> {
MapPredicateRef(self).collapse_frames(|e| match e {
ExprFrame::Predicate(p) => Expr::Predicate(f(p)),
ExprFrame::And(a, b) => Expr::and(a, b),
ExprFrame::Or(a, b) => Expr::or(a, b),
ExprFrame::Not(a) => Expr::negate(a),
ExprFrame::Literal(x) => Expr::Literal(x),
})
}
pub fn and(a: Self, b: Self) -> Self {
Self::And(Box::new(a), Box::new(b))
}
pub fn or(a: Self, b: Self) -> Self {
Self::Or(Box::new(a), Box::new(b))
}
pub fn negate(a: Self) -> Self {
Self::Not(Box::new(a))
}
}
impl<P: Clone> Expr<P> {
pub fn map_predicate<B>(self, f: impl Fn(P) -> B) -> Expr<B> {
self.collapse_frames(|e| match e {
ExprFrame::Predicate(p) => Expr::Predicate(f(p)),
ExprFrame::And(a, b) => Expr::and(a, b),
ExprFrame::Or(a, b) => Expr::or(a, b),
ExprFrame::Not(a) => Expr::negate(a),
ExprFrame::Literal(x) => Expr::Literal(x),
})
}
pub fn map_predicate_err<E, B>(self, f: impl Fn(P) -> Result<B, E>) -> Result<Expr<B>, E> {
self.collapse_frames(|e| match e {
ExprFrame::Predicate(p) => Ok(Expr::Predicate(f(p)?)),
ExprFrame::And(a, b) => Ok(Expr::and(a?, b?)),
ExprFrame::Or(a, b) => Ok(Expr::or(a?, b?)),
ExprFrame::Not(a) => Ok(Expr::negate(a?)),
ExprFrame::Literal(x) => Ok(Expr::Literal(x)),
})
}
}
impl<P: Clone> Expr<P> {
pub fn reduce_predicate_and_short_circuit<B, X: Into<ShortCircuit<B>>>(
&self,
mut f: impl FnMut(P) -> X,
) -> Expr<B> {
self.collapse_frames(|e| match e {
ExprFrame::Predicate(p) => match f(p).into() {
ShortCircuit::Known(b) => Expr::Literal(b),
ShortCircuit::Unknown(p) => Expr::Predicate(p),
},
ExprFrame::And(Expr::Literal(false), _) => Expr::Literal(false),
ExprFrame::And(_, Expr::Literal(false)) => Expr::Literal(false),
ExprFrame::And(x, Expr::Literal(true)) => x,
ExprFrame::And(Expr::Literal(true), x) => x,
ExprFrame::And(a, b) => Expr::and(a, b),
ExprFrame::Or(Expr::Literal(true), _) => Expr::Literal(true),
ExprFrame::Or(_, Expr::Literal(true)) => Expr::Literal(true),
ExprFrame::Or(x, Expr::Literal(false)) => x,
ExprFrame::Or(Expr::Literal(false), x) => x,
ExprFrame::Or(a, b) => Expr::or(a, b),
ExprFrame::Not(Expr::Literal(k)) => Expr::Literal(!k),
ExprFrame::Not(x) => Expr::negate(x),
ExprFrame::Literal(x) => Expr::Literal(x),
})
}
}