There is no need to write your own parser if you're willing to transform S in to a string suitable for use with eval(). Change S from '(A or B) and not(A and C)'
into an equivalent T that uses Python's in-operator '(x in A or x in B) and not(x in A and x in C)'
.
Compute the result by looping over the universe of elements and testing whether they match the above expression.
Here's a worked-out example at the interactive prompt:
>>> T = '(x in A or x in B) and not(x in A and x in C)'
>>> sets = {'A': {0, 1}, 'B': {0, 2}, 'C': {1, 3}}
>>> universe = {x for s in sets.values() for x in s}
>>> {x for x in universe if eval(T, sets, {'x': x})}
set([0, 2])
To make the transformation automatically, create a namespace for the set variables where variable lookups do a set membership test. Putting it all together gives you a simple and clean set-expression evaluator:
class SetVariables(dict):
'Transform a variable lookup into a membership test'
def __getitem__(self, var):
s = dict.__getitem__(self, var)
return self.x in s
def set_eval(expr, **sets):
'Evaluation a set expression for the given sets'
universe = {x for s in sets.values() for x in s}
expr = compile(expr, '', 'eval')
variables = SetVariables(sets)
results = set()
for x in universe:
variables.x = x
if eval(expr, {}, variables):
results.add(x)
return results
if __name__ == '__main__':
print set_eval(expr = '(A or B) and not(A and C)',
A = {0, 1},
B = {0, 2},
C = {1, 3}
)
Hope this solves your problem and saves you from having to write your own parser :-)