From a13dfeaeebba0a2681634038a75d42b5de465e2a Mon Sep 17 00:00:00 2001 From: gbrochar Date: Mon, 22 Jan 2024 18:34:40 +0100 Subject: [PATCH] refacto: use generic types --- src/ast.rs | 89 +++++++++++++++++++++++++------ src/ast/tests.rs | 29 +++++++--- src/boolean_evaluation.rs | 22 +------- src/main.rs | 11 +--- src/negation_normal_form.rs | 30 +++++++++++ src/negation_normal_form/tests.rs | 23 ++++++++ 6 files changed, 150 insertions(+), 54 deletions(-) create mode 100644 src/negation_normal_form.rs create mode 100644 src/negation_normal_form/tests.rs diff --git a/src/ast.rs b/src/ast.rs index 92a5863..3dd53a6 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -25,6 +25,79 @@ pub enum Node { }, } +impl Node { + pub fn parse_formula(formula: &str) -> Node { + let mut stack = vec![]; + for c in formula.chars() { + match c { + '0' => stack.push(Node::Leaf(false)), + '1' => stack.push(Node::Leaf(true)), + '!' => add_unary_node(&mut stack, Token::Negation), + '&' => add_binary_node(&mut stack, Token::Conjunction), + '|' => add_binary_node(&mut stack, Token::Disjunction), + '^' => add_binary_node(&mut stack, Token::ExclusiveDisjunction), + '>' => add_binary_node(&mut stack, Token::MaterialCondition), + '=' => add_binary_node(&mut stack, Token::LogicalEquivalence), + _ => panic!("Error: {} is not a valid character", c) + } + } + stack.pop().unwrap() + } + + pub fn ast_to_formula(ast: Node) -> String { + let mut str = String::from(""); + match ast { + Node::Unary { operator, operand } => { + str.push_str(Self::ast_to_formula(*operand).as_str()); + str.push(token_to_char(operator)); + }, + Node::Binary { operator, lhs, rhs } => { + str.push_str(Self::ast_to_formula(*lhs).as_str()); + str.push_str(Self::ast_to_formula(*rhs).as_str()); + str.push(token_to_char(operator)); + }, + Node::Leaf(b) => str.push(bool_to_char(b)), + }; + str + } +} + +impl Node { + pub fn parse_formula(formula: &str) -> Node { + let mut stack = vec![]; + for c in formula.chars() { + match c { + 'A'..='Z' => stack.push(Node::Leaf(c)), + '!' => add_unary_node(&mut stack, Token::Negation), + '&' => add_binary_node(&mut stack, Token::Conjunction), + '|' => add_binary_node(&mut stack, Token::Disjunction), + '^' => add_binary_node(&mut stack, Token::ExclusiveDisjunction), + '>' => add_binary_node(&mut stack, Token::MaterialCondition), + '=' => add_binary_node(&mut stack, Token::LogicalEquivalence), + _ => panic!("Error: {} is not a valid character", c) + } + } + stack.pop().unwrap() + } + + pub fn ast_to_formula(ast: Node) -> String { + let mut str = String::from(""); + match ast { + Node::Unary { operator, operand } => { + str.push_str(Self::ast_to_formula(*operand).as_str()); + str.push(token_to_char(operator)); + }, + Node::Binary { operator, lhs, rhs } => { + str.push_str(Self::ast_to_formula(*lhs).as_str()); + str.push_str(Self::ast_to_formula(*rhs).as_str()); + str.push(token_to_char(operator)); + }, + Node::Leaf(c) => str.push(c), + }; + str + } +} + pub fn add_unary_node(stack: &mut Vec>, token: Token) { let operand = Box::new(stack.pop().unwrap()); stack.push(Node::Unary { @@ -61,19 +134,3 @@ fn token_to_char(token: Token) -> char { } } -fn ast_to_formula(ast: Node) -> String { - let mut str = String::from(""); - match ast { - Node::Unary { operator, operand } => { - str.push_str(ast_to_formula(*operand).as_str()); - str.push(token_to_char(operator)); - }, - Node::Binary { operator, lhs, rhs } => { - str.push_str(ast_to_formula(*rhs).as_str()); - str.push_str(ast_to_formula(*lhs).as_str()); - str.push(token_to_char(operator)); - }, - Node::Leaf(b) => str.push(bool_to_char(b)), - }; - str -} \ No newline at end of file diff --git a/src/ast/tests.rs b/src/ast/tests.rs index 25c9509..d878aaf 100644 --- a/src/ast/tests.rs +++ b/src/ast/tests.rs @@ -1,7 +1,6 @@ #[cfg(test)] mod tests { - use crate::ast::{ add_binary_node, ast_to_formula, Node, Token }; - use crate::boolean_evaluation::parse_formula; + use crate::ast::{ add_binary_node, Node, Token }; #[test] fn add_nodes() { @@ -16,16 +15,30 @@ mod tests { } #[test] - fn reverse_ast() { + fn ast_to_formula_bool() { let formula = "01&"; - assert_eq!(ast_to_formula(parse_formula(formula)), formula); + assert_eq!(Node::::ast_to_formula(Node::::parse_formula(formula)), formula); let formula = "01&00|&"; - assert_eq!(ast_to_formula(parse_formula(formula)), formula); + assert_eq!(Node::::ast_to_formula(Node::::parse_formula(formula)), formula); let formula = "01&00|&11^&"; - assert_eq!(ast_to_formula(parse_formula(formula)), formula); + assert_eq!(Node::::ast_to_formula(Node::::parse_formula(formula)), formula); let formula = "01&00|&11=^"; - assert_eq!(ast_to_formula(parse_formula(formula)), formula); + assert_eq!(Node::::ast_to_formula(Node::::parse_formula(formula)), formula); let formula = "01&00|&0!="; - assert_eq!(ast_to_formula(parse_formula(formula)), formula); + assert_eq!(Node::::ast_to_formula(Node::::parse_formula(formula)), formula); + } + + #[test] + fn ast_to_formula_char() { + let formula = "AB&"; + assert_eq!(Node::::ast_to_formula(Node::::parse_formula(formula)), formula); + let formula = "AB&CD|&"; + assert_eq!(Node::::ast_to_formula(Node::::parse_formula(formula)), formula); + let formula = "AB&AC|&DE^&"; + assert_eq!(Node::::ast_to_formula(Node::::parse_formula(formula)), formula); + let formula = "AB&CD|&EF=^"; + assert_eq!(Node::::ast_to_formula(Node::::parse_formula(formula)), formula); + let formula = "AB&CD|&E!="; + assert_eq!(Node::::ast_to_formula(Node::::parse_formula(formula)), formula); } } \ No newline at end of file diff --git a/src/boolean_evaluation.rs b/src/boolean_evaluation.rs index 4a34b80..b51b3f5 100644 --- a/src/boolean_evaluation.rs +++ b/src/boolean_evaluation.rs @@ -1,24 +1,6 @@ mod tests; -use crate::ast::{ Token, Node, add_unary_node, add_binary_node }; - -pub fn parse_formula(formula: &str) -> Node { - let mut stack = vec![]; - for c in formula.chars() { - match c { - '0' => stack.push(Node::Leaf(false)), - '1' => stack.push(Node::Leaf(true)), - '!' => add_unary_node(&mut stack, Token::Negation), - '&' => add_binary_node(&mut stack, Token::Conjunction), - '|' => add_binary_node(&mut stack, Token::Disjunction), - '^' => add_binary_node(&mut stack, Token::ExclusiveDisjunction), - '>' => add_binary_node(&mut stack, Token::MaterialCondition), - '=' => add_binary_node(&mut stack, Token::LogicalEquivalence), - _ => panic!("Error: {} is not a valid character", c) - } - } - stack.pop().unwrap() -} +use crate::ast::{ Token, Node }; fn compute(operator: Token, lhs: bool, rhs: bool) -> bool { match operator { @@ -40,6 +22,6 @@ fn evaluate(tree: Node) -> bool { } pub fn eval_formula(formula: &str) -> bool { - let tree = parse_formula(formula); + let tree = Node::::parse_formula(formula); evaluate(tree) } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 5dc93d7..e793fc2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,10 +4,10 @@ mod gray_code; mod boolean_evaluation; mod truth_table; mod ast; +mod negation_normal_form; use gray_code::gray_code; use truth_table::print_truth_table; -use boolean_evaluation::eval_formula; fn main() { println!("Hello, world!"); @@ -21,13 +21,4 @@ fn main() { println!("{}", gray_code(7)); println!("{}", gray_code(3)); print_truth_table("ABC|&"); - //print_truth_table("ABCF&&&"); - // println!("{}", eval_formula("000|&")); - // println!("{}", eval_formula("001|&")); - // println!("{}", eval_formula("010|&")); - // println!("{}", eval_formula("011|&")); - // println!("{}", eval_formula("100|&")); - // println!("{}", eval_formula("101|&")); - // println!("{}", eval_formula("110|&")); - // println!("{}", eval_formula("111|&")); } diff --git a/src/negation_normal_form.rs b/src/negation_normal_form.rs new file mode 100644 index 0000000..88439d8 --- /dev/null +++ b/src/negation_normal_form.rs @@ -0,0 +1,30 @@ +mod tests; + +use crate::ast::Node; + +// fn cancel_double_negation(ast: Node) { +// let mut new_ast = ast.clone(); + +// fn recursion(ast: &mut Node, node_count: usize) { +// match ast { +// Node::Unary { operator: _, operand } => { +// recursion(&mut *operand, node_count + 1); +// }, +// Node::Binary { operator: _, lhs, rhs } => { +// recursion(&mut *lhs, node_count + 1); +// recursion(&mut *rhs, node_count + 1); +// }, +// Node::Leaf(c) => (), +// } +// } + +// recursion(&mut new_ast, 0); + +// new_ast +// } + +fn negation_normal_form(formula: &str) -> String { + let ast = Node::::parse_formula(formula); + + Node::::ast_to_formula(ast) +} \ No newline at end of file diff --git a/src/negation_normal_form/tests.rs b/src/negation_normal_form/tests.rs new file mode 100644 index 0000000..ff08058 --- /dev/null +++ b/src/negation_normal_form/tests.rs @@ -0,0 +1,23 @@ +#[cfg(test)] +mod tests { + use crate::negation_normal_form::negation_normal_form; + + #[test] + fn double_neg() { + assert_eq!(negation_normal_form("A!!"), "A"); + assert_eq!(negation_normal_form("A!!!!"), "A"); + assert_eq!(negation_normal_form("A!!!"), "A!"); + assert_eq!(negation_normal_form("A!!!!!!!!!!!!!!!!"), "A"); + assert_eq!(negation_normal_form("A!!!!!!!!!!!!!!!"), "A!"); + } + + #[test] + fn neg_and() { + assert_eq!(negation_normal_form("AB&!"), "A!B!|"); + } + + #[test] + fn neg_or() { + assert_eq!(negation_normal_form("AB|!"), "A!B!&"); + } +} \ No newline at end of file