def get_boolean_evaluator(statment, variables=None, constants=None, grammar_tokens=None): """ an fast and easy to use helper to build a parser with variables and constants. return a callable which take a dict of value (for the variables). the returning scope will cotains the variables as is, and the constants into the namespace ``const:`` ie: 'age < const:majority & "o" in name & birthdate > "1983-02-02"' :param str statment: the statment to use :param list|tuple variables: the sample of variables :param dict constants: the value for the consts :param dict grammar_tokens: all extra parameters must be a valid grammar token replacements. (is_subset='in') :return: the parse tree that, if called, return a boolean result :rtype: :class:`booleano.parser.trees.ParseTree` """ grammar_tokens = grammar_tokens or {} grammar = Grammar(**grammar_tokens) if variables is None: variables = [{}] root_table = variable_symbol_table_builder('root', variables[0]) """:type: booleano.parser.scope.SymbolTable""" if constants: root_table.add_subtable( constants_symbol_table_builder('const', constants), ) parse_manager = EvaluableParseManager(root_table, grammar) return parse_manager.parse(statment)
def test_parsing_with_no_localized_grammars(self): mgr = EvaluableParseManager(self.symbol_table, Grammar()) parse_tree1 = mgr.parse('message == "2009-07-13"') parse_tree2 = mgr.parse('message == "2009-07-13"', None) expected_tree = EvaluableParseTree( Equal(String("Hello world"), String("2009-07-13"))) eq_(parse_tree1, parse_tree2) eq_(parse_tree1, expected_tree)
def _solve_rules(self, rules, metrics): """ solve the rules using the given metrics. metrics must contains all needed metrics. :param list[Rule] rules: the :param metrics: :return: :raises: pyparsing.ParseException """ results = {} root_table = SymbolTable('root', ()) for metric_name, values in metrics.items(): # bind to allow "rmq & rmq:xxx" root_table.add_object(Bind(metric_name, Constant(bool(values)))) # bind to allow "rmq:latency" etc root_table.add_subtable( SymbolTable( metric_name, tuple(Bind(k, Constant(v)) for k, v in values.items()))) # build the symbol table for all rules (as boolean) rules_symbols = SymbolTable('rules', ()) root_table.add_subtable(rules_symbols) for rule in rules: rule_name_ = rule['name'] rules_symbols.add_object( Bind( rule_name_, BooleanVariable( partial(get_rule_result, rule_name=rule_name_, rule=rule)))) rules_symbols.add_subtable( SymbolTable(rule_name_, (Bind( 'since', DurationVariable( partial(get_since, rule_name=rule_name_, rule=rule))), ))) parse_manager = EvaluableParseManager(root_table, grammar) for rule in rules: expression_ = rule['expression'] try: results[rule['name']] = parse_manager.parse(expression_)( results) except pyparsing.ParseException as e: logger.debug("error while parsing %r: %s", expression_, e) raise return results
def test_parsing_with_localized_grammars(self): castilian_grammar = Grammar(decimal_separator=",", thousands_separator=".") mgr = EvaluableParseManager(self.symbol_table, Grammar(), es=castilian_grammar) parse_tree = mgr.parse(u"tráfico:peatones_cruzando_calle <= 3,00", "es") expected_tree = EvaluableParseTree( LessEqual(PedestriansCrossingRoad(), Number(3.0))) eq_(parse_tree, expected_tree)
def test_adding_grammar(self): """It should be possible to add grammars after instantiation.""" castilian_grammar = Grammar(decimal_separator=",", thousands_separator=".") mgr = EvaluableParseManager(self.symbol_table, Grammar()) mgr.add_parser("es", castilian_grammar) parse_tree = mgr.parse(u'tráfico:peatones_cruzando_calle <= 3,00', "es") expected_tree = EvaluableParseTree( LessEqual(PedestriansCrossingRoad(), Number(3.0))) eq_(parse_tree, expected_tree)
def test_evaluating_expressions(self): """Managers should be able to evaluate the expressions too.""" mgr = EvaluableParseManager(self.symbol_table, Grammar()) context = {'pedestrians_crossroad': (u"gustavo", u"carla")} evaluation1 = mgr.evaluate("traffic:pedestrians_crossing_road <= 3", None, context) evaluation2 = mgr.evaluate( u'"carla" ∈ traffic:pedestrians_crossing_road', None, context) evaluation3 = mgr.evaluate("traffic:pedestrians_crossing_road > 2", None, context) ok_(evaluation1) ok_(evaluation2) assert_false(evaluation3)
def test_parsing_with_defined_grammar_but_no_available_translations(self): """ When an expression is written in an supported grammar but there are no translated bindings, the default names must be used along with the custom grammar. """ french_grammar = Grammar(decimal_separator=",", thousands_separator=".") mgr = EvaluableParseManager(self.symbol_table, Grammar(), fr=french_grammar) # French grammar is not supported: parse_tree = mgr.parse("traffic:pedestrians_crossing_road <= 3,0", "fr") expected_tree = EvaluableParseTree( LessEqual(PedestriansCrossingRoad(), Number(3))) eq_(parse_tree, expected_tree)
def test_parsing_with_undefined_grammar_and_no_translated_bindings(self): """ When an expression is written in an unsupported grammar, a parser based on the generic grammar must be created and used. If there are no translated bindings, the default names must be used. """ log_handler = LoggingHandlerFixture() mgr = EvaluableParseManager(self.symbol_table, Grammar()) # French grammar is not supported: parse_tree = mgr.parse("traffic:pedestrians_crossing_road <= 3.0", "fr") expected_tree = EvaluableParseTree( LessEqual(PedestriansCrossingRoad(), Number(3))) eq_(parse_tree, expected_tree) # Checking the log: info = "Generated parser for unknown grammar %s" % repr('fr') ok_(info in log_handler.handler.messages['info']) log_handler.undo()
def test_parsing_with_undefined_grammar_but_available_translations(self): """ When an expression is written in an unsupported grammar, a parser based on the generic grammar must be created and used. The respective translated bindings must be used if available. """ log_handler = LoggingHandlerFixture() mgr = EvaluableParseManager(self.symbol_table, Grammar()) # Castilian grammar is not supported: parse_tree = mgr.parse(u"tráfico:peatones_cruzando_calle <= 3.0", "es") expected_tree = EvaluableParseTree( LessEqual(PedestriansCrossingRoad(), Number(3))) eq_(parse_tree, expected_tree) # Checking the log: info = "Generated parser for unknown grammar %s" % repr(u'es') ok_(info in log_handler.handler.messages['info']) log_handler.undo()
def create_grammar(self): root_table = SymbolTable( "root", map(lambda f: Bind(f['name'], GeometryProperty(f['name'])), self.fields)) tokens = { 'not': 'not', 'eq': '==', 'ne': '!=', 'belongs_to': 'in', 'is_subset': 'are included in', 'or': "or", 'and': 'and' } grammar = Grammar(**tokens) self.parse_manager = EvaluableParseManager(root_table, grammar)
def test_adding_existing_grammar(self): """There can't be duplicate/overlapped parsers.""" mgr = EvaluableParseManager(self.symbol_table, Grammar(), es=Grammar()) assert_raises(GrammarError, mgr.add_parser, "es", Grammar())
class TestNativeVariableEvaluableParseManager(object): """ Tests for the :class:`EvaluableParseManager` with caching disabled. """ # A symbol table to be used across the tests: symbol_table = SymbolTable( "root", ( Bind("myint", NumberVariable("myint")), Bind("mybool", BooleanVariable("mybool")), Bind("mystring", StringVariable("mystring")), Bind("mydate", DateVariable("mydate")), Bind("mydate2", DateVariable("mydate2")), Bind("mydatetime", DateTimeVariable("mydatetime")), Bind("mydatetime2", DateTimeVariable("mydatetime2")), Bind("myset", SetVariable("myset")), Bind("myintbis", NumberVariable("myint")), ), SymbolTable( "sub", (Bind("myint", NumberVariable("sub.myint")), ), ), SymbolTable("const", ( Bind("hello", String("hello")), Bind("li", String("li")), ))) mgr = EvaluableParseManager( symbol_table, Grammar(belongs_to='in', is_subset='is subset of')) def test_variable_values_myint(self): ok_(self.mgr.parse('myint > 0')({'myint': 1})) ok_(not self.mgr.parse('myint > 0')({'myint': 0})) ok_(self.mgr.parse('myint == 1')({'myint': 1})) ok_(not self.mgr.parse('myint == 1')({'myint': 0})) ok_(self.mgr.parse('myint < 1')({'myint': 0})) ok_(not self.mgr.parse('myint < 0')({'myint': 2})) def test_variable_values_mybool(self): ok_(self.mgr.parse('mybool')({'mybool': True})) ok_(not self.mgr.parse('mybool')({'mybool': False})) ok_(self.mgr.parse('mybool > 0')({'mybool': True})) ok_(not self.mgr.parse('mybool > 0')({'mybool': False})) ok_(self.mgr.parse('mybool == 1')({'mybool': True})) ok_(not self.mgr.parse('mybool == 1')({'mybool': False})) ok_(self.mgr.parse('mybool < 1')({'mybool': False})) ok_(not self.mgr.parse('mybool < 0')({'mybool': True})) def test_variable_values_mystring(self): mystring = {'mystring': "hello"} # equiality ok_(self.mgr.parse('mystring == "hello"')(mystring)) ok_(not self.mgr.parse('mystring != "hello"')(mystring)) ok_(self.mgr.parse('mystring != "hell"')(mystring)) ok_(not self.mgr.parse('mystring == "hell"')(mystring)) # inequality ok_(self.mgr.parse('mystring > "hella"')(mystring)) ok_(self.mgr.parse('mystring < "hellz"')(mystring)) ok_(self.mgr.parse('mystring < "hellz"')(mystring)) ok_(self.mgr.parse('mystring <= "hello"')(mystring)) ok_(self.mgr.parse('mystring >= "hello"')(mystring)) # inequality reversed ok_(self.mgr.parse('"hella" < mystring')(mystring)) ok_(self.mgr.parse('"hellz" > mystring')(mystring)) ok_(self.mgr.parse('"hellz" > mystring')(mystring)) ok_(self.mgr.parse('"hello" >= mystring')(mystring)) ok_(self.mgr.parse('"hello" >= mystring')(mystring)) # bool ok_(self.mgr.parse('mystring')(mystring)) ok_(not self.mgr.parse('~ mystring')(mystring)) ok_(not self.mgr.parse('mystring')({'mystring': ''})) ok_(self.mgr.parse('~ mystring')({'mystring': ''})) # membership ok_(self.mgr.parse('mystring is subset of "zhelloy"')(mystring)) ok_(not self.mgr.parse('mystring is subset of "zhellai"')(mystring)) ok_(not self.mgr.parse('mystring is subset of "hello"')(mystring)) ok_(self.mgr.parse('mystring in "ahelloa"')(mystring)) ok_(self.mgr.parse('mystring in "hello"')(mystring)) ok_(not self.mgr.parse('mystring in "hola"')(mystring)) # inversed membership ok_(self.mgr.parse('"he" in mystring')(mystring)) ok_(self.mgr.parse('"lo" in mystring')(mystring)) ok_(not self.mgr.parse('"li" in mystring')(mystring)) ok_(self.mgr.parse('"he" is subset of mystring')(mystring)) ok_(self.mgr.parse('"lo" is subset of mystring')(mystring)) ok_(not self.mgr.parse('"li" is subset of mystring')(mystring)) ok_(not self.mgr.parse(' "hello" is subset of mystring')(mystring)) # test to string ok_(not self.mgr.parse('const:hello is subset of mystring')(mystring)) ok_(not self.mgr.parse('const:li in mystring')(mystring)) def test_variable_str(self): eq_("%s" % NativeVariable('coucou'), 'Scop variable for coucou [NativeVariable]') eq_("%s" % BooleanVariable('name'), 'Scop variable for name [BooleanVariable]') eq_("%s" % NumberVariable('age'), 'Scop variable for age [NumberVariable]') def test_variable_values_date(self): mydate = { 'mydate': datetime.date(2017, 1, 15), 'mydate2': datetime.date(2017, 1, 17), } ok_(self.mgr.parse('mydate')(mydate)) ok_(self.mgr.parse('mydate < "2017-01-18"')(mydate)) ok_(self.mgr.parse('mydate < "18-01-2017"')(mydate)) ok_(not self.mgr.parse('mydate < "12-01-2017"')(mydate)) ok_(self.mgr.parse('mydate > "12-01-2017"')(mydate)) ok_(self.mgr.parse('mydate == "15-01-2017"')(mydate)) ok_(not self.mgr.parse('mydate == 1')(mydate)) ok_(not self.mgr.parse('mydate == "13-01-2017"')(mydate)) ok_(self.mgr.parse('mydate < mydate2')(mydate)) ok_(not self.mgr.parse('mydate > mydate2')(mydate)) ok_(not self.mgr.parse('mydate == mydate2')(mydate)) def test_variable_values_datetime(self): mydatetime = { 'mydatetime': datetime.datetime(2017, 1, 15, 12, 25), 'mydatetime2': datetime.datetime(2017, 1, 17, 12, 23), } ok_(self.mgr.parse('mydatetime')(mydatetime)) ok_(self.mgr.parse('mydatetime < "2017-01-15 12:25:02"')(mydatetime)) ok_(self.mgr.parse('mydatetime > "2017-01-15 12:24:52"')(mydatetime)) ok_(self.mgr.parse('mydatetime < "2017-01-15 13:00:00"')(mydatetime)) ok_(self.mgr.parse('mydatetime < "18-01-2017 12:25:02"')(mydatetime)) ok_(self.mgr.parse('mydatetime < "18-01-2017 12:25:02"')(mydatetime)) ok_(not self.mgr.parse('mydatetime < "12-01-2017 12:25:02"') (mydatetime)) ok_(self.mgr.parse('mydatetime > "12-01-2017 12:25:02"')(mydatetime)) ok_(self.mgr.parse('mydatetime == "15-01-2017 12:25:00"')(mydatetime)) ok_(not self.mgr.parse('mydatetime == 1')(mydatetime)) ok_(not self.mgr.parse('mydatetime == "13-01-2017 12:25:02"') (mydatetime)) ok_(self.mgr.parse('mydatetime < mydatetime2')(mydatetime)) ok_(not self.mgr.parse('mydatetime > mydatetime2')(mydatetime)) ok_(not self.mgr.parse('mydatetime == mydatetime2')(mydatetime)) def test_variable_values_set(self): myset = { "myset": {1, 2, 3}, } ok_(self.mgr.parse("1 is subset of myset ")(myset)) ok_(self.mgr.parse("{1, 2} is subset of myset ")(myset)) ok_(not self.mgr.parse("{1, 2, 3} is subset of myset ")(myset)) ok_(self.mgr.parse("1 in myset ")(myset)) ok_(self.mgr.parse("{1, 2} in myset ")(myset)) ok_(self.mgr.parse("{1, 2, 3} in myset ")(myset)) def test_variable_values_namespaced(self): myints = {"myint": 1, "sub.myint": 4} ok_(self.mgr.parse('sub:myint > 3')(myints)) ok_(self.mgr.parse('sub:myint < 5')(myints)) ok_(self.mgr.parse('sub:myint == 4')(myints)) ok_(self.mgr.parse('sub:myint > myint')(myints)) ok_(not self.mgr.parse('sub:myint < myint')(myints)) ok_(not self.mgr.parse('sub:myint == myint')(myints)) def test_format_date(self): dt = DateVariable("mydate", '%d %m %y') assert_raises(ValueError, dt._from_native_string, "2001-03-04") eq_(dt._from_native_string("04 03 01"), datetime.date(2001, 3, 4)) def test_multi_formats(self): dt = DateVariable("mydate", ['%d %m %Y', '%Y %m %d']) assert_raises(ValueError, dt._from_native_string, "2001-03-04") assert_raises(ValueError, dt._from_native_string, "04-03-2001") eq_(dt._from_native_string("04 03 2001"), datetime.date(2001, 3, 4)) eq_(dt._from_native_string("2001 03 04"), datetime.date(2001, 3, 4))
root_table = SymbolTable( "root", ( # variablet taken from context Bind("age", NumberVariable('age')), Bind("name", StringVariable('name')), Bind("birthdate", DateVariable("birthdate")), Bind("tags", SetVariable("tags")), # constants Bind("majority", Number(18)), )) # 2: create the parser with se symbol table and the grammar (customized) grammar = Grammar(belongs_to='in') parse_manager = EvaluableParseManager(root_table, grammar) # 3: compile a expression # compiled_expression = parse_manager.parse('age < majority & "o" in name & birthdate > "1983-02-02"') # check the command line args if len(sys.argv) == 1: print("try something like %s '\"a\" in name'" % sys.argv[0]) exit(1) stmt = " ".join(sys.argv[1:]) print("searching with expression %r" % stmt) try: compiled_expression = parse_manager.parse(stmt) except pyparsing.ParseException as e: attrs = dict(expr=stmt, e=e,
'my_table', [ Bind('character', name), ], SymbolTable('character', [ Bind('age', NumberVariable('age')), Bind('name', name), Bind('training', DurationVariable('training')), Bind('bending', SetVariable('bending')), ]), SymbolTable('consts', [ Bind('majority', Number(18)), ])) grammar = Grammar(**{'and': 'and', 'or': 'or', 'belongs_to': 'in'}) # 2: create the parser with se symbol table and the grammar (customized) parse_manager = EvaluableParseManager(symbols, grammar) # 3: compile a expression compiled_expression = parse_manager.parse( 'character:age < consts:majority and ' '"a" in character and ' '(' ' {"water"} in character:bending or ' ' character:training > "3d4h"' ')') sample = [ { "name": "katara", "age": 14, "training": datetime.timedelta(days=24), "bending": {'water'}