use std::{ collections::HashMap, fmt::{Display, Formatter}, iter::FromIterator, }; // Generic expression evaluation automaton and expression formatting support #[derive(Clone, Debug)] pub enum EvaluationError { NoResults, TooManyResults, OperatorFailed(T), } pub trait Operator { type Err; fn execute(&self, stack: &mut Vec) -> Result<(), Self::Err>; } #[derive(Clone, Copy, Debug)] enum Element { Operator(O), Variable(usize), } #[derive(Clone, Debug)] pub struct Expression { elements: Vec>, symbols: Vec, } impl Expression { pub fn evaluate( &self, mut bindings: impl FnMut(usize) -> T, ) -> Result> where O: Operator, { let mut stack = Vec::new(); for element in self.elements.iter() { match element { Element::Variable(index) => stack.push(bindings(*index)), Element::Operator(op) => op .execute(&mut stack) .map_err(EvaluationError::OperatorFailed)?, } } match stack.pop() { Some(result) if stack.is_empty() => Ok(result), Some(_) => Err(EvaluationError::TooManyResults), None => Err(EvaluationError::NoResults), } } pub fn symbols(&self) -> &[String] { &self.symbols } pub fn formatted(&self) -> Result> where O: Operator, { self.evaluate(|index| Formatted(self.symbols[index].clone())) .map(|formatted| formatted.0) } } #[derive(Clone, Debug)] pub struct Formatted(pub String); impl Display for Formatted { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } impl Display for Expression where O: Operator, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self.formatted() { Ok(result) => write!(f, "{}", result), Err(_) => write!(f, ""), } } } // Generic parts of the parsing machinery #[derive(Clone, Copy, Debug)] pub enum Token<'a, O> { LBrace, RBrace, Operator(O), Variable(&'a str), Malformed(&'a str), } pub type Symbol<'a, O> = (&'a str, bool, Token<'a, O>); #[derive(Debug)] pub struct Tokens<'a, O> { source: &'a str, symbols: &'a [Symbol<'a, O>], } impl<'a, O> Tokens<'a, O> { pub fn new(source: &'a str, symbols: &'a [Symbol<'a, O>]) -> Self { Self { source, symbols } } } impl<'a, O: Clone> Iterator for Tokens<'a, O> { type Item = Token<'a, O>; fn next(&mut self) -> Option { self.source = self.source.trim_start(); let symbol = self.symbols.iter().find_map(|(symbol, word, token)| { if self.source.starts_with(symbol) { let end = symbol.len(); if *word { match &self.source[end..].chars().next() { Some(c) if !c.is_whitespace() => return None, _ => (), } } Some((token, end)) } else { None } }); if let Some((token, end)) = symbol { self.source = &self.source[end..]; Some(token.clone()) } else { match self.source.chars().next() { Some(c) if c.is_alphabetic() => { let end = self .source .char_indices() .find_map(|(i, c)| Some(i).filter(|_| !c.is_alphanumeric())) .unwrap_or_else(|| self.source.len()); let result = &self.source[0..end]; self.source = &self.source[end..]; Some(Token::Variable(result)) } Some(c) => { let end = c.len_utf8(); let result = &self.source[0..end]; self.source = &self.source[end..]; Some(Token::Malformed(result)) } None => None, } } } } pub trait WithPriority { type Priority; fn priority(&self) -> Self::Priority; } impl<'a, O> FromIterator> for Result, Token<'a, O>> where O: WithPriority, O::Priority: Ord, { fn from_iter>>(tokens: T) -> Self { let mut token_stack = Vec::new(); let mut indices = HashMap::new(); let mut symbols = Vec::new(); let mut elements = Vec::new(); 'outer: for token in tokens { match token { Token::Malformed(_) => return Err(token), Token::LBrace => token_stack.push(token), Token::RBrace => { // Flush all operators to the matching LBrace while let Some(token) = token_stack.pop() { match token { Token::LBrace => continue 'outer, Token::Operator(op) => elements.push(Element::Operator(op)), _ => return Err(token), } } } Token::Variable(name) => { let index = indices.len(); let symbol = name.to_string(); let index = *indices.entry(symbol.clone()).or_insert_with(|| { symbols.push(symbol); index }); elements.push(Element::Variable(index)); } Token::Operator(ref op) => { while let Some(token) = token_stack.pop() { match token { Token::Operator(pop) if op.priority() < pop.priority() => { elements.push(Element::Operator(pop)); } Token::Operator(pop) => { token_stack.push(Token::Operator(pop)); break; } _ => { token_stack.push(token); break; } } } token_stack.push(token); } } } // Handle leftovers while let Some(token) = token_stack.pop() { match token { Token::Operator(op) => elements.push(Element::Operator(op)), _ => return Err(token), } } Ok(Expression { elements, symbols }) } } // Definition of Boolean operators #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Boolean { Or, Xor, And, Not, } impl Display for Boolean { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let s = match self { Self::Or => "∨", Self::And => "∧", Self::Not => "¬", Self::Xor => "⩛", }; write!(f, "{}", s) } } impl WithPriority for Boolean { type Priority = u8; fn priority(&self) -> u8 { match self { Self::Or => 0, Self::Xor => 1, Self::And => 2, Self::Not => 3, } } } #[derive(Clone, Debug)] pub enum BooleanError { StackUnderflow, } impl Operator for Boolean { type Err = BooleanError; fn execute(&self, stack: &mut Vec) -> Result<(), Self::Err> { let mut pop = || stack.pop().ok_or(BooleanError::StackUnderflow); let result = match self { Boolean::Or => pop()? | pop()?, Boolean::And => pop()? & pop()?, Boolean::Xor => pop()? ^ pop()?, Boolean::Not => !pop()?, }; stack.push(result); Ok(()) } } impl Operator for Boolean { type Err = BooleanError; fn execute(&self, stack: &mut Vec) -> Result<(), Self::Err> { let mut pop = || stack.pop().ok_or(BooleanError::StackUnderflow); let result = match self { Boolean::Not => format!("{}{}", Boolean::Not, pop()?), binary_operator => { // The stack orders the operands backwards, so to format them // properly, we have to count with the reversed popping order let b = pop()?; let a = pop()?; format!("({} {} {})", a, binary_operator, b) } }; stack.push(Formatted(result)); Ok(()) } } impl Boolean { // It is important for the tokens to be ordered by their parsing priority (if // some operator was a prefix of another operator, the prefix must come later) const SYMBOLS: [Symbol<'static, Boolean>; 18] = [ ("(", false, Token::LBrace), (")", false, Token::RBrace), ("|", false, Token::Operator(Boolean::Or)), ("∨", false, Token::Operator(Boolean::Or)), ("or", true, Token::Operator(Boolean::Or)), ("OR", true, Token::Operator(Boolean::Or)), ("&", false, Token::Operator(Boolean::And)), ("∧", false, Token::Operator(Boolean::And)), ("and", true, Token::Operator(Boolean::And)), ("AND", true, Token::Operator(Boolean::And)), ("!", false, Token::Operator(Boolean::Not)), ("¬", false, Token::Operator(Boolean::Not)), ("not", true, Token::Operator(Boolean::Not)), ("NOT", true, Token::Operator(Boolean::Not)), ("^", false, Token::Operator(Boolean::Xor)), ("⩛", false, Token::Operator(Boolean::Xor)), ("xor", true, Token::Operator(Boolean::Xor)), ("XOR", true, Token::Operator(Boolean::Xor)), ]; pub fn tokenize(s: &str) -> Tokens<'_, Boolean> { Tokens::new(s, &Self::SYMBOLS) } pub fn parse<'a>(s: &'a str) -> Result, Token<'a, Boolean>> { Self::tokenize(s).collect() } } // Finally the table printing fn print_truth_table(s: &str) -> Result<(), std::borrow::Cow<'_, str>> { let expression = Boolean::parse(s).map_err(|e| format!("Parsing failed at token {:?}", e))?; let formatted = expression .formatted() .map_err(|_| "Malformed expression detected.")?; let var_count = expression.symbols().len(); if var_count > 64 { return Err("Too many variables to list.".into()); } let column_widths = { // Print header and compute the widths of columns let mut widths = Vec::with_capacity(var_count); for symbol in expression.symbols() { print!("{} ", symbol); widths.push(symbol.chars().count() + 2); // Include spacing } println!("{}", formatted); let width = widths.iter().sum::() + formatted.chars().count(); (0..width).for_each(|_| print!("-")); println!(); widths }; // Choose the bit extraction order for the more traditional table ordering let var_value = |input, index| (input >> (var_count - 1 - index)) & 1 ^ 1; // Use counter to enumerate all bit combinations for var_values in 0u64..(1 << var_count) { for (var_index, width) in column_widths.iter().enumerate() { let value = var_value(var_values, var_index); print!("{: println!("{}", if result { "1" } else { "0" }), Err(e) => println!("{:?}", e), } } println!(); Ok(()) } fn main() { loop { let input = { println!("Enter the expression to parse (or nothing to quit):"); let mut input = String::new(); std::io::stdin().read_line(&mut input).unwrap(); println!(); input }; if input.trim().is_empty() { break; } if let Err(e) = print_truth_table(&input) { eprintln!("{}\n", e); } } }