I would like to make an expression evaluator with pyparsing as follows:
Strings to be parsed should be usual expressions using combinations of ~ & | symbols (for not and or with, say, this precedence order) with strings of certain format (assume just single letters for now, a to z) where each letter evaluates to a list of booleans by a custom function (assume f), and the ~ & | operations to each boolean list should be applied pointwise.
i.e
a=[True, False] -> a=[True, False]
a=[True, False] -> ~a=[False, True]
a=[True, False] -> ~~a=[True, False]
a=[True, False] -> (((a)))=[True, False]
a=[True, False], b=[False,False], c=[True, True] -> ((a|b)&(~c))=[False, False]
to any nested level.
Any suggestions are appreciated.
EDIT: After reading the comment and also the comment's author's published e-book "Getting Started With Pyparsing" (O'Reilly), I have got to here:
class UnaryOperation(object):
def __init__(self, tokens):
self.operator, self.operand = tokens[0]
class BinaryOperation(object):
def __init__(self, tokens):
self.operator = tokens[0][1]
self.operands = tokens[0][0::2]
class SearchNot(UnaryOperation):
def generateExpression(self):
return u'~{}'.format(self.operand.generateExpression())
class SearchAnd(BinaryOperation):
def generateExpression(self):
return u'({})'.format('&'.join(op.generateExpression() for op in self.operands))
class SearchOr(BinaryOperation):
def generateExpression(self):
return u'({})'.format('|'.join(op.generateExpression() for op in self.operands))
class ConditionString(object):
def __init__(self, tokens):
self.term = tokens[0]
def generateExpression(self):
return str(mapper(self.term))
def mapper(input_string):
p = True
q = False
r = True
d = {u'b_1':[p,q,r],
u'a_1':[r,q,q],
u'c2':[p,p,r],
u'a1':[q,q,r],
u'a':[r,r,p],
u'd1':[q,p,p]}
return d[input_string] if input_string in d.keys() else 3*[True]
qname = Word(alphas+u'_', alphanums+u'_')
expression = operatorPrecedence(qname.setParseAction(ConditionString),
[(u'~', 1, opAssoc.RIGHT, SearchNot),
(u'&', 2, opAssoc.LEFT, SearchAnd),
(u'|', 2, opAssoc.LEFT, SearchOr)])
tests = [
u'b_1',
u'~a_1',
u'b_1&~c2',
u'~a1|(a&(((c2|d_1))))',
u'a&a1&b_1|c2'
]
for t in tests:
try:
evalStack = (expression + stringEnd).parseString(t)[0]
except ParseException, pe:
print "Invalid search string"
continue
evalExpr = evalStack.generateExpression()
print "Eval expr: ", evalExpr
which will print out
Eval expr: [True, False, True]
Eval expr: ~[True, False, False]
Eval expr: ([True, False, True]&~[True, True, True])
Eval expr: (~[False, False, True]|([True, True, True]&([True, True, True]|[True, True, True])))
Eval expr: (([True, True, True]&[False, False, True]&[True, False, True])|[True, True, True])
which is something like in the example presented in pages 59-60, but how could we proceed from here in a similar way with the eval() used there (but for sets)?
To add evaluation add eval()
methods to each of your classes. Each eval() method will perform the respective operation on your lists of bools:
list_wise_op(op, operands):
return [op(a_b) for a_b in zip(operands)]
# ConditionString
def eval(self):
return mapper(self.term)
#SearchNot
def eval(self):
return [not x for x in self.operand.eval()]
# SearchAnd
def eval(self):
return list_wise_op(all, [op.eval() for op in self.operands])
# SearchOr
def eval(self):
return list_wise_op(any, [op.eval() for op in self.operands])
Now you should be able to call evalstack.eval()
.