Replaced macros with functions and Box with Rc

This commit is contained in:
Martin Berg Alstad 2024-06-14 18:38:37 +02:00
parent db4dc2db54
commit 4b22fc4a7e
9 changed files with 280 additions and 369 deletions

View File

@ -10,7 +10,7 @@ nom = "7.1.3"
# Async
tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread"] }
# Serialization / Deserialization
serde = { version = "1.0.203", features = ["derive"] }
serde = { version = "1.0.203", features = ["derive", "rc"] }
# API
axum = "0.7.5"
tower-http = { version = "0.5.2", features = ["cors", "trace"] }

View File

@ -1,7 +1,7 @@
# Creates a new cargo project, copies the Cargo.toml and Cargo.lock files to the new project,
# builds the project, and then copies the built binary to a new image.
FROM rust:1.78 as build
FROM rust:1.79 as build
LABEL authors="Martin Berg Alstad"
RUN USER=root cargo new --bin simplify_truths

View File

@ -1,8 +1,10 @@
use std::collections::HashSet;
use std::fmt::Display;
use serde::{Deserialize, Serialize};
use crate::expressions::iterator::ExpressionIterator;
use std::rc::Rc;
use serde::{Deserialize, Serialize};
use crate::expressions::iterator::ExpressionIterator;
use crate::expressions::operator::BinaryOperator;
use crate::parsing::expression_parser::parse_expression;
@ -13,8 +15,8 @@ pub trait OppositeEq {
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum Expression {
Not(Box<Expression>),
Binary { left: Box<Expression>, operator: BinaryOperator, right: Box<Expression> },
Not(Rc<Expression>),
Binary { left: Rc<Expression>, operator: BinaryOperator, right: Rc<Expression> },
Atomic(String),
}
@ -27,14 +29,6 @@ impl Expression {
}
}
pub fn exists(&self, atomic_value: &str) -> bool {
match self {
Expression::Not(expr) => expr.exists(atomic_value),
Expression::Binary { left, right, .. } => left.exists(atomic_value) || right.exists(atomic_value),
Expression::Atomic(value) => value == atomic_value,
}
}
pub fn get_atomic_values(&self) -> HashSet<String> {
match self {
Expression::Not(expr) => expr.get_atomic_values(),
@ -47,33 +41,13 @@ impl Expression {
}
}
pub fn count_distinct(&self) -> usize {
self.count_distinct_with_visited(vec![].as_mut())
}
fn count_distinct_with_visited(&self, visited: &mut Vec<String>) -> usize {
match self {
Expression::Not(expr) => expr.count_distinct_with_visited(visited),
Expression::Binary { left, right, .. } =>
left.count_distinct_with_visited(visited) + right.count_distinct_with_visited(visited),
Expression::Atomic(value) => {
if visited.contains(value) {
0
} else {
visited.push(value.clone());
1
}
}
}
}
pub fn iter(&self) -> ExpressionIterator {
ExpressionIterator::new(self.clone())
}
}
impl IntoIterator for Expression {
type Item = Expression;
type Item = Rc<Expression>;
type IntoIter = ExpressionIterator;
fn into_iter(self) -> Self::IntoIter {
@ -129,38 +103,14 @@ impl Display for Expression {
#[cfg(test)]
mod tests {
use crate::{and, atomic, implies, not, or};
use crate::expressions::expression::Expression;
#[test]
fn test_count_distinct() {
let expression = and!(
atomic!("a"),
or!(
atomic!("b"),
atomic!("c")
)
);
assert_eq!(expression.count_distinct(), 3);
}
#[test]
fn test_count_distinct_duplicates() {
let expression = and!(
atomic!("a"),
or!(
atomic!("b"),
atomic!("a")
)
);
assert_eq!(expression.count_distinct(), 2);
}
use crate::expressions::helpers::{and, atomic, implies, not, or};
#[test]
fn test_expression_a_and_not_b_display() {
let expression = and!(
atomic!("a"),
not!(atomic!("b"))
let expression = and(
atomic("a"),
not(atomic("b")),
);
assert_eq!(expression.to_string(), "a ⋀ ¬b");
}
@ -169,41 +119,41 @@ mod tests {
#[ignore]
fn test_expression_a_or_b_and_c_display() {
// TODO
let expression = or!(
atomic!("a"),
and!(
atomic!("b"),
atomic!("c")
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")
let expression = and(
or(
atomic("a"),
atomic("b"),
),
atomic!("c")
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")
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")
let expression = not(and(
atomic("a"),
atomic("b"),
));
assert_eq!(expression.to_string(), "¬(a ⋀ b)");
}
@ -211,49 +161,49 @@ mod tests {
#[test]
fn test_from_str_into_expression_atomic() {
let expression: Expression = "a".try_into().unwrap();
assert_eq!(expression, atomic!("a"));
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")));
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")));
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")));
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")));
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")));
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"))));
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"))));
assert_eq!(expression, implies(or(and(atomic("a"), atomic("b")), atomic("c")), and(atomic("d"), atomic("e"))));
}
#[test]

View File

@ -1,93 +1,51 @@
#[macro_export]
macro_rules! and {
($left:expr, $right:expr) => {
$crate::binary!($left, $crate::expressions::operator::BinaryOperator::And, $right)
};
use std::rc::Rc;
use crate::expressions::expression::Expression;
use crate::expressions::operator::BinaryOperator;
#[inline]
pub fn and<L, R>(left: L, right: R) -> Expression
where
L: Into<Rc<Expression>>,
R: Into<Rc<Expression>>,
{
binary(left, BinaryOperator::And, right)
}
#[macro_export]
macro_rules! or {
($left:expr, $right:expr) => {
$crate::binary!($left, $crate::expressions::operator::BinaryOperator::Or, $right)
};
#[inline]
pub fn or<L, R>(left: L, right: R) -> Expression
where
L: Into<Rc<Expression>>,
R: Into<Rc<Expression>>,
{
binary(left, BinaryOperator::Or, right)
}
#[macro_export]
macro_rules! implies {
($left:expr, $right:expr) => {
$crate::binary!($left, $crate::expressions::operator::BinaryOperator::Implication, $right)
};
#[inline]
pub fn implies<L, R>(left: L, right: R) -> Expression
where
L: Into<Rc<Expression>>,
R: Into<Rc<Expression>>,
{
binary(left, BinaryOperator::Implication, right)
}
#[macro_export]
macro_rules! binary {
($left:expr, $operator:expr, $right:expr) => {
$crate::expressions::expression::Expression::Binary { left: Box::new($left), operator: $operator, right: Box::new($right) }
};
#[inline]
pub fn binary<L, R>(left: L, operator: BinaryOperator, right: R) -> Expression
where
L: Into<Rc<Expression>>,
R: Into<Rc<Expression>>,
{
Expression::Binary { left: left.into(), operator, right: right.into() }
}
#[macro_export]
macro_rules! not {
($value:expr) => {
$crate::expressions::expression::Expression::Not(Box::new($value))
};
#[inline]
pub fn not<T: Into<Rc<Expression>>>(value: T) -> Expression {
Expression::Not(value.into())
}
#[macro_export]
macro_rules! atomic {
($value:expr) => {
$crate::expressions::expression::Expression::Atomic($value.to_string())
};
#[inline]
pub fn atomic<T: Into<String>>(value: T) -> Expression {
Expression::Atomic(value.into())
}
// TODO
#[macro_export]
macro_rules! eval {
($a:literal && $b:literal) => {
$crate::and!($crate::eval!($a), $crate::eval!($b))
};
($a:literal || $b:literal) => {
$crate::or!($crate::eval!($a), $crate::eval!($b))
};
($a:literal => $b:literal) => {
$crate::implies!($crate::eval!($a), $crate::eval!($b))
};
(!$a:expr) => {
$crate::not!($crate::eval!($a))
};
($value:expr) => {
$crate::atomic!($value)
};
}
#[cfg(test)]
mod tests {
use crate::eval;
use crate::expressions::expression::Expression::{Atomic, Binary, Not};
use crate::expressions::operator::BinaryOperator::{And, Implication, Or};
#[test]
fn eval_atomic() {
assert_eq!(eval!("a"), Atomic("a".to_string()));
}
#[test]
fn eval_not() {
assert_eq!(eval!(!"a"), Not(Box::new(Atomic("a".to_string()))));
}
#[test]
fn eval_and() {
assert_eq!(eval!("a" && "b"), Binary { left: Box::new(Atomic("a".to_string())), operator: And, right: Box::new(Atomic("b".to_string())) });
}
#[test]
fn eval_or() {
assert_eq!(eval!("a" || "b"), Binary { left: Box::new(Atomic("a".to_string())), operator: Or, right: Box::new(Atomic("b".to_string())) });
}
#[test]
fn eval_implies() {
assert_eq!(eval!("a" => "b"), Binary { left: Box::new(Atomic("a".to_string())), operator: Implication, right: Box::new(Atomic("b".to_string())) });
}
}
// TODO eval function using nom parser

View File

@ -1,49 +1,50 @@
use std::ops::Deref;
use std::rc::Rc;
use crate::expressions::expression::Expression;
pub struct ExpressionIterator {
stack: Vec<Expression>,
stack: Vec<Rc<Expression>>,
}
impl ExpressionIterator {
pub fn new(expression: Expression) -> Self {
let stack = vec![expression];
let stack = vec![expression.into()];
Self { stack }
}
}
impl Iterator for ExpressionIterator {
type Item = Expression;
type Item = Rc<Expression>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(expression) = self.stack.pop() {
match &expression {
Expression::Atomic(_) => Some(expression),
Expression::Not(inner) => {
self.stack.push(*inner.clone());
Some(expression)
}
Expression::Binary { left, right, .. } => {
self.stack.push(*right.clone());
self.stack.push(*left.clone());
Some(expression)
}
let expression = self.stack.pop()?;
match expression.deref() {
Expression::Atomic(_) => Some(expression),
Expression::Not(inner) => {
self.stack.push(inner.clone());
Some(expression)
}
Expression::Binary { left, right, .. } => {
self.stack.push(right.clone());
self.stack.push(left.clone());
Some(expression)
}
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use crate::expressions::helpers::{and, atomic, not};
#[test]
fn test_expression_iterator() {
let expression = not!(and!(atomic!("A"), atomic!("B")));
let expression = not(and(atomic("A"), atomic("B")));
let mut iterator = expression.iter();
assert_eq!(iterator.next().unwrap(), expression);
assert_eq!(iterator.next().unwrap(), and!(atomic!("A"), atomic!("B")));
assert_eq!(iterator.next().unwrap(), atomic!("A"));
assert_eq!(iterator.next().unwrap(), atomic!("B"));
assert_eq!(iterator.next().unwrap(), expression.into());
assert_eq!(iterator.next().unwrap(), and(atomic("A"), atomic("B")).into());
assert_eq!(iterator.next().unwrap(), atomic("A").into());
assert_eq!(iterator.next().unwrap(), atomic("B").into());
assert_eq!(iterator.next(), None);
}
}

View File

@ -1,6 +1,5 @@
pub mod expression;
pub mod operator;
#[macro_use]
pub mod helpers;
pub mod simplify;
pub mod truth_table;

View File

@ -1,4 +1,6 @@
use std::ops::Deref;
use crate::expressions::expression::{Expression, OppositeEq};
use crate::expressions::helpers::{and, binary, not, or};
use crate::expressions::operator::BinaryOperator;
pub trait Simplify {
@ -27,16 +29,16 @@ impl Simplify for Expression {
/// This is done by replacing `a ➔ b` with `¬a b`.
fn elimination_of_implication(&self) -> Self {
match self {
Expression::Not(expr) => not!(expr.elimination_of_implication()),
Expression::Not(expr) => not(expr.elimination_of_implication()),
Expression::Binary { left, operator: BinaryOperator::Implication, right } => {
let left = left.elimination_of_implication();
let right = right.elimination_of_implication();
or!(not!(left), right)
or(not(left), right)
}
Expression::Binary { left, operator, right } => {
let left = left.elimination_of_implication();
let right = right.elimination_of_implication();
binary!(left, *operator, right)
binary(left, *operator, right)
}
atomic @ Expression::Atomic(_) => atomic.clone(),
}
@ -48,16 +50,16 @@ impl Simplify for Expression {
fn double_negation_elimination(&self) -> Self {
match self {
Expression::Not(expr) => {
if let Expression::Not(inner) = *expr.clone() {
if let Expression::Not(inner) = expr.deref() {
inner.double_negation_elimination()
} else {
not!(expr.double_negation_elimination())
not(expr.double_negation_elimination())
}
}
Expression::Binary { left, operator, right } => {
let left = left.double_negation_elimination();
let right = right.double_negation_elimination();
binary!(left, *operator, right)
binary(left, *operator, right)
}
atomic @ Expression::Atomic(_) => atomic.clone(),
}
@ -66,25 +68,25 @@ impl Simplify for Expression {
fn de_morgans_laws(&self) -> Self {
match self {
Expression::Not(expr) => {
match *expr.clone() {
match expr.deref() {
Expression::Binary { left, operator: BinaryOperator::And, right } => {
// TODO unnecessary cloning calls to de_morgans_laws?
let left = not!(left.de_morgans_laws());
let right = not!(right.de_morgans_laws());
or!(left, right).de_morgans_laws()
let left = not(left.de_morgans_laws());
let right = not(right.de_morgans_laws());
or(left, right).de_morgans_laws()
}
Expression::Binary { left, operator: BinaryOperator::Or, right } => {
let left = not!(left.de_morgans_laws());
let right = not!(right.de_morgans_laws());
and!(left, right).de_morgans_laws()
let left = not(left.de_morgans_laws());
let right = not(right.de_morgans_laws());
and(left, right).de_morgans_laws()
}
_ => not!(expr.de_morgans_laws()),
_ => not(expr.de_morgans_laws()),
}
}
Expression::Binary { left, operator, right } => {
let left = left.de_morgans_laws();
let right = right.de_morgans_laws();
binary!(left, *operator, right)
binary(left, *operator, right)
}
atomic @ Expression::Atomic(_) => atomic.clone(),
}
@ -101,26 +103,26 @@ impl Simplify for Expression {
return left.absorption_law();
} else if right_left.is_atomic() && right_right.is_atomic() && left.opposite_eq(right_left) {
if left.opposite_eq(right_left) {
return and!(left.absorption_law(), right_left.absorption_law());
return and(left.absorption_law(), right_left.absorption_law());
} else if left.opposite_eq(right_right) {
return and!(left.absorption_law(), right_right.absorption_law());
return and(left.absorption_law(), right_right.absorption_law());
}
}
and!(left.absorption_law(), right.absorption_law())
and(left.absorption_law(), right.absorption_law())
}
(Expression::Binary { left: left_left, operator: BinaryOperator::Or, right: left_right }, _) => {
if right_ref == left_left.as_ref() || right_ref == left_right.as_ref() {
return right.absorption_law();
} else if left_left.is_atomic() && left_right.is_atomic() && right.opposite_eq(left_left) {
if right.opposite_eq(left_left) {
return and!(left_right.absorption_law(), right.absorption_law());
return and(left_right.absorption_law(), right.absorption_law());
} else if right.opposite_eq(left_right) {
return and!(left_left.absorption_law(), right.absorption_law());
return and(left_left.absorption_law(), right.absorption_law());
}
}
and!(left.absorption_law(), right.absorption_law())
and(left.absorption_law(), right.absorption_law())
}
(left, right) => and!(left.absorption_law(), right.absorption_law())
(left, right) => and(left.absorption_law(), right.absorption_law())
}
}
Expression::Binary { left, operator: BinaryOperator::Or, right } => {
@ -131,34 +133,34 @@ impl Simplify for Expression {
return left.absorption_law();
} else if right_left.is_atomic() && right_right.is_atomic() && left.opposite_eq(right_left) {
if left.opposite_eq(right_left) {
return or!(left.absorption_law(), right_left.absorption_law());
return or(left.absorption_law(), right_left.absorption_law());
} else if left.opposite_eq(right_right) {
return or!(left.absorption_law(), right_right.absorption_law());
return or(left.absorption_law(), right_right.absorption_law());
}
}
or!(left.absorption_law(), right.absorption_law())
or(left.absorption_law(), right.absorption_law())
}
(Expression::Binary { left: left_left, operator: BinaryOperator::And, right: left_right }, _) => {
if right_ref == left_left.as_ref() || right_ref == left_right.as_ref() {
return right.absorption_law();
} else if left_left.is_atomic() && left_right.is_atomic() && right.opposite_eq(left_left) {
if right.opposite_eq(left_left) {
return or!(left_right.absorption_law(), right.absorption_law());
return or(left_right.absorption_law(), right.absorption_law());
} else if right.opposite_eq(left_right) {
return or!(left_left.absorption_law(), right.absorption_law());
return or(left_left.absorption_law(), right.absorption_law());
}
}
or!(left.absorption_law(), right.absorption_law())
or(left.absorption_law(), right.absorption_law())
}
(left, right) => or!(left.absorption_law(), right.absorption_law())
(left, right) => or(left.absorption_law(), right.absorption_law())
}
}
Expression::Binary { left, operator, right } => {
let left = left.absorption_law();
let right = right.absorption_law();
binary!(left, *operator, right)
binary(left, *operator, right)
}
Expression::Not(expr) => not!(expr.absorption_law()),
Expression::Not(expr) => not(expr.absorption_law()),
atomic => atomic.clone(),
}
}
@ -175,14 +177,14 @@ impl Simplify for Expression {
(Expression::Atomic(_), Expression::Binary { left: right_left, operator: BinaryOperator::Or, right: right_right }) => {
let right_left = right_left.distribution_law();
let right_right = right_right.distribution_law();
or!(and!(*left.clone(), right_left), and!(*left.clone(), right_right))
or(and(left.clone(), right_left), and(left.clone(), right_right))
}
(Expression::Binary { left: left_left, operator: BinaryOperator::Or, right: left_right }, Expression::Atomic(_)) => {
let left_left = left_left.distribution_law();
let left_right = left_right.distribution_law();
or!(and!(left_left, *right.clone()), and!(left_right, *right.clone()))
or(and(left_left, right.clone()), and(left_right, right.clone()))
}
(left, right) => and!(left.distribution_law(), right.distribution_law())
(left, right) => and(left.distribution_law(), right.distribution_law())
}
}
Expression::Binary { left, operator: BinaryOperator::Or, right } => {
@ -190,22 +192,22 @@ impl Simplify for Expression {
(Expression::Atomic(_), Expression::Binary { left: right_left, operator: BinaryOperator::And, right: right_right }) => {
let right_left = right_left.distribution_law();
let right_right = right_right.distribution_law();
and!(or!(*left.clone(), right_left), or!(*left.clone(), right_right))
and(or(left.clone(), right_left), or(left.clone(), right_right))
}
(Expression::Binary { left: left_left, operator: BinaryOperator::And, right: left_right }, Expression::Atomic(_)) => {
let left_left = left_left.distribution_law();
let left_right = left_right.distribution_law();
and!(or!(left_left, *right.clone()), or!(left_right, *right.clone()))
and(or(left_left, right.clone()), or(left_right, right.clone()))
}
(left, right) => or!(left.distribution_law(), right.distribution_law())
(left, right) => or(left.distribution_law(), right.distribution_law())
}
}
Expression::Binary { left, operator, right } => {
let left = left.distribution_law();
let right = right.distribution_law();
binary!(left, *operator, right)
binary(left, *operator, right)
}
Expression::Not(expr) => not!(expr.distribution_law()),
Expression::Not(expr) => not(expr.distribution_law()),
atomic => atomic.clone(),
}
}
@ -217,165 +219,166 @@ impl Simplify for Expression {
#[cfg(test)]
mod tests {
use crate::expressions::helpers::{and, atomic, implies, not, or};
use crate::expressions::simplify::Simplify;
#[test]
fn test_simplify() {
let expression = eval!("a" => "b").simplify();
assert_eq!(expression, or!(not!(atomic!("a")), atomic!("b")));
let expression = implies(atomic("a"), atomic("b")).simplify();
assert_eq!(expression, or(not(atomic("a")), atomic("b")));
}
#[test]
fn test_implication_and_de_morgans() {
let expression = implies!(and!(not!(atomic!("a")), atomic!("b")), atomic!("c")).simplify();
assert_eq!(expression, or!(or!(atomic!("a"), not!(atomic!("b"))), atomic!("c")));
let expression = implies(and(not(atomic("a")), atomic("b")), atomic("c")).simplify();
assert_eq!(expression, or(or(atomic("a"), not(atomic("b"))), atomic("c")));
}
#[test]
fn test_elimination_of_implication() {
let expression = eval!("a" => "b").elimination_of_implication();
assert_eq!(expression, or!(not!(atomic!("a")), atomic!("b")));
let expression = implies(atomic("a"), atomic("b")).elimination_of_implication();
assert_eq!(expression, or(not(atomic("a")), atomic("b")));
}
#[test]
fn test_elimination_of_implication_nested() {
let expression = implies!(atomic!("a"), implies!(atomic!("b"), atomic!("c"))).elimination_of_implication();
assert_eq!(expression, or!(not!(atomic!("a")), or!(not!(atomic!("b")), atomic!("c"))));
let expression = implies(atomic("a"), implies(atomic("b"), atomic("c"))).elimination_of_implication();
assert_eq!(expression, or(not(atomic("a")), or(not(atomic("b")), atomic("c"))));
}
#[test]
fn test_elimination_of_implication_none() {
let expression = eval!("a" && "b").elimination_of_implication();
assert_eq!(expression, eval!("a" && "b"));
let expression = and(atomic("a"), atomic("b")).elimination_of_implication();
assert_eq!(expression, and(atomic("a"), atomic("b")));
}
#[test]
fn test_elimination_of_implication_nested_none() {
let expression = or!(atomic!("a"), and!(atomic!("b"), atomic!("c"))).elimination_of_implication();
assert_eq!(expression, or!(atomic!("a"), and!(atomic!("b"), atomic!("c"))));
let expression = or(atomic("a"), and(atomic("b"), atomic("c"))).elimination_of_implication();
assert_eq!(expression, or(atomic("a"), and(atomic("b"), atomic("c"))));
}
#[test]
fn test_double_negation_elimination() {
let expression = not!(not!(atomic!("a"))).double_negation_elimination();
assert_eq!(expression, atomic!("a"));
let expression = not(not(atomic("a"))).double_negation_elimination();
assert_eq!(expression, atomic("a"));
}
#[test]
fn test_triple_negation_elimination() {
let expression = not!(not!(not!(atomic!("a")))).double_negation_elimination();
assert_eq!(expression, not!(atomic!("a")));
let expression = not(not(not(atomic("a")))).double_negation_elimination();
assert_eq!(expression, not(atomic("a")));
}
#[test]
fn test_five_negation_elimination() {
let expression = not!(not!(not!(not!(not!(atomic!("a")))))).double_negation_elimination();
assert_eq!(expression, not!(atomic!("a")));
let expression = not(not(not(not(not(atomic("a")))))).double_negation_elimination();
assert_eq!(expression, not(atomic("a")));
}
#[test]
fn test_no_negation_elimination() {
let expression = atomic!("a").double_negation_elimination();
assert_eq!(expression, atomic!("a"));
let expression = atomic("a").double_negation_elimination();
assert_eq!(expression, atomic("a"));
}
#[test]
fn test_double_negation_nested_elimination() {
let expression = and!(or!(not!(eval!(!"a")), eval!("b")), not!(eval!(!"c"))).double_negation_elimination();
assert_eq!(expression, and!(or!(atomic!("a"), atomic!("b")), atomic!("c")));
let expression = and(or(not(not(atomic("a"))), atomic("b")), not(not(atomic("c")))).double_negation_elimination();
assert_eq!(expression, and(or(atomic("a"), atomic("b")), atomic("c")));
}
#[test]
fn test_de_morgans_laws_and() {
let expression = not!(eval!("a" && "b")).de_morgans_laws();
assert_eq!(expression, or!(not!(atomic!("a")), not!(atomic!("b"))));
let expression = not(and(atomic("a"), atomic("b"))).de_morgans_laws();
assert_eq!(expression, or(not(atomic("a")), not(atomic("b"))));
}
#[test]
fn test_de_morgans_laws_or() {
let expression = not!(eval!("a" || "b")).de_morgans_laws();
assert_eq!(expression, and!(not!(atomic!("a")), not!(atomic!("b"))));
let expression = not(or(atomic("a"), atomic("b"))).de_morgans_laws();
assert_eq!(expression, and(not(atomic("a")), not(atomic("b"))));
}
#[test]
fn test_de_morgans_laws_nested_or() {
let expression = not!(or!(eval!("a" && "b"), atomic!("c"))).de_morgans_laws(); // ¬(a ⋀ b c)
assert_eq!(expression, and!(or!(eval!(!"a"), eval!(!"b")), eval!(!"c"))); // ¬(a ⋀ b) ⋀ ¬c == (¬a ¬b) ⋀ ¬c
let expression = not(or(and(atomic("a"), atomic("b")), atomic("c"))).de_morgans_laws(); // ¬(a ⋀ b c)
assert_eq!(expression, and(or(not(atomic("a")), not(atomic("b"))), not(atomic("c")))); // ¬(a ⋀ b) ⋀ ¬c == (¬a ¬b) ⋀ ¬c
}
#[test]
fn test_de_morgans_laws_nested_and() {
let expression = not!(and!(eval!("a" || "b"), atomic!("c"))).de_morgans_laws(); // ¬(a b ⋀ c)
assert_eq!(expression, or!(and!(eval!(!"a"), eval!(!"b")), eval!(!"c"))); // ¬(a b) ⋀ ¬c == (¬a ⋀ ¬b) ¬c
let expression = not(and(or(atomic("a"), atomic("b")), atomic("c"))).de_morgans_laws(); // ¬(a b ⋀ c)
assert_eq!(expression, or(and(not(atomic("a")), not(atomic("b"))), not(atomic("c")))); // ¬(a b) ⋀ ¬c == (¬a ⋀ ¬b) ¬c
}
#[test]
fn test_de_morgans_laws_nested_and_or() {
let expression = not!(and!(eval!("a" || "b"), or!(atomic!("c"), atomic!("d")))).de_morgans_laws(); // ¬(a b ⋀ c d)
assert_eq!(expression, or!(and!(eval!(!"a"), eval!(!"b")), and!(eval!(!"c"), eval!(!"d")))); // ¬(a b) ⋀ ¬(c d) == (¬a ⋀ ¬b) (¬c ⋀ ¬d)
let expression = not(and(or(atomic("a"), atomic("b")), or(atomic("c"), atomic("d")))).de_morgans_laws(); // ¬(a b ⋀ c d)
assert_eq!(expression, or(and(not(atomic("a")), not(atomic("b"))), and(not(atomic("c")), not(atomic("d"))))); // ¬(a b) ⋀ ¬(c d) == (¬a ⋀ ¬b) (¬c ⋀ ¬d)
}
#[test]
fn test_absorption_law_and() {
let expression = and!(atomic!("a"), eval!("a" || "b")).absorption_law();
assert_eq!(expression, atomic!("a"));
let expression = and(atomic("a"), or(atomic("a"), atomic("b"))).absorption_law();
assert_eq!(expression, atomic("a"));
}
#[test]
fn test_absorption_law_or() {
let expression = or!(atomic!("a"), eval!("a" && "b")).absorption_law();
assert_eq!(expression, atomic!("a"));
let expression = or(atomic("a"), and(atomic("a"), atomic("b"))).absorption_law();
assert_eq!(expression, atomic("a"));
}
#[test]
fn test_absorption_law_nested_and() {
let expression = and!(atomic!("a"), or!(atomic!("a"), atomic!("b"))).absorption_law();
assert_eq!(expression, atomic!("a"));
let expression = and(atomic("a"), or(atomic("a"), atomic("b"))).absorption_law();
assert_eq!(expression, atomic("a"));
}
// !A & B | A <=> B | A
#[test]
fn test_absorption_law_not() {
let expression = or!(and!(not!(atomic!("a")), atomic!("b")), atomic!("a")).absorption_law();
assert_eq!(expression, or!(atomic!("b"), atomic!("a")));
let expression = or(and(not(atomic("a")), atomic("b")), atomic("a")).absorption_law();
assert_eq!(expression, or(atomic("b"), atomic("a")));
}
// A & B | !A <=> B | !A
#[test]
fn test_absorption_law_not_reversed() {
let expression = or!(and!(atomic!("a"), atomic!("b")), not!(atomic!("a"))).absorption_law();
assert_eq!(expression, or!(atomic!("b"), not!(atomic!("a"))));
let expression = or(and(atomic("a"), atomic("b")), not(atomic("a"))).absorption_law();
assert_eq!(expression, or(atomic("b"), not(atomic("a"))));
}
// !A & B | !A <=> !A
#[test]
fn test_absorption_law_double_not() {
let expression = or!(and!(not!(atomic!("a")), atomic!("b")), not!(atomic!("a"))).absorption_law();
assert_eq!(expression, not!(atomic!("a")));
let expression = or(and(not(atomic("a")), atomic("b")), not(atomic("a"))).absorption_law();
assert_eq!(expression, not(atomic("a")));
}
// (A | B) & !A <=> B & !A
#[test]
fn test_in_parenthesis() {
let expression = and!(or!(atomic!("a"), atomic!("b")), not!(atomic!("a"))).absorption_law();
assert_eq!(expression, and!(atomic!("b"), not!(atomic!("a"))));
let expression = and(or(atomic("a"), atomic("b")), not(atomic("a"))).absorption_law();
assert_eq!(expression, and(atomic("b"), not(atomic("a"))));
}
#[test]
fn test_distributive_law_and() {
let expression = and!(atomic!("a"), or!(atomic!("b"), atomic!("c"))).distribution_law();
assert_eq!(expression, or!(and!(atomic!("a"), atomic!("b")), and!(atomic!("a"), atomic!("c"))));
let expression = and(atomic("a"), or(atomic("b"), atomic("c"))).distribution_law();
assert_eq!(expression, or(and(atomic("a"), atomic("b")), and(atomic("a"), atomic("c"))));
}
#[test]
fn test_distributive_law_or() {
let expression = or!(atomic!("a"), and!(atomic!("b"), atomic!("c"))).distribution_law();
assert_eq!(expression, and!(or!(atomic!("a"), atomic!("b")), or!(atomic!("a"), atomic!("c"))));
let expression = or(atomic("a"), and(atomic("b"), atomic("c"))).distribution_law();
assert_eq!(expression, and(or(atomic("a"), atomic("b")), or(atomic("a"), atomic("c"))));
}
#[test]
fn test_distributive_law_nested_not() {
let expression = and!(atomic!("a"), not!(or!(atomic!("b"), atomic!("c")))).distribution_law();
assert_eq!(expression, and!(atomic!("a"), not!(or!(atomic!("b"), atomic!("c")))))
let expression = and(atomic("a"), not(or(atomic("b"), atomic("c")))).distribution_law();
assert_eq!(expression, and(atomic("a"), not(or(atomic("b"), atomic("c")))))
}
}

View File

@ -55,8 +55,8 @@ impl TruthTable {
/// - A vector of strings representing the header
/// # Example
/// ```
/// let expression = TruthTable::extract_header(&atomic!("A"));
/// let complex_expression = TruthTable::extract_header(&implies!(and!(atomic!("A"), atomic!("B")), or!(atomic!("C"), atomic!("D"))));
/// let expression = TruthTable::extract_header(&atomic("A"));
/// let complex_expression = TruthTable::extract_header(&implies(and(atomic("A"), atomic("B")), or(atomic("C"), atomic("D"))));
/// assert_eq!(expression, vec!["A"]);
/// assert_eq!(complex_expression, vec!["A", "B", "A ⋀ B", "C", "D", "(C D)", "A ⋀ B ➔ (C D)"]);
/// ```
@ -146,14 +146,14 @@ impl TruthTable {
#[cfg(test)]
mod tests {
use crate::expressions::helpers::{and, atomic, implies, not, or};
use crate::matrix;
use super::*;
// TODO fails sometimes...
#[test]
fn test_new_truth_table() {
let expression = and!(atomic!("A"), atomic!("B"));
let expression = and(atomic("A"), atomic("B"));
let truth_table = TruthTable::new(&expression, Default::default());
assert_eq!(truth_table.header, vec!["A", "B", "A ⋀ B"]);
assert_ne!(truth_table.truth_matrix, matrix![
@ -172,7 +172,7 @@ mod tests {
#[test]
fn test_new_truth_table_a_and_b_or_c() {
let expression = and!(or!(atomic!("A"), atomic!("C")), or!(atomic!("B"), atomic!("C")));
let expression = and(or(atomic("A"), atomic("C")), or(atomic("B"), atomic("C")));
let truth_table = TruthTable::new(&expression, Default::default());
let atomics = 3;
@ -217,7 +217,7 @@ mod tests {
#[test]
fn test_resolve_expression_and_all_true() {
let expression = and!(atomic!("A"), atomic!("B"));
let expression = and(atomic("A"), atomic("B"));
let booleans = map!["A".into() => true, "B".into() => true];
let header = vec!["A".into(), "B".into(), "A ⋀ B".into()];
let values = TruthTable::resolve_expression(&expression, &booleans, &header);
@ -226,7 +226,7 @@ mod tests {
#[test]
fn test_resolve_expression_and_1_true_1_false() {
let expression = and!(atomic!("A"), atomic!("B"));
let expression = and(atomic("A"), atomic("B"));
let booleans = map!["A".into() => true, "B".into() => false];
let header = vec!["A".into(), "B".into(), "A ⋀ B".into()];
let values = TruthTable::resolve_expression(&expression, &booleans, &header);
@ -235,7 +235,7 @@ mod tests {
#[test]
fn test_resolve_expression_or_1_true_1_false() {
let expression = or!(atomic!("A"), atomic!("B"));
let expression = or(atomic("A"), atomic("B"));
let booleans = map!["A".into() => true, "B".into() => false];
let header = vec!["A".into(), "B".into(), "(A B)".into()];
let values = TruthTable::resolve_expression(&expression, &booleans, &header);
@ -244,7 +244,7 @@ mod tests {
#[test]
fn test_resolve_expression_duplicate_atomic() {
let expression = and!(atomic!("A"), atomic!("A"));
let expression = and(atomic("A"), atomic("A"));
let booleans = map!["A".into() => true];
let header = vec!["A".into(), "A ⋀ A".into()];
let values = TruthTable::resolve_expression(&expression, &booleans, &header);
@ -253,7 +253,7 @@ mod tests {
#[test]
fn test_resolve_expression_even_more_duplicates() {
let expression = and!(atomic!("A"), and!(atomic!("A"), and!(atomic!("A"), atomic!("A"))));
let expression = and(atomic("A"), and(atomic("A"), and(atomic("A"), atomic("A"))));
let booleans = HashMap::from([("A".into(), true)]);
let header = vec!["A".into(), "A ⋀ A".into(), "A ⋀ A ⋀ A".into(), "A ⋀ A ⋀ A ⋀ A".into()];
let values = TruthTable::resolve_expression(&expression, &booleans, &header);
@ -262,70 +262,70 @@ mod tests {
#[test]
fn _test_resolve_expression_even_more_duplicates() {
let expression = and!(atomic!("A"), and!(atomic!("A"), and!(atomic!("A"), atomic!("A"))));
let expression = and(atomic("A"), and(atomic("A"), and(atomic("A"), atomic("A"))));
let booleans = HashMap::from([("A".into(), true)]);
let values = TruthTable::_resolve_expression(&expression, &booleans);
assert_eq!(values, HashMap::from([
(&atomic!("A"), true),
(&and!(atomic!("A"), atomic!("A")), true),
(&and!(atomic!("A"), and!(atomic!("A"), atomic!("A"))), true),
(&and!(atomic!("A"), and!(atomic!("A"), and!(atomic!("A"), atomic!("A")))), true),
(&atomic("A"), true),
(&and(atomic("A"), atomic("A")), true),
(&and(atomic("A"), and(atomic("A"), atomic("A"))), true),
(&and(atomic("A"), and(atomic("A"), and(atomic("A"), atomic("A")))), true),
]));
}
#[test]
fn test_atomic_expression() {
let expression = atomic!("A");
let expression = atomic("A");
let header = TruthTable::extract_header(&expression);
assert_eq!(header, vec!["A"]);
}
#[test]
fn test_not_expression() {
let expression = not!(atomic!("A"));
let expression = not(atomic("A"));
let header = TruthTable::extract_header(&expression);
assert_eq!(header, vec!["A", "¬A"]);
}
#[test]
fn test_binary_and_expression() {
let expression = and!(atomic!("A"), atomic!("B"));
let expression = and(atomic("A"), atomic("B"));
let header = TruthTable::extract_header(&expression);
assert_eq!(header, vec!["A", "B", "A ⋀ B"]);
}
#[test]
fn test_binary_or_expression() {
let expression = or!(atomic!("A"), atomic!("B"));
let expression = or(atomic("A"), atomic("B"));
let header = TruthTable::extract_header(&expression);
assert_eq!(header, vec!["A", "B", "(A B)"]);
}
#[test]
fn test_binary_implies_expression() {
let expression = implies!(atomic!("A"), atomic!("B"));
let expression = implies(atomic("A"), atomic("B"));
let header = TruthTable::extract_header(&expression);
assert_eq!(header, vec!["A", "B", "A ➔ B"]);
}
#[test]
fn test_complex_expression() {
let expression = implies!(and!(atomic!("A"), atomic!("B")), or!(atomic!("C"), atomic!("D")));
let expression = implies(and(atomic("A"), atomic("B")), or(atomic("C"), atomic("D")));
let header = TruthTable::extract_header(&expression);
assert_eq!(header, vec!["A", "B", "A ⋀ B", "C", "D", "(C D)", "A ⋀ B ➔ (C D)"]);
}
#[test]
fn test_equal_expressions_should_not_duplicate() {
let expression = and!(atomic!("A"), and!(atomic!("A"), and!(atomic!("A"), atomic!("A"))));
let expression = and(atomic("A"), and(atomic("A"), and(atomic("A"), atomic("A"))));
let header = TruthTable::extract_header(&expression);
assert_eq!(header, vec!["A", "A ⋀ A", "A ⋀ A ⋀ A", "A ⋀ A ⋀ A ⋀ A"]);
}
#[test]
fn test_somewhat_equal() {
let expression = and!(atomic!("A"), and!(or!(not!(atomic!("A")), atomic!("B")), atomic!("A")));
let expression = and(atomic("A"), and(or(not(atomic("A")), atomic("B")), atomic("A")));
let header = TruthTable::extract_header(&expression);
assert_eq!(header, vec!["A", "¬A", "B", "(¬A B)", "(¬A B) ⋀ A", "A ⋀ (¬A B) ⋀ A"]);
}

View File

@ -6,8 +6,8 @@ use nom::error::Error;
use nom::IResult;
use nom::sequence::{pair, preceded};
use crate::{and, atomic, implies, not, or};
use crate::expressions::expression::Expression;
use crate::expressions::helpers::{and, atomic, implies, not, or};
use crate::parsing::utils::{exhausted, IntoResult, parenthesized, trim};
pub fn parse_expression(input: &str) -> Result<Expression, nom::Err<Error<&str>>> {
@ -26,7 +26,7 @@ fn _parse_expression(input: &str) -> IResult<&str, Expression> {
fn left_hand_side(input: &str) -> IResult<&str, Expression> {
alt((
value,
not,
not_expression,
parenthesized(complete_expression)
))(input)
}
@ -45,10 +45,10 @@ fn expression<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str,
fn operator_combinators(expression: Expression) -> impl Fn(&str) -> IResult<&str, Expression> {
move |input: &str| {
alt((
implies(expression.clone()),
or(expression.clone()),
and(expression.clone()),
not,
implication_expression(expression.clone()),
or_expression(expression.clone()),
and_expression(expression.clone()),
not_expression,
))(input)
}
}
@ -58,23 +58,23 @@ fn complete_expression(input: &str) -> IResult<&str, Expression> {
operator_combinators(atomic.clone())(remaining)
}
fn and<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Expression> {
fn and_expression<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Expression> {
move |input: &'a str| {
preceded(
trim(char('&')),
left_hand_side,
)(input).map(|(remaining, right)| {
(remaining, and!(previous.clone(), right))
(remaining, and(previous.clone(), right))
})
}
}
fn complete_and(input: &str) -> IResult<&str, Expression> {
let (remaining, atomic) = value(input)?;
and(atomic.clone())(remaining)
and_expression(atomic.clone())(remaining)
}
fn or<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Expression> {
fn or_expression<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Expression> {
move |input: &'a str| {
preceded(
trim(char('|')),
@ -83,17 +83,17 @@ fn or<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Expressi
left_hand_side,
)),
)(input).map(|(remaining, right)| {
(remaining, or!(previous.clone(), right))
(remaining, or(previous.clone(), right))
})
}
}
fn complete_or(input: &str) -> IResult<&str, Expression> {
let (remaining, atomic) = value(input)?;
or(atomic.clone())(remaining)
or_expression(atomic.clone())(remaining)
}
fn implies<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Expression> {
fn implication_expression<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Expression> {
move |input: &'a str| {
preceded(
trim(tag("=>")),
@ -103,17 +103,17 @@ fn implies<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Exp
left_hand_side,
)),
)(input).map(|(remaining, right)| {
(remaining, implies!(previous.clone(), right))
(remaining, implies(previous.clone(), right))
})
}
}
fn not(input: &str) -> IResult<&str, Expression> {
fn not_expression(input: &str) -> IResult<&str, Expression> {
preceded(
char('!'),
left_hand_side,
)(input).map(|(remaining, right)| {
(remaining, not!(right))
(remaining, not(right))
})
}
@ -124,39 +124,39 @@ fn value(input: &str) -> IResult<&str, Expression> {
)(input)
.map(|(remaining, (first, rest))| {
let value = format!("{first}{rest}");
(remaining, atomic!(value))
(remaining, atomic(value))
})
}
#[cfg(test)]
mod tests {
use crate::{and, atomic, implies, not, or};
use crate::expressions::helpers::*;
#[test]
fn test_parse() {
let input = "a & b => c";
let result = super::parse_expression(input);
assert_eq!(result, Ok(implies!(and!(atomic!("a"), atomic!("b")), atomic!("c"))));
assert_eq!(result, Ok(implies(and(atomic("a"), atomic("b")), atomic("c"))));
}
#[test]
fn test_parse_complex() {
let input = "a => b | !(!c | d & e) => b";
let result = super::parse_expression(input);
assert_eq!(result, Ok(implies!(
implies!(
atomic!("a"),
or!(
atomic!("b"),
not!(
or!(
not!(atomic!("c")),
and!(atomic!("d"), atomic!("e"))
assert_eq!(result, Ok(implies(
implies(
atomic("a"),
or(
atomic("b"),
not(
or(
not(atomic("c")),
and(atomic("d"), atomic("e")),
)
)
)
),
),
),
atomic!("b")
atomic("b"),
)));
}
@ -164,98 +164,98 @@ mod tests {
fn test_operator_weight() {
let input = "A & B | C => D | E & F";
let result = super::parse_expression(input);
assert_eq!(result, Ok(implies!(or!(and!(atomic!("A"), atomic!("B")), atomic!("C")), or!(atomic!("D"), and!(atomic!("E"), atomic!("F"))))));
assert_eq!(result, Ok(implies(or(and(atomic("A"), atomic("B")), atomic("C")), or(atomic("D"), and(atomic("E"), atomic("F"))))));
}
#[test]
fn test_implies_chain() {
let input = "a => b => c";
let result = super::parse_expression(input);
assert_eq!(result, Ok(implies!(implies!(atomic!("a"), atomic!("b")), atomic!("c"))));
assert_eq!(result, Ok(implies(implies(atomic("a"), atomic("b")), atomic("c"))));
}
#[test]
fn test_parse_parentheses() {
let input = "a & (b => c)";
let result = super::parse_expression(input);
assert_eq!(result, Ok(and!(atomic!("a"), implies!(atomic!("b"), atomic!("c")))));
assert_eq!(result, Ok(and(atomic("a"), implies(atomic("b"), atomic("c")))));
}
#[test]
fn test_parse_not() {
let input = "!a";
let result = super::parse_expression(input);
assert_eq!(result, Ok(not!(atomic!("a"))));
assert_eq!(result, Ok(not(atomic("a"))));
}
#[test]
fn test_parse_not_parentheses() {
let input = "!(a & b)";
let result = super::parse_expression(input);
assert_eq!(result, Ok(not!(and!(atomic!("a"), atomic!("b")))));
assert_eq!(result, Ok(not(and(atomic("a"), atomic("b")))));
}
#[test]
fn test_expression_with_not_inside_and() {
let input = "a & !b";
let result = super::parse_expression(input);
assert_eq!(result, Ok(and!(atomic!("a"), not!(atomic!("b")))));
assert_eq!(result, Ok(and(atomic("a"), not(atomic("b")))));
}
#[test]
fn test_expression_with_not_inside_or() {
let input = "a | !b";
let result = super::parse_expression(input);
assert_eq!(result, Ok(or!(atomic!("a"), not!(atomic!("b")))));
assert_eq!(result, Ok(or(atomic("a"), not(atomic("b")))));
}
#[test]
fn test_expression_with_not_inside_implies() {
let input = "a => !b";
let result = super::parse_expression(input);
assert_eq!(result, Ok(implies!(atomic!("a"), not!(atomic!("b")))));
assert_eq!(result, Ok(implies(atomic("a"), not(atomic("b")))));
}
#[test]
fn test_expression_with_not_inside_parentheses() {
let input = "a & !(b | c)";
let result = super::parse_expression(input);
assert_eq!(result, Ok(and!(atomic!("a"), not!(or!(atomic!("b"), atomic!("c"))))));
assert_eq!(result, Ok(and(atomic("a"), not(or(atomic("b"), atomic("c"))))));
}
#[test]
fn test_even_not() {
let input = "!!!!a";
let result = super::parse_expression(input);
assert_eq!(result, Ok(not!(not!(not!(not!(atomic!("a")))))));
assert_eq!(result, Ok(not(not(not(not(atomic("a")))))));
}
#[test]
fn test_odd_not() {
let input = "!!!!!a";
let result = super::parse_expression(input);
assert_eq!(result, Ok(not!(not!(not!(not!(not!(atomic!("a"))))))));
assert_eq!(result, Ok(not(not(not(not(not(atomic("a"))))))));
}
#[test]
fn test_atomic() {
let input = "a";
let result = super::parse_expression(input);
assert_eq!(result, Ok(atomic!("a")));
assert_eq!(result, Ok(atomic("a")));
}
#[test]
fn test_atomic_with_underscore() {
let input = "a_b";
let result = super::parse_expression(input);
assert_eq!(result, Ok(atomic!("a_b")));
assert_eq!(result, Ok(atomic("a_b")));
}
#[test]
fn test_atomic_with_digits() {
let input = "a1";
let result = super::parse_expression(input);
assert_eq!(result, Ok(atomic!("a1")));
assert_eq!(result, Ok(atomic("a1")));
}
#[test]
@ -269,83 +269,83 @@ mod tests {
fn test_or_chain() {
let input = "a | b | c | d | e | f | g";
let result = super::parse_expression(input);
assert_eq!(result, Ok(or!(or!(or!(or!(or!(or!(atomic!("a"), atomic!("b")), atomic!("c")), atomic!("d")), atomic!("e")), atomic!("f")), atomic!("g"))));
assert_eq!(result, Ok(or(or(or(or(or(or(atomic("a"), atomic("b")), atomic("c")), atomic("d")), atomic("e")), atomic("f")), atomic("g"))));
}
#[test]
fn test_expression() {
let input = "a";
let result = super::_parse_expression(input);
assert_eq!(result, Ok(("", atomic!("a"))));
assert_eq!(result, Ok(("", atomic("a"))));
}
#[test]
fn test_expression_and() {
let expression = atomic!("a");
let expression = atomic("a");
let input = " & b";
let result = super::expression(expression)(input);
assert_eq!(result, Ok(("", and!(atomic!("a"), atomic!("b")))));
assert_eq!(result, Ok(("", and(atomic("a"), atomic("b")))));
}
#[test]
fn test_expression_and_or() {
let expression = atomic!("a");
let expression = atomic("a");
let input = " & b | c";
let result = super::expression(expression)(input);
assert_eq!(result, Ok(("", or!(and!(atomic!("a"), atomic!("b")), atomic!("c")))));
assert_eq!(result, Ok(("", or(and(atomic("a"), atomic("b")), atomic("c")))));
}
#[test]
fn test_expression_and_or_implies() {
let expression = atomic!("a");
let expression = atomic("a");
let input = " & b | c => d";
let result = super::expression(expression)(input);
assert_eq!(result, Ok(("", implies!(or!(and!(atomic!("a"), atomic!("b")), atomic!("c")), atomic!("d")))));
assert_eq!(result, Ok(("", implies(or(and(atomic("a"), atomic("b")), atomic("c")), atomic("d")))));
}
#[test]
fn test_expression_parentheses_or() {
let expression = atomic!("a");
let expression = atomic("a");
let input = " & (b | c) => d";
let result = super::expression(expression)(input);
assert_eq!(result, Ok(("", implies!(and!(atomic!("a"), or!(atomic!("b"), atomic!("c"))), atomic!("d")))));
assert_eq!(result, Ok(("", implies(and(atomic("a"), or(atomic("b"), atomic("c"))), atomic("d")))));
}
#[test]
fn test_expression_parentheses_and() {
let input = "(a & b) | (c & d)";
let result = super::_parse_expression(input);
assert_eq!(result, Ok(("", or!(and!(atomic!("a"), atomic!("b")), and!(atomic!("c"), atomic!("d"))))));
assert_eq!(result, Ok(("", or(and(atomic("a"), atomic("b")), and(atomic("c"), atomic("d"))))));
}
#[test]
fn test_expression_parentheses_implies() {
let expression = atomic!("a");
let expression = atomic("a");
let input = " & b | (c => d)";
let result = super::expression(expression)(input);
assert_eq!(result, Ok(("", or!(and!(atomic!("a"), atomic!("b")), implies!(atomic!("c"), atomic!("d"))))));
assert_eq!(result, Ok(("", or(and(atomic("a"), atomic("b")), implies(atomic("c"), atomic("d"))))));
}
#[test]
fn test_expression_nested_parentheses() {
let expression = atomic!("a");
let expression = atomic("a");
let input = " & (b | (c => d))";
let result = super::expression(expression)(input);
assert_eq!(result, Ok(("", and!(atomic!("a"), or!(atomic!("b"), implies!(atomic!("c"), atomic!("d")))))));
assert_eq!(result, Ok(("", and(atomic("a"), or(atomic("b"), implies(atomic("c"), atomic("d")))))));
}
#[test]
fn test_parse_or() {
let expression = atomic!("a");
let expression = atomic("a");
let input = " | b";
let result = super::or(expression)(input);
assert_eq!(result, Ok(("", or!(atomic!("a"), atomic!("b")))));
let result = super::or_expression(expression)(input);
assert_eq!(result, Ok(("", or(atomic("a"), atomic("b")))));
}
#[test]
fn test_parse_or_parentheses() {
let input = "(a | b)";
let result = super::_parse_expression(input);
assert_eq!(result, Ok(("", or!(atomic!("a"), atomic!("b")))));
assert_eq!(result, Ok(("", or(atomic("a"), atomic("b")))));
}
}