2024-06-13 11:34:57 +02:00
|
|
|
|
use std::collections::HashSet;
|
2024-06-05 20:41:00 +02:00
|
|
|
|
use std::fmt::Display;
|
2024-06-14 18:38:37 +02:00
|
|
|
|
use std::rc::Rc;
|
|
|
|
|
|
2024-06-06 23:53:30 +02:00
|
|
|
|
use serde::{Deserialize, Serialize};
|
2024-06-05 20:41:00 +02:00
|
|
|
|
|
|
|
|
|
use crate::expressions::operator::BinaryOperator;
|
|
|
|
|
use crate::parsing::expression_parser::parse_expression;
|
|
|
|
|
|
2024-06-13 11:34:57 +02:00
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
2024-06-06 23:53:30 +02:00
|
|
|
|
#[serde(rename_all = "camelCase")]
|
2024-06-05 20:41:00 +02:00
|
|
|
|
pub enum Expression {
|
2024-06-14 18:38:37 +02:00
|
|
|
|
Not(Rc<Expression>),
|
2024-07-18 17:45:57 +02:00
|
|
|
|
Binary {
|
|
|
|
|
left: Rc<Expression>,
|
|
|
|
|
operator: BinaryOperator,
|
|
|
|
|
right: Rc<Expression>,
|
|
|
|
|
},
|
2024-06-05 20:41:00 +02:00
|
|
|
|
Atomic(String),
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-27 17:55:39 +02:00
|
|
|
|
impl PartialEq<Rc<Expression>> for Expression {
|
|
|
|
|
fn eq(&self, other: &Rc<Expression>) -> bool {
|
|
|
|
|
self == other.as_ref()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-05 20:41:00 +02:00
|
|
|
|
impl Expression {
|
|
|
|
|
pub fn is_atomic(&self) -> bool {
|
|
|
|
|
match self {
|
|
|
|
|
Expression::Not(expr) => expr.is_atomic(),
|
2024-06-07 16:27:52 +02:00
|
|
|
|
Expression::Binary { .. } => false,
|
2024-07-18 17:45:57 +02:00
|
|
|
|
Expression::Atomic(_) => true,
|
2024-06-05 20:41:00 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-13 11:34:57 +02:00
|
|
|
|
pub fn get_atomic_values(&self) -> HashSet<String> {
|
|
|
|
|
match self {
|
|
|
|
|
Expression::Not(expr) => expr.get_atomic_values(),
|
|
|
|
|
Expression::Binary { left, right, .. } => {
|
|
|
|
|
let mut values = left.get_atomic_values();
|
|
|
|
|
values.extend(right.get_atomic_values());
|
|
|
|
|
values
|
|
|
|
|
}
|
2024-07-18 17:45:57 +02:00
|
|
|
|
Expression::Atomic(value) => HashSet::from([value.clone()]),
|
2024-06-13 11:34:57 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-05 20:41:00 +02:00
|
|
|
|
|
2024-07-18 17:45:57 +02:00
|
|
|
|
//TODO replace with eq_ignore_case and use a macro to select correct eq function
|
2024-06-21 18:07:42 +02:00
|
|
|
|
pub fn eq(&self, other: &Self, ignore_case: bool) -> bool {
|
|
|
|
|
match (self, other) {
|
2024-07-18 17:45:57 +02:00
|
|
|
|
(Expression::Not(left), Expression::Not(right)) => {
|
|
|
|
|
Expression::eq(left, right, ignore_case)
|
|
|
|
|
}
|
|
|
|
|
(
|
|
|
|
|
Expression::Binary {
|
|
|
|
|
left: left_left,
|
|
|
|
|
operator: left_operator,
|
|
|
|
|
right: left_right,
|
|
|
|
|
},
|
|
|
|
|
Expression::Binary {
|
|
|
|
|
left: right_left,
|
|
|
|
|
operator: right_operator,
|
|
|
|
|
right: right_right,
|
|
|
|
|
},
|
|
|
|
|
) => {
|
2024-06-21 18:07:42 +02:00
|
|
|
|
Expression::eq(left_left, right_left, ignore_case)
|
|
|
|
|
&& left_operator == right_operator
|
|
|
|
|
&& Expression::eq(left_right, right_right, ignore_case)
|
|
|
|
|
}
|
|
|
|
|
(Expression::Atomic(left), Expression::Atomic(right)) => {
|
|
|
|
|
if ignore_case {
|
|
|
|
|
left.eq_ignore_ascii_case(right)
|
|
|
|
|
} else {
|
|
|
|
|
left == right
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-18 17:45:57 +02:00
|
|
|
|
_ => false,
|
2024-06-21 18:07:42 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn opposite_eq(&self, other: &Self, ignore_case: bool) -> bool {
|
2024-06-05 20:41:00 +02:00
|
|
|
|
match (self, other) {
|
|
|
|
|
(Expression::Not(_), Expression::Not(_)) => false,
|
2024-06-21 18:07:42 +02:00
|
|
|
|
(Expression::Not(left), right) => left.as_ref().eq(right, ignore_case),
|
|
|
|
|
(left, Expression::Not(right)) => left.eq(right.as_ref(), ignore_case),
|
2024-06-05 20:41:00 +02:00
|
|
|
|
_ => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-27 17:55:39 +02:00
|
|
|
|
|
|
|
|
|
pub fn is_in(&self, other: &Self) -> bool {
|
|
|
|
|
if let Expression::Binary { left, right, .. } = other {
|
|
|
|
|
self == left || self == right
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-28 17:58:56 +02:00
|
|
|
|
pub fn is_or(&self) -> bool {
|
2024-07-18 17:45:57 +02:00
|
|
|
|
matches!(
|
|
|
|
|
self,
|
|
|
|
|
Expression::Binary {
|
|
|
|
|
operator: BinaryOperator::Or,
|
|
|
|
|
..
|
|
|
|
|
}
|
|
|
|
|
)
|
2024-06-27 17:55:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-28 17:58:56 +02:00
|
|
|
|
pub fn is_and(&self) -> bool {
|
2024-07-18 17:45:57 +02:00
|
|
|
|
matches!(
|
|
|
|
|
self,
|
|
|
|
|
Expression::Binary {
|
|
|
|
|
operator: BinaryOperator::And,
|
|
|
|
|
..
|
|
|
|
|
}
|
|
|
|
|
)
|
2024-06-27 17:55:39 +02:00
|
|
|
|
}
|
2024-06-05 20:41:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> TryFrom<&'a str> for Expression {
|
|
|
|
|
type Error = nom::Err<nom::error::Error<&'a str>>;
|
|
|
|
|
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
|
|
|
|
|
parse_expression(value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TryFrom<String> for Expression {
|
|
|
|
|
type Error = nom::Err<nom::error::Error<String>>;
|
|
|
|
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
|
|
|
|
Self::try_from(value.as_str())
|
|
|
|
|
.map_err(|err| err.map(|err| nom::error::Error::new(err.input.into(), err.code)))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for Expression {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
2024-06-17 11:41:28 +02:00
|
|
|
|
return write!(f, "{}", fmt_helper(self, None));
|
|
|
|
|
|
|
|
|
|
fn fmt_helper(expression: &Expression, parent: Option<&Expression>) -> String {
|
|
|
|
|
match expression {
|
2024-07-18 17:45:57 +02:00
|
|
|
|
Expression::Not(expr) if expr.is_atomic() => {
|
|
|
|
|
format!("¬{}", fmt_helper(expr, Some(expression)))
|
|
|
|
|
}
|
2024-06-17 11:41:28 +02:00
|
|
|
|
Expression::Not(expr) => format!("¬({})", fmt_helper(expr, Some(expression))),
|
2024-07-18 17:45:57 +02:00
|
|
|
|
Expression::Binary {
|
|
|
|
|
left,
|
|
|
|
|
operator: BinaryOperator::And,
|
|
|
|
|
right,
|
|
|
|
|
} => {
|
|
|
|
|
format!(
|
|
|
|
|
"{} ⋀ {}",
|
|
|
|
|
fmt_helper(left, Some(expression)),
|
|
|
|
|
fmt_helper(right, Some(expression))
|
|
|
|
|
)
|
2024-06-17 11:41:28 +02:00
|
|
|
|
}
|
2024-07-18 17:45:57 +02:00
|
|
|
|
Expression::Binary {
|
|
|
|
|
left,
|
|
|
|
|
operator: BinaryOperator::Or,
|
|
|
|
|
right,
|
|
|
|
|
} => {
|
|
|
|
|
if parent.is_none()
|
|
|
|
|
|| matches!(
|
|
|
|
|
parent,
|
|
|
|
|
Some(
|
|
|
|
|
Expression::Not(_)
|
|
|
|
|
| Expression::Binary {
|
|
|
|
|
operator: BinaryOperator::Or | BinaryOperator::Implication,
|
|
|
|
|
..
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
format!(
|
|
|
|
|
"{} ⋁ {}",
|
|
|
|
|
fmt_helper(left, Some(expression)),
|
|
|
|
|
fmt_helper(right, Some(expression))
|
|
|
|
|
)
|
2024-06-17 11:41:28 +02:00
|
|
|
|
} else {
|
2024-07-18 17:45:57 +02:00
|
|
|
|
format!(
|
|
|
|
|
"({} ⋁ {})",
|
|
|
|
|
fmt_helper(left, Some(expression)),
|
|
|
|
|
fmt_helper(right, Some(expression))
|
|
|
|
|
)
|
2024-06-17 11:41:28 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-18 17:45:57 +02:00
|
|
|
|
Expression::Binary {
|
|
|
|
|
left,
|
|
|
|
|
operator: BinaryOperator::Implication,
|
|
|
|
|
right,
|
|
|
|
|
} => {
|
|
|
|
|
format!(
|
|
|
|
|
"{} ➔ {}",
|
|
|
|
|
fmt_helper(left, Some(expression)),
|
|
|
|
|
fmt_helper(right, Some(expression))
|
|
|
|
|
)
|
2024-06-17 11:41:28 +02:00
|
|
|
|
}
|
|
|
|
|
Expression::Atomic(value) => value.clone(),
|
2024-06-05 20:41:00 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use crate::expressions::expression::Expression;
|
2024-06-14 18:38:37 +02:00
|
|
|
|
use crate::expressions::helpers::{and, atomic, implies, not, or};
|
2024-06-08 21:41:30 +02:00
|
|
|
|
|
2024-06-21 18:07:42 +02:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_eq_ignore_case_atomics() {
|
|
|
|
|
let expression_lower = atomic("a");
|
|
|
|
|
let expression_upper = atomic("A");
|
|
|
|
|
assert!(expression_lower.eq(&expression_upper, true));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_eq_ignore_case_not() {
|
|
|
|
|
let expression_lower = not(atomic("a"));
|
|
|
|
|
let expression_upper = not(atomic("A"));
|
|
|
|
|
assert!(expression_lower.eq(&expression_upper, true));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_eq_ignore_case_and() {
|
|
|
|
|
let expression_lower = and(atomic("a"), atomic("b"));
|
|
|
|
|
let expression_upper = and(atomic("A"), atomic("B"));
|
|
|
|
|
assert!(expression_lower.eq(&expression_upper, true));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_eq_ignore_case_equal() {
|
|
|
|
|
let expression_lower = or(atomic("a"), atomic("b"));
|
|
|
|
|
let expression_upper = or(atomic("a"), atomic("b"));
|
|
|
|
|
assert!(expression_lower.eq(&expression_upper, true));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_eq_ignore_case_unequal() {
|
|
|
|
|
let expression_lower = or(atomic("a"), atomic("b"));
|
|
|
|
|
let expression_upper = or(atomic("a"), atomic("c"));
|
|
|
|
|
assert!(!expression_lower.eq(&expression_upper, true));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_eq_dont_ignore_case() {
|
|
|
|
|
let expression_lower = or(atomic("a"), atomic("b"));
|
|
|
|
|
let expression_upper = or(atomic("a"), atomic("B"));
|
|
|
|
|
assert!(!expression_lower.eq(&expression_upper, false));
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-05 20:41:00 +02:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_expression_a_and_not_b_display() {
|
2024-07-18 17:45:57 +02:00
|
|
|
|
let expression = and(atomic("a"), not(atomic("b")));
|
2024-06-05 20:41:00 +02:00
|
|
|
|
assert_eq!(expression.to_string(), "a ⋀ ¬b");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_expression_a_or_b_and_c_display() {
|
2024-07-18 17:45:57 +02:00
|
|
|
|
let expression = or(atomic("a"), and(atomic("b"), atomic("c")));
|
2024-06-05 20:41:00 +02:00
|
|
|
|
assert_eq!(expression.to_string(), "a ⋁ b ⋀ c");
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 11:41:28 +02:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_expression_a_or_b() {
|
2024-07-18 17:45:57 +02:00
|
|
|
|
let expression = or(atomic("a"), atomic("b"));
|
2024-06-17 11:41:28 +02:00
|
|
|
|
assert_eq!(expression.to_string(), "a ⋁ b");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_expression_double_or() {
|
2024-07-18 17:45:57 +02:00
|
|
|
|
let expression = or(atomic("a"), or(atomic("b"), atomic("c")));
|
2024-06-17 11:41:28 +02:00
|
|
|
|
assert_eq!(expression.to_string(), "a ⋁ b ⋁ c");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_expression_triple_or() {
|
2024-07-18 17:45:57 +02:00
|
|
|
|
let expression = or(atomic("a"), or(atomic("b"), or(atomic("c"), atomic("d"))));
|
2024-06-17 11:41:28 +02:00
|
|
|
|
assert_eq!(expression.to_string(), "a ⋁ b ⋁ c ⋁ d");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_expression_nested_parenthesized_or() {
|
2024-07-18 17:45:57 +02:00
|
|
|
|
let expression = or(atomic("a"), and(atomic("b"), or(atomic("b"), atomic("c"))));
|
2024-06-17 11:41:28 +02:00
|
|
|
|
assert_eq!(expression.to_string(), "a ⋁ b ⋀ (b ⋁ c)");
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-05 20:41:00 +02:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_expression_c_and_a_or_b_display() {
|
2024-07-18 17:45:57 +02:00
|
|
|
|
let expression = and(or(atomic("a"), atomic("b")), atomic("c"));
|
2024-06-05 20:41:00 +02:00
|
|
|
|
assert_eq!(expression.to_string(), "(a ⋁ b) ⋀ c");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_expression_a_implies_b_display() {
|
2024-07-18 17:45:57 +02:00
|
|
|
|
let expression = implies(atomic("a"), atomic("b"));
|
2024-06-05 20:41:00 +02:00
|
|
|
|
assert_eq!(expression.to_string(), "a ➔ b");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_expression_not_a_and_b_display() {
|
2024-07-18 17:45:57 +02:00
|
|
|
|
let expression = not(and(atomic("a"), atomic("b")));
|
2024-06-05 20:41:00 +02:00
|
|
|
|
assert_eq!(expression.to_string(), "¬(a ⋀ b)");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_str_into_expression_atomic() {
|
|
|
|
|
let expression: Expression = "a".try_into().unwrap();
|
2024-06-14 18:38:37 +02:00
|
|
|
|
assert_eq!(expression, atomic("a"));
|
2024-06-05 20:41:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_str_into_expression_not() {
|
|
|
|
|
let expression: Expression = "!a".try_into().unwrap();
|
2024-06-14 18:38:37 +02:00
|
|
|
|
assert_eq!(expression, not(atomic("a")));
|
2024-06-05 20:41:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_str_into_expression_and() {
|
|
|
|
|
let expression: Expression = "a & b".try_into().unwrap();
|
2024-06-14 18:38:37 +02:00
|
|
|
|
assert_eq!(expression, and(atomic("a"), atomic("b")));
|
2024-06-05 20:41:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_str_into_expression_or() {
|
|
|
|
|
let expression: Expression = "a | b".try_into().unwrap();
|
2024-06-14 18:38:37 +02:00
|
|
|
|
assert_eq!(expression, or(atomic("a"), atomic("b")));
|
2024-06-05 20:41:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_str_into_expression_implies() {
|
|
|
|
|
let expression: Expression = "a => b".try_into().unwrap();
|
2024-06-14 18:38:37 +02:00
|
|
|
|
assert_eq!(expression, implies(atomic("a"), atomic("b")));
|
2024-06-05 20:41:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_str_into_expression_complex() {
|
|
|
|
|
let expression: Expression = "a & b | c".try_into().unwrap();
|
2024-06-14 18:38:37 +02:00
|
|
|
|
assert_eq!(expression, or(and(atomic("a"), atomic("b")), atomic("c")));
|
2024-06-05 20:41:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_str_into_expression_complex_parentheses() {
|
|
|
|
|
let expression: Expression = "a & (b | c)".try_into().unwrap();
|
2024-06-14 18:38:37 +02:00
|
|
|
|
assert_eq!(expression, and(atomic("a"), or(atomic("b"), atomic("c"))));
|
2024-06-05 20:41:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_str_into_expression_very_complex_parentheses() {
|
|
|
|
|
let expression: Expression = "(a & b) | c => (d & e)".try_into().unwrap();
|
2024-07-18 17:45:57 +02:00
|
|
|
|
assert_eq!(
|
|
|
|
|
expression,
|
|
|
|
|
implies(
|
|
|
|
|
or(and(atomic("a"), atomic("b")), atomic("c")),
|
|
|
|
|
and(atomic("d"), atomic("e"))
|
|
|
|
|
)
|
|
|
|
|
);
|
2024-06-05 20:41:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_str_into_expression_empty() {
|
|
|
|
|
assert!(Expression::try_from("").is_err());
|
|
|
|
|
}
|
|
|
|
|
}
|