The Examples page on the pyparsing wiki includes a sample SimpleBool.py that will parse and evaluate expressions such as:
test = ["p and not q",
"not not p",
"not(p and q)",
"q or not p and r",
"q or not (p and r)",
"p or q or r",
"p or q or r and False",
]
(Hmmm, there aren't any examples with nested parens, but these are supported too.)
The actual parser is defined in its entirety using this code:
boolOperand = Word(alphas,max=1) | oneOf("True False")
boolExpr = operatorPrecedence( boolOperand,
[
("not", 1, opAssoc.RIGHT, BoolNot),
("and", 2, opAssoc.LEFT, BoolAnd),
("or", 2, opAssoc.LEFT, BoolOr),
])
The remainder of the example gives the implementations of BoolNot, BoolOr, and BoolAnd. The operatorPrecedence construct defines the sequence of operations, their arity and associativity, and optionally a class to be constructed with the parsed elements. operatorPrecedence then takes care of defining the grammar, including recursive definition of boolExpr's within nested parentheses. The resulting structure is similar to a nested AST using the given BoolXxx classes. These classes in turn define eval
methods so that the expressions can parsed and evaluated using this code:
p = True
q = False
r = True
for t in test:
res = boolExpr.parseString(t)[0]
print t,'\n', res, '=', bool(res),'\n'
pyparsing itself is a somewhat longish module, but it is a single source file so its installation footprint is pretty small. MIT license permits both noncommercial and commercial use.