fef-questionnaire/questionnaire/parsers.py

143 lines
3.9 KiB
Python

#!/usr/bin/python
__all__ = ('parse_checks', 'BooleanParser')
try: from pyparsing import *
except ImportError: from utils.pyparsing import *
def __make_parser():
key = Word(alphas, alphanums+"_-")
value = Word(alphanums + "-.,_=<>!@$%^&*[]{}:;|/'") | QuotedString('"')
return Dict(ZeroOrMore(Group( key + Optional( Suppress("=") + value, default=True ) ) ))
__checkparser = __make_parser()
def parse_checks(string):
"""
from parsers import parse_checks
>>> parse_checks('dependent=5a,no dependent="5a && 4a" dog="Roaming Rover" name=Robert foo bar')
([(['dependent', '5a,no'], {}), (['dependent', '5a && 4a'], {}), (['dog', 'Roaming Rover'], {}), (['name', 'Robert'], {}), (['foo', True], {}), (['bar', True], {})], {'dependent': [('5a,no', 0), ('5a && 4a', 1)], 'foo': [(True, 4)], 'bar': [(True, 5)], 'dog': [('Roaming Rover', 2)], 'name': [('Robert', 3)]})
"""
return __checkparser.parseString(string, parseAll=True)
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# - Boolean Expression Parser -
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-
class BoolOperand(object):
def __init__(self, t):
self.args = t[0][0::2]
def __str__(self):
sep = " %s " % self.reprsymbol
return "(" + sep.join(map(str, self.args)) + ")"
class BoolAnd(BoolOperand):
reprsymbol = '&&'
def __nonzero__(self):
for a in self.args:
if not bool(a):
return False
return True
class BoolOr(BoolOperand):
reprsymbol = '||'
def __nonzero__(self):
for a in self.args:
if bool(a):
return True
return False
class BoolNot(BoolOperand):
def __init__(self,t):
self.arg = t[0][1]
def __str__(self):
return "!" + str(self.arg)
def __nonzero__(self):
return not bool(self.arg)
class Checker(object):
"Simple wrapper to call a specific function, passing in args and kwargs each time"
def __init__(self, func, expr, *args, **kwargs):
self.func = func
self.expr = expr
self.args = args
self.kwargs = kwargs
def __nonzero__(self):
return self.func(self.expr, *self.args, **self.kwargs)
def __hash__(self):
return hash(self.expr)
def __unicode__(self):
try: fname=self.func.func_name
except: fname="TestExpr"
return "%s('%s')" % (fname, self.expr)
__str__ = __unicode__
class BooleanParser(object):
"""Simple boolean parser
>>> def foo(x):
... if x == '1': return True
... return False
...
>>> foo('1')
True
>>> foo('0')
False
>>> p = BooleanParser(foo)
>>> p.parse('1 and 0')
False
>>> p.parse('1 and 1')
True
>>> p.parse('1 or 1')
True
>>> p.parse('0 or 1')
True
>>> p.parse('0 or 0')
False
>>> p.parse('(0 or 0) and 1')
False
>>> p.parse('(0 or 0) and (1)')
False
>>> p.parse('(0 or 1) and (1)')
True
>>> p.parse('(0 or 0) or (1)')
True
"""
def __init__(self, func, *args, **kwargs): # treats kwarg boolOperand specially!
self.args = args
self.kwargs = kwargs
self.func = func
if "boolOperand" in kwargs:
boolOperand = kwargs["boolOperand"]
del kwargs["boolOperand"]
else:
boolOperand = Word(alphanums + "-.,_=<>!@$%^&*[]{}:;|/\\")
boolOperand = boolOperand.setParseAction(self._check)
self.boolExpr = operatorPrecedence( boolOperand,
[
("not ", 1, opAssoc.RIGHT, BoolNot),
("or", 2, opAssoc.LEFT, BoolOr),
("and", 2, opAssoc.LEFT, BoolAnd),
])
def _check(self, string, location, tokens):
checker = Checker(self.func, tokens[0], *self.args, **self.kwargs)
tokens[0] = checker
def parse(self, code):
if not code or not code.strip():
return False
return bool(self.boolExpr.parseString(code)[0])
def toString(self, code):
return str(self.boolExpr.parseString(code)[0])
if __name__ == '__main__':
import doctest
doctest.testmod()