Last active
November 4, 2016 13:12
-
-
Save patriziotufarolo/9fc96d34ac899513690849c57c82ae5a to your computer and use it in GitHub Desktop.
Simple evaluator for boolean expressions, supporting not, and, or, implies and iff operators. It supports nested formulas with parentheses. Based on http://stackoverflow.com/a/2472414/3800901
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
TOKEN = { | |
"True": True, | |
"False": False, | |
"and": lambda a, b: a and b, | |
"or": lambda a, b: a or b, | |
"implies": lambda a, b: (not a) or b, | |
"iff": lambda a, b: ((not a) or b) and ((not b) or a), | |
"not": lambda a: not a, | |
"(": "(", | |
")": ")" | |
} | |
class UnknownToken(Exception): | |
pass | |
class MalformedExpression(Exception): | |
pass | |
def find(input_list, obj, start=0): | |
return [index | |
for index, item in enumerate(input_list) | |
if item == obj and index >= start | |
] | |
def create_token_list(s): | |
try: | |
s = s.replace('(', ' ( ') | |
s = s.replace(')', ' ) ') | |
return [TOKEN[i] for i in s.split()] | |
except KeyError as e: | |
raise UnknownToken(e.args[0]) | |
def find_inner_parentheses(input_list): | |
open_pars = find(input_list, '(') | |
if not open_pars: | |
return False, -1, -1 | |
most_inner_opar = open_pars[-1] | |
most_inner_cpar = find(input_list, ')', most_inner_opar)[0] | |
return True, most_inner_opar, most_inner_cpar | |
def evaluate_boolean_atom(input_list): | |
if len(input_list) == 0: | |
return False | |
elif len(input_list) == 1: | |
return input_list[0] | |
elif len(input_list) == 2: | |
if not callable(input_list[0]): | |
raise MalformedExpression() | |
return input_list[0](input_list[1]) | |
elif len(input_list) == 3: | |
if not callable(input_list[1]): | |
raise MalformedExpression() | |
return input_list[1](input_list[0], input_list[2]) | |
else: | |
indexes_not = find(input_list, TOKEN["not"]) | |
for index_not in indexes_not: | |
input_list[index_not:index_not+1] = [evaluate_boolean_atom(input_list[index_not:index_not+1])] | |
indexes_and = find(input_list, TOKEN["and"]) | |
for index_and in indexes_and: | |
input_list[index_and-1:index_and+2] = [evaluate_boolean_atom(input_list[index_and-1:index_and+2])] | |
indexes_or = find(input_list, TOKEN["or"]) | |
for index_or in indexes_or: | |
input_list[index_or-1:index_or+2] = [evaluate_boolean_atom(input_list[index_or-1:index_or+2])] | |
indexes_implies = find(input_list, TOKEN["implies"]) | |
for index_implies in indexes_implies: | |
input_list[index_implies-1:index_implies+2] = [evaluate_boolean_atom(input_list[index_implies-1:index_implies+2])] | |
indexes_iff = find(input_list, TOKEN["iff"]) | |
for index_iff in indexes_iff: | |
input_list[index_iff-1:index_iff+2] = [evaluate_boolean_atom(input_list[index_iff-1:index_iff+2])] | |
return evaluate_boolean_atom(input_list) | |
def evaluate_tokens(input_list): | |
if not input_list: | |
return False | |
if len(input_list) == 1: | |
return evaluate_boolean_atom(input_list) | |
has_parens, left_par, right_par = find_inner_parentheses(input_list) | |
if not has_parens: | |
return evaluate_boolean_atom(input_list) | |
input_list[left_par:right_par+1] = \ | |
[evaluate_boolean_atom( input_list[left_par+1:right_par])] | |
return evaluate_tokens(input_list) | |
def evaluate(s): | |
return evaluate_tokens(create_token_list(s)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment