2024-06-05 20:41:00 +02:00
|
|
|
|
use std::fmt::Display;
|
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;
|
|
|
|
|
|
|
|
|
|
pub trait OppositeEq {
|
|
|
|
|
fn opposite_eq(&self, other: &Self) -> bool;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-06 23:53:30 +02:00
|
|
|
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
|
|
|
|
#[serde(rename_all = "camelCase")]
|
2024-06-05 20:41:00 +02:00
|
|
|
|
pub enum Expression {
|
|
|
|
|
Not(Box<Expression>),
|
2024-06-07 16:27:52 +02:00
|
|
|
|
Binary { left: Box<Expression>, operator: BinaryOperator, right: Box<Expression> },
|
2024-06-05 20:41:00 +02:00
|
|
|
|
Atomic(String),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_not(&self) -> bool {
|
|
|
|
|
matches!(self, Expression::Not(_))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn exists(&self, atomic_value: &str) -> bool {
|
|
|
|
|
match self {
|
|
|
|
|
Expression::Not(expr) => expr.exists(atomic_value),
|
2024-06-07 16:27:52 +02:00
|
|
|
|
Expression::Binary { left, right, .. } => left.exists(atomic_value) || right.exists(atomic_value),
|
2024-06-05 20:41:00 +02:00
|
|
|
|
Expression::Atomic(value) => value == atomic_value,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl OppositeEq for Expression {
|
|
|
|
|
fn opposite_eq(&self, other: &Self) -> bool {
|
|
|
|
|
match (self, other) {
|
|
|
|
|
(Expression::Not(_), Expression::Not(_)) => false,
|
2024-06-06 23:53:30 +02:00
|
|
|
|
(Expression::Not(left), right) => left.as_ref() == right,
|
|
|
|
|
(left, Expression::Not(right)) => left == right.as_ref(),
|
2024-06-05 20:41:00 +02:00
|
|
|
|
_ => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 {
|
|
|
|
|
match self {
|
|
|
|
|
Expression::Not(expr) if expr.is_atomic() => write!(f, "¬{expr}"),
|
|
|
|
|
Expression::Not(expr) => write!(f, "¬({expr})"),
|
2024-06-07 16:27:52 +02:00
|
|
|
|
Expression::Binary { left, operator: BinaryOperator::And, right } => {
|
2024-06-05 20:41:00 +02:00
|
|
|
|
write!(f, "{left} ⋀ {right}")
|
|
|
|
|
}
|
|
|
|
|
// TODO do not use parentheses on root level or if several operators are on the same level
|
2024-06-07 16:27:52 +02:00
|
|
|
|
Expression::Binary { left, operator: BinaryOperator::Or, right } => {
|
2024-06-05 20:41:00 +02:00
|
|
|
|
write!(f, "({left} ⋁ {right})")
|
|
|
|
|
}
|
2024-06-07 16:27:52 +02:00
|
|
|
|
Expression::Binary { left, operator: BinaryOperator::Implication, right } => {
|
2024-06-05 20:41:00 +02:00
|
|
|
|
write!(f, "{left} ➔ {right}")
|
|
|
|
|
}
|
|
|
|
|
Expression::Atomic(value) => write!(f, "{value}"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use crate::{and, atomic, implies, not, or};
|
|
|
|
|
use crate::expressions::expression::Expression;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_expression_a_and_not_b_display() {
|
|
|
|
|
let expression = and!(
|
|
|
|
|
atomic!("a"),
|
|
|
|
|
not!(atomic!("b"))
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(expression.to_string(), "a ⋀ ¬b");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[ignore]
|
|
|
|
|
fn test_expression_a_or_b_and_c_display() {
|
|
|
|
|
// TODO
|
|
|
|
|
let expression = or!(
|
|
|
|
|
atomic!("a"),
|
|
|
|
|
and!(
|
|
|
|
|
atomic!("b"),
|
|
|
|
|
atomic!("c")
|
|
|
|
|
));
|
|
|
|
|
assert_eq!(expression.to_string(), "a ⋁ b ⋀ c");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_expression_c_and_a_or_b_display() {
|
|
|
|
|
let expression = and!(
|
|
|
|
|
or!(
|
|
|
|
|
atomic!("a"),
|
|
|
|
|
atomic!("b")
|
|
|
|
|
),
|
|
|
|
|
atomic!("c")
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(expression.to_string(), "(a ⋁ b) ⋀ c");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_expression_a_implies_b_display() {
|
|
|
|
|
let expression = implies!(
|
|
|
|
|
atomic!("a"),
|
|
|
|
|
atomic!("b")
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(expression.to_string(), "a ➔ b");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_expression_not_a_and_b_display() {
|
|
|
|
|
let expression = not!(and!(
|
|
|
|
|
atomic!("a"),
|
|
|
|
|
atomic!("b")
|
|
|
|
|
));
|
|
|
|
|
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"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_str_into_expression_not() {
|
|
|
|
|
let expression: Expression = "!a".try_into().unwrap();
|
|
|
|
|
assert_eq!(expression, not!(atomic!("a")));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_str_into_expression_and() {
|
|
|
|
|
let expression: Expression = "a & b".try_into().unwrap();
|
|
|
|
|
assert_eq!(expression, and!(atomic!("a"), atomic!("b")));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_str_into_expression_or() {
|
|
|
|
|
let expression: Expression = "a | b".try_into().unwrap();
|
|
|
|
|
assert_eq!(expression, or!(atomic!("a"), atomic!("b")));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_str_into_expression_implies() {
|
|
|
|
|
let expression: Expression = "a => b".try_into().unwrap();
|
|
|
|
|
assert_eq!(expression, implies!(atomic!("a"), atomic!("b")));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[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")));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[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"))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[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"))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_str_into_expression_empty() {
|
|
|
|
|
assert!(Expression::try_from("").is_err());
|
|
|
|
|
}
|
|
|
|
|
}
|