334 lines
11 KiB
Rust
Raw Normal View History

2024-06-13 11:34:57 +02:00
use std::collections::HashSet;
2024-06-05 20:41:00 +02:00
use std::fmt::Display;
use std::rc::Rc;
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)]
#[serde(rename_all = "camelCase")]
2024-06-05 20:41:00 +02:00
pub enum Expression {
Not(Rc<Expression>),
Binary { left: Rc<Expression>, operator: BinaryOperator, right: Rc<Expression> },
2024-06-05 20:41:00 +02:00
Atomic(String),
}
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-06-05 20:41:00 +02:00
Expression::Atomic(_) => true
}
}
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
}
Expression::Atomic(value) => HashSet::from([value.clone()])
}
}
2024-06-05 20:41:00 +02:00
2024-06-21 18:07:42 +02:00
pub fn eq(&self, other: &Self, ignore_case: bool) -> bool {
match (self, other) {
(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 }) => {
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
}
}
_ => false
}
}
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,
}
}
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 {
matches!(self, Expression::Binary { operator: BinaryOperator::Or, .. })
}
2024-06-28 17:58:56 +02:00
pub fn is_and(&self) -> bool {
matches!(self, Expression::Binary { operator: BinaryOperator::And, .. })
}
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 {
return write!(f, "{}", fmt_helper(self, None));
fn fmt_helper(expression: &Expression, parent: Option<&Expression>) -> String {
match expression {
Expression::Not(expr) if expr.is_atomic() => format!("¬{}", fmt_helper(expr, Some(expression))),
Expression::Not(expr) => format!("¬({})", fmt_helper(expr, Some(expression))),
Expression::Binary { left, operator: BinaryOperator::And, right } => {
format!("{}{}", fmt_helper(left, Some(expression)), fmt_helper(right, Some(expression)))
}
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)))
} else {
format!("({} {})", fmt_helper(left, Some(expression)), fmt_helper(right, Some(expression)))
}
}
Expression::Binary { left, operator: BinaryOperator::Implication, right } => {
format!("{}{}", fmt_helper(left, Some(expression)), fmt_helper(right, Some(expression)))
}
Expression::Atomic(value) => value.clone(),
2024-06-05 20:41:00 +02:00
}
}
}
}
#[cfg(test)]
mod tests {
use crate::expressions::expression::Expression;
use crate::expressions::helpers::{and, atomic, implies, not, or};
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() {
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() {
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");
}
#[test]
fn test_expression_a_or_b() {
let expression = or(
atomic("a"),
atomic("b"),
);
assert_eq!(expression.to_string(), "a b");
}
#[test]
fn test_expression_double_or() {
let expression = or(
atomic("a"),
or(
atomic("b"),
atomic("c"),
),
);
assert_eq!(expression.to_string(), "a b c");
}
#[test]
fn test_expression_triple_or() {
let expression = or(
atomic("a"),
or(
atomic("b"),
or(
atomic("c"),
atomic("d"),
),
),
);
assert_eq!(expression.to_string(), "a b c d");
}
#[test]
fn test_expression_nested_parenthesized_or() {
let expression = or(
atomic("a"),
and(
atomic("b"),
or(
atomic("b"),
atomic("c"),
),
),
);
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() {
let expression = and(
or(
atomic("a"),
atomic("b"),
2024-06-05 20:41:00 +02:00
),
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() {
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() {
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();
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();
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();
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();
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();
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();
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();
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();
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());
}
}