RosettaCodeData/Task/Truth-table/Smalltalk/truth-table.st

56 lines
1.7 KiB
Smalltalk

[:repeat |
expr := Stdin
request:'Enter boolean expression (name variables a,b,c...):'
defaultAnswer:'a|b'.
ast := Parser parseExpression:expr inNameSpace:nil onError:repeat.
"
ensure that only boolean logic operations are inside (sandbox)
"
(ast messageSelectors asSet
conform:[:each | #( '|' '&' 'not' 'xor:' '==>' ) includes:each])
ifFalse:repeat.
] valueWithRestart.
"
extract variables from the AST as a collection
(i.e. if user entered 'a & (b | x)', we get #('a' 'b' 'x')
"
varNames := StringCollection streamContents:[:s | ast variableNodesDo:[:each | s nextPut:each name]].
"
generate code for a block (aka lambda) to evaluate it; this makes a string like:
[:a :b :x | a & (b | x) ]
"
code := '[' , ((varNames collect:[:nm | ':',nm]) asString), ' | ' , expr , ']'.
"
eval the code, to get the block
"
func := Parser evaluate:code.
'Truth table for %s:\n' printf:{expr} on:Stdout.
'===================\n' printf:{} on:Stdout.
(varNames,{' result'}) do:[:each | '|%6s' printf:{each} on:Stdout].
Stdout cr.
Stdout next:(varNames size + 1)*7 put:$-.
Stdout cr.
"
now print with all combinations
"
allCombinationsDo :=
[:remainingVars :valuesIn :func |
remainingVars isEmpty ifTrue:[
valuesIn do:[:each | '|%6s' printf:{each}on:Stdout].
'|%6s\n' printf:{ func valueWithArguments:valuesIn} on:Stdout.
] ifFalse:[
#(false true) do:[:each |
allCombinationsDo value:(remainingVars from:2)
value:(valuesIn copyWith:each)
value:func.
].
].
].
allCombinationsDo value:varNames value:#() value:func