Truth table: Difference between revisions

Content added Content deleted
(Add clojure solution to truth tables)
(Add source for Rust)
Line 941: Line 941:
{{incorrect|Déjà Vu|User input is not arbitrary but fixed to the three examples shown}}
{{incorrect|Déjà Vu|User input is not arbitrary but fixed to the three examples shown}}
<lang dejavu>print-line lst end:
<lang dejavu>print-line lst end:
for v in reversed copy lst:
for v in reversed copy lst:
print\( v chr 9 )
print\( v chr 9 )
print end
print end


(print-truth-table) t n func:
(print-truth-table) t n func:
if n:
if n:
(print-truth-table) push-through copy t 0 -- n @func
(print-truth-table) push-through copy t 0 -- n @func
(print-truth-table) push-through copy t 1 -- n @func
(print-truth-table) push-through copy t 1 -- n @func
else:
else:
print-line t func for in copy t
print-line t func for in copy t


print-truth-table vars name func:
print-truth-table vars name func:
print-line vars name
print-line vars name
(print-truth-table) [] len vars @func
(print-truth-table) [] len vars @func
print "" # extra new line
print "" # extra new line


stu s t u:
stu s t u:
or s /= t u
or s /= t u


abcd a b c d:
abcd a b c d:
/= a /= b /= c d
/= a /= b /= c d


print-truth-table [ "A" "B" ] "A ^ B" @/=
print-truth-table [ "A" "B" ] "A ^ B" @/=
Line 967: Line 967:
print-truth-table [ "A" "B" "C" "D" ] "A ^ (B ^ (C ^ D))" @abcd</lang>
print-truth-table [ "A" "B" "C" "D" ] "A ^ (B ^ (C ^ D))" @abcd</lang>
{{out}}
{{out}}
<pre>A B A ^ B
<pre>A B A ^ B
0 0 0
0 0 0
0 1 1
0 1 1
1 0 1
1 0 1
1 1 0
1 1 0


S T U S | (T ^ U)
S T U S | (T ^ U)
0 0 0 0
0 0 0 0
0 0 1 1
0 0 1 1
0 1 0 1
0 1 0 1
0 1 1 0
0 1 1 0
1 0 0 1
1 0 0 1
1 0 1 1
1 0 1 1
1 1 0 1
1 1 0 1
1 1 1 1
1 1 1 1


A B C D A ^ (B ^ (C ^ D))
A B C D A ^ (B ^ (C ^ D))
0 0 0 0 0
0 0 0 0 0
0 0 0 1 1
0 0 0 1 1
0 0 1 0 1
0 0 1 0 1
0 0 1 1 0
0 0 1 1 0
0 1 0 0 1
0 1 0 0 1
0 1 0 1 0
0 1 0 1 0
0 1 1 0 0
0 1 1 0 0
0 1 1 1 1
0 1 1 1 1
1 0 0 0 1
1 0 0 0 1
1 0 0 1 0
1 0 0 1 0
1 0 1 0 0
1 0 1 0 0
1 0 1 1 1
1 0 1 1 1
1 1 0 0 0
1 1 0 0 0
1 1 0 1 1
1 1 0 1 1
1 1 1 0 1
1 1 1 0 1
1 1 1 1 0
1 1 1 1 0
</pre>
</pre>


Line 1,582: Line 1,582:
function isboolop(chr){return "&|!^".indexOf(chr)!=-1;}
function isboolop(chr){return "&|!^".indexOf(chr)!=-1;}
function varsindexof(chr){
function varsindexof(chr){
var i;
var i;
for(i=0;i<vars.length;i++){if(vars[i][0]==chr)return i;}
for(i=0;i<vars.length;i++){if(vars[i][0]==chr)return i;}
return -1;
return -1;
}
}
function printtruthtable(){
function printtruthtable(){
var i,str;
var i,str;
elem=document.createElement("pre");
elem=document.createElement("pre");
expr=prompt("Boolean expression:\nAccepts single-character variables (except for \"T\" and \"F\", which specify explicit true or false values), postfix, with \"&|!^\" for and, or, not, xor, respectively; optionally seperated by whitespace.").replace(/\s/g,"");
expr=prompt("Boolean expression:\nAccepts single-character variables (except for \"T\" and \"F\", which specify explicit true or false values), postfix, with \"&|!^\" for and, or, not, xor, respectively; optionally seperated by whitespace.").replace(/\s/g,"");
vars=[];
vars=[];
for(i=0;i<expr.length;i++)if(!isboolop(expr[i])&&expr[i]!="T"&&expr[i]!="F"&&varsindexof(expr[i])==-1)vars.push([expr[i],-1]);
for(i=0;i<expr.length;i++)if(!isboolop(expr[i])&&expr[i]!="T"&&expr[i]!="F"&&varsindexof(expr[i])==-1)vars.push([expr[i],-1]);
if(vars.length==0)return;
if(vars.length==0)return;
str="";
str="";
for(i=0;i<vars.length;i++)str+=vars[i][0]+" ";
for(i=0;i<vars.length;i++)str+=vars[i][0]+" ";
elem.innerHTML="<b>"+str+expr+"</b>\n";
elem.innerHTML="<b>"+str+expr+"</b>\n";
vars[0][1]=false;
vars[0][1]=false;
truthpartfor(1);
truthpartfor(1);
vars[0][1]=true;
vars[0][1]=true;
truthpartfor(1);
truthpartfor(1);
vars[0][1]=-1;
vars[0][1]=-1;
document.body.appendChild(elem);
document.body.appendChild(elem);
}
}
function truthpartfor(index){
function truthpartfor(index){
if(index==vars.length){
if(index==vars.length){
var str,i;
var str,i;
str="";
str="";
for(i=0;i<index;i++)str+=(vars[i][1]?"<b>T</b>":"F")+" ";
for(i=0;i<index;i++)str+=(vars[i][1]?"<b>T</b>":"F")+" ";
elem.innerHTML+=str+(parsebool()?"<b>T</b>":"F")+"\n";
elem.innerHTML+=str+(parsebool()?"<b>T</b>":"F")+"\n";
return;
return;
}
}
vars[index][1]=false;
vars[index][1]=false;
truthpartfor(index+1);
truthpartfor(index+1);
vars[index][1]=true;
vars[index][1]=true;
truthpartfor(index+1);
truthpartfor(index+1);
vars[index][1]=-1;
vars[index][1]=-1;
}
}
function parsebool(){
function parsebool(){
var stack,i,idx;
var stack,i,idx;
console.log(vars);
console.log(vars);
stack=[];
stack=[];
for(i=0;i<expr.length;i++){
for(i=0;i<expr.length;i++){
if(expr[i]=="T")stack.push(true);
if(expr[i]=="T")stack.push(true);
else if(expr[i]=="F")stack.push(false);
else if(expr[i]=="F")stack.push(false);
else if((idx=varsindexof(expr[i]))!=-1)stack.push(vars[idx][1]);
else if((idx=varsindexof(expr[i]))!=-1)stack.push(vars[idx][1]);
else if(isboolop(expr[i])){
else if(isboolop(expr[i])){
switch(expr[i]){
switch(expr[i]){
case "&":stack.push(stack.pop()&stack.pop());break;
case "&":stack.push(stack.pop()&stack.pop());break;
case "|":stack.push(stack.pop()|stack.pop());break;
case "|":stack.push(stack.pop()|stack.pop());break;
case "!":stack.push(!stack.pop());break;
case "!":stack.push(!stack.pop());break;
case "^":stack.push(stack.pop()^stack.pop());break;
case "^":stack.push(stack.pop()^stack.pop());break;
}
}
} else alert("Non-conformant character "+expr[i]+" in expression. Should not be possible.");
} else alert("Non-conformant character "+expr[i]+" in expression. Should not be possible.");
console.log(stack);
console.log(stack);
}
}
return stack[0];
return stack[0];
}
}
</script></head><body onload="printtruthtable()"></body></html></lang>
</script></head><body onload="printtruthtable()"></body></html></lang>
Line 2,014: Line 2,014:
<pre>TruthTable["V ~Xor~ (B ~Xor~ (K ~Xor~ D ) )"]
<pre>TruthTable["V ~Xor~ (B ~Xor~ (K ~Xor~ D ) )"]


B D K V V ~Xor~ (B ~Xor~ (K ~Xor~ D ) )
B D K V V ~Xor~ (B ~Xor~ (K ~Xor~ D ) )
False False False False False
False False False False False
False False False True True
False False False True True
False False True False True
False False True False True
False False True True False
False False True True False
False True False False True
False True False False True
False True False True False
False True False True False
False True True False False
False True True False False
False True True True True
False True True True True
True False False False True
True False False False True
True False False True False
True False False True False
True False True False False
True False True False False
True False True True True
True False True True True
True True False False False
True True False False False
True True False True True
True True False True True
True True True False True
True True True False True
True True True True False</pre>
True True True True False</pre>


=={{header|Maxima}}==
=={{header|Maxima}}==
Line 2,125: Line 2,125:
It would be easy to modify the program to take <code>+</code> for XOR instead.
It would be easy to modify the program to take <code>+</code> for XOR instead.
<lang parigp>vars(P)={
<lang parigp>vars(P)={
my(v=List(),x);
my(v=List(),x);
while(type(P)=="t_POL",
while(type(P)=="t_POL",
x=variable(P);
x=variable(P);
listput(v,x);
listput(v,x);
P=subst(P,x,1)
P=subst(P,x,1)
);
);
Vec(v)
Vec(v)
};
};
truthTable(P)={
truthTable(P)={
my(var=vars(P),t,b);
my(var=vars(P),t,b);
for(i=0,2^#var-1,
for(i=0,2^#var-1,
t=eval(P);
t=eval(P);
for(j=1,#var,
for(j=1,#var,
b=bittest(i,j-1);
b=bittest(i,j-1);
t=subst(t,var[j],b);
t=subst(t,var[j],b);
print1(b)
print1(b)
);
);
print(!!t)
print(!!t)
);
);
};
};
truthTable("x+y") \\ OR
truthTable("x+y") \\ OR
Line 2,455: Line 2,455:


sub truth_table {
sub truth_table {
my $s = shift;
my $s = shift;
my (%seen, @vars);
my (%seen, @vars);
for ($s =~ /([a-zA-Z_]\w*)/g) {
for ($s =~ /([a-zA-Z_]\w*)/g) {
$seen{$_} //= do { push @vars, $_; 1 };
$seen{$_} //= do { push @vars, $_; 1 };
}
}


print "\n", join("\t", @vars, $s), "\n", '-' x 40, "\n";
print "\n", join("\t", @vars, $s), "\n", '-' x 40, "\n";
@vars = map("\$$_", @vars);
@vars = map("\$$_", @vars);


$s =~ s/([a-zA-Z_]\w*)/\$$1/g;
$s =~ s/([a-zA-Z_]\w*)/\$$1/g;
$s = "print(".join(',"\t", ', map("($_?'T':'F')", @vars, $s)).",\"\\n\")";
$s = "print(".join(',"\t", ', map("($_?'T':'F')", @vars, $s)).",\"\\n\")";
$s = "for my $_ (0, 1) { $s }" for (reverse @vars);
$s = "for my $_ (0, 1) { $s }" for (reverse @vars);
eval $s;
eval $s;
}
}


Line 2,771: Line 2,771:
{{works with|SWI-Prolog|Any - tested with release 7.6.4}}
{{works with|SWI-Prolog|Any - tested with release 7.6.4}}
<lang prolog>/*
<lang prolog>/*
To evaluate the truth table a line of text is inputted and then there are three steps
To evaluate the truth table a line of text is inputted and then there are three steps
Let's say the expression is:
Let's say the expression is:
'not a and (b or c)'
'not a and (b or c)'
Step 1: tokenize into atoms and brackets
Step 1: tokenize into atoms and brackets
eg: Tokenized = [ not, a, and, '(', b, or, c, ')' ].
eg: Tokenized = [ not, a, and, '(', b, or, c, ')' ].
Step 2: convert to a term that can be evaluated, and get out the variables
Step 2: convert to a term that can be evaluated, and get out the variables
eg: Expression = op(and, op(not, a), op(or, b, c)), Variables = [ a, b, c ]
eg: Expression = op(and, op(not, a), op(or, b, c)), Variables = [ a, b, c ]
Step 3: permeate over the variables, substituting the values for each var, and evaluate the expression for each permutation
Step 3: permeate over the variables, substituting the values for each var, and evaluate the expression for each permutation
eg: [ 0, 0, 0]
eg: [ 0, 0, 0]
op(and, op(not, 0), op(or, 0, 0))
op(and, op(not, 0), op(or, 0, 0))
op(and, 1, op(or, 0, 0))
op(and, 1, op(or, 0, 0))
op(and, 1, 0)
op(and, 1, 0)
0
0
[ 0, 0, 1]
[ 0, 0, 1]
op(and, op(not, 0), op(or, 0, 1))
op(and, op(not, 0), op(or, 0, 1))
op(and, 1, op(or, 0, 0))
op(and, 1, op(or, 0, 0))
op(and, 1, 1)
op(and, 1, 1)
1
1
*/
*/
truth_table :-
truth_table :-
current_input(In),
current_input(In),
read_line_to_codes(In, Line),
read_line_to_codes(In, Line),
atom_codes(A, Line),
atom_codes(A, Line),
atom_chars(A, Chars),
atom_chars(A, Chars),


% parse everything into the form we want
% parse everything into the form we want
phrase(tok(Tok), Chars, _),
phrase(tok(Tok), Chars, _),
phrase(expr(Expr,Vars), Tok, _),
phrase(expr(Expr,Vars), Tok, _),
list_to_set(Vars,VarSet),
list_to_set(Vars,VarSet),
% evaluate
% evaluate
print_expr(Expr, VarSet), !.
print_expr(Expr, VarSet), !.


print_expr(Expr, Vars) :-
print_expr(Expr, Vars) :-
% write the header (once)
% write the header (once)
maplist(format('~p '), Vars),
maplist(format('~p '), Vars),
format('~n'),
format('~n'),
% write the results for as many times as there are rows
% write the results for as many times as there are rows
eval_expr(Expr, Vars, Tvals, R),
eval_expr(Expr, Vars, Tvals, R),
maplist(format('~p '), Tvals),
maplist(format('~p '), Tvals),
format('~p~n', R),
format('~p~n', R),
fail.
fail.
print_expr(_, _).
print_expr(_, _).
% Step 1 - tokenize the input into spaces, brackets and atoms
% Step 1 - tokenize the input into spaces, brackets and atoms
tok([A|As]) --> spaces(_), chars([X|Xs]), {atom_codes(A, [X|Xs])}, spaces(_), tok(As).
tok([A|As]) --> spaces(_), chars([X|Xs]), {atom_codes(A, [X|Xs])}, spaces(_), tok(As).
Line 2,831: Line 2,831:
bracket('(') --> ['('].
bracket('(') --> ['('].
bracket(')') --> [')'].
bracket(')') --> [')'].
% Step 2 - Parse the expression into an evaluable term
% Step 2 - Parse the expression into an evaluable term
expr(op(I, E, E2), V) --> starter(E, V1), infix(I), expr(E2, V2), { append(V1, V2, V) }.
expr(op(I, E, E2), V) --> starter(E, V1), infix(I), expr(E2, V2), { append(V1, V2, V) }.
Line 2,848: Line 2,848:
variable(V) --> [V], \+ infix(V), \+ bracket(V).
variable(V) --> [V], \+ infix(V), \+ bracket(V).
space(' ') --> [' '].
space(' ') --> [' '].
char(X) --> [X], { dif(X, ' ') }.
char(X) --> [X], { dif(X, ' ') }.
% Step 3 - evaluate the parsed expression
% Step 3 - evaluate the parsed expression
eval_expr(Expr, Vars, Tvals, R) :-
eval_expr(Expr, Vars, Tvals, R) :-
length(Vars,Len),
length(Vars,Len),
length(Tvals, Len),
length(Tvals, Len),
maplist(truth_val, Tvals),
maplist(truth_val, Tvals),
eval(Expr, [Tvals,Vars],R).
eval(Expr, [Tvals,Vars],R).


eval(X, [Vals,Vars], R) :- nth1(N,Vars,X), nth1(N,Vals,R).
eval(X, [Vals,Vars], R) :- nth1(N,Vars,X), nth1(N,Vals,R).
Line 3,086: Line 3,086:
<pre>
<pre>
$ truthtable 'A ^ B'
$ truthtable 'A ^ B'
A B A ^ B
A B A ^ B
False False False
False False False
False True True
False True True
True False True
True False True
True True False
True True False


$ truthtable 'foo & bar | baz'
$ truthtable 'foo & bar | baz'
foo bar baz foo & bar | baz
foo bar baz foo & bar | baz
False False False False
False False False False
False False True True
False False True True
False True False False
False True False False
False True True True
False True True True
True False False False
True False False False
True False True True
True False True True
True True False True
True True False True
True True True True
True True True True


$ truthtable 'Jim & (Spock ^ Bones) | Scotty'
$ truthtable 'Jim & (Spock ^ Bones) | Scotty'
Jim Spock Bones Scotty Jim & (Spock ^ Bones) | Scotty
Jim Spock Bones Scotty Jim & (Spock ^ Bones) | Scotty
False False False False False
False False False False False
False False False True True
False False False True True
False False True False False
False False True False False
False False True True True
False False True True True
False True False False False
False True False False False
False True False True True
False True False True True
False True True False False
False True True False False
False True True True True
False True True True True
True False False False False
True False False False False
True False False True True
True False False True True
True False True False True
True False True False True
True False True True True
True False True True True
True True False False True
True True False False True
True True False True True
True True False True True
True True True False False
True True True False False
True True True True True</pre>
True True True True True</pre>


=={{header|REXX}}==
=={{header|REXX}}==
Line 3,531: Line 3,531:
true true true false | true
true true true false | true
true true true true | false</pre>
true true true true | false</pre>

=={{header|Rust}}==
The solution accepts Boolean expressions in infix notation with priorities and parentheses.
Operators NOT, AND, OR and XOR are supported and recognized in a few common notations (e.g., <code>OR</code>, <code>or</code> and <code>|</code> are equivalent).
Non-word symbols does not have to be separated with spaces, for instance <code>a|b&c</code> is parsed correctly.

The implementation is mostly generic (tokenizer, infix-to-postfix translation and evaluation automaton) and not limited to Boolean expressions.
There is no thorough verification that the tokens form an actual infix expression though.
Therefore an invalid expression may be accepted if its evaluation finishes without an error.
Extending the set of implemented operators should be almost trivial without any change of the logically more complex parts.

<lang Rust>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);
}
}
}</lang>

{{out}}
<pre>
Enter the expression to parse (or nothing to quit):
Jim & (Spock xor Bones) | Scotty

Jim Spock Bones Scotty ((Jim ∧ (Spock ⩛ Bones)) ∨ Scotty)
-------------------------------------------------------------
1 1 1 1 1
1 1 1 0 0
1 1 0 1 1
1 1 0 0 1
1 0 1 1 1
1 0 1 0 1
1 0 0 1 1
1 0 0 0 0
0 1 1 1 1
0 1 1 0 0
0 1 0 1 1
0 1 0 0 0
0 0 1 1 1
0 0 1 0 0
0 0 0 1 1
0 0 0 0 0

Enter the expression to parse (or nothing to quit):

</pre>


=={{header|Sidef}}==
=={{header|Sidef}}==
Line 3,561: Line 4,034:
<pre>
<pre>
Boolean expression (e.g. 'a & b'): (a & b) | c
Boolean expression (e.g. 'a & b'): (a & b) | c
a b c | (a & b) | c
a b c | (a & b) | c
false false false | false
false false false | false
false false true | true
false false true | true
false true false | false
false true false | false
false true true | true
false true true | true
true false false | false
true false false | false
true false true | true
true false true | true
true true false | true
true true false | true
true true true | true
true true true | true
</pre>
</pre>


Line 3,593: Line 4,066:
<pre>
<pre>
Enter a boolean expression: ($a&&$b)||$c
Enter a boolean expression: ($a&&$b)||$c
$a $b $c Result
$a $b $c Result
0 0 0 0
0 0 0 0
0 0 1 1
0 0 1 1
0 1 0 0
0 1 0 0
0 1 1 1
0 1 1 1
1 0 0 0
1 0 0 0
1 0 1 1
1 0 1 1
1 1 0 1
1 1 0 1
1 1 1 1
1 1 1 1
</pre>
</pre>