435 lines
12 KiB
Text
435 lines
12 KiB
Text
|
|
use std::{
|
|||
|
|
collections::HashMap,
|
|||
|
|
fmt::{Display, Formatter},
|
|||
|
|
iter::FromIterator,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Generic expression evaluation automaton and expression formatting support
|
|||
|
|
|
|||
|
|
#[derive(Clone, Debug)]
|
|||
|
|
pub enum EvaluationError<T> {
|
|||
|
|
NoResults,
|
|||
|
|
TooManyResults,
|
|||
|
|
OperatorFailed(T),
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
pub trait Operator<T> {
|
|||
|
|
type Err;
|
|||
|
|
|
|||
|
|
fn execute(&self, stack: &mut Vec<T>) -> Result<(), Self::Err>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[derive(Clone, Copy, Debug)]
|
|||
|
|
enum Element<O> {
|
|||
|
|
Operator(O),
|
|||
|
|
Variable(usize),
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[derive(Clone, Debug)]
|
|||
|
|
pub struct Expression<O> {
|
|||
|
|
elements: Vec<Element<O>>,
|
|||
|
|
symbols: Vec<String>,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
impl<O> Expression<O> {
|
|||
|
|
pub fn evaluate<T>(
|
|||
|
|
&self,
|
|||
|
|
mut bindings: impl FnMut(usize) -> T,
|
|||
|
|
) -> Result<T, EvaluationError<O::Err>>
|
|||
|
|
where
|
|||
|
|
O: Operator<T>,
|
|||
|
|
{
|
|||
|
|
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<String, EvaluationError<O::Err>>
|
|||
|
|
where
|
|||
|
|
O: Operator<Formatted>,
|
|||
|
|
{
|
|||
|
|
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<O> Display for Expression<O>
|
|||
|
|
where
|
|||
|
|
O: Operator<Formatted>,
|
|||
|
|
{
|
|||
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|||
|
|
match self.formatted() {
|
|||
|
|
Ok(result) => write!(f, "{}", result),
|
|||
|
|
Err(_) => write!(f, "<malformed expression>"),
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 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::Item> {
|
|||
|
|
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<Token<'a, O>> for Result<Expression<O>, Token<'a, O>>
|
|||
|
|
where
|
|||
|
|
O: WithPriority,
|
|||
|
|
O::Priority: Ord,
|
|||
|
|
{
|
|||
|
|
fn from_iter<T: IntoIterator<Item = Token<'a, O>>>(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<bool> for Boolean {
|
|||
|
|
type Err = BooleanError;
|
|||
|
|
|
|||
|
|
fn execute(&self, stack: &mut Vec<bool>) -> 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<Formatted> for Boolean {
|
|||
|
|
type Err = BooleanError;
|
|||
|
|
|
|||
|
|
fn execute(&self, stack: &mut Vec<Formatted>) -> 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<Expression<Boolean>, 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::<usize>() + 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!("{:<width$}", value, width = width);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
match expression.evaluate(|var_index| var_value(var_values, var_index) == 1) {
|
|||
|
|
Ok(result) => 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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|