Implemented a first and basic version of a Rete rule engine

This commit is contained in:
2021-02-09 16:06:32 +01:00
parent 821dbed189
commit a2a8d5c5e5
110 changed files with 7301 additions and 1654 deletions
+60
View File
@@ -0,0 +1,60 @@
import pytest
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
from core.rule import Rule, RuleMetadata
from core.sheerka.services.SheerkaExecute import ParserInput
from core.tokenizer import Tokenizer
from evaluators.DefRuleEvaluator import DefRuleEvaluator
from parsers.DefRuleParser import DefFormatRuleNode, DefRuleParser, DefExecRuleNode
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
class TestDefRuleEvaluator(TestUsingMemoryBasedSheerka):
@staticmethod
def get_ret_val_from_rule(rule_def):
tokens = {k: list(Tokenizer(k.value + " " + v, yield_eof=False)) for k, v in rule_def.items()}
for v in tokens.values():
del v[1]
node = DefFormatRuleNode(tokens)
return ReturnValueConcept("parsers.FormatRule", True, ParserResultConcept(value=node))
@pytest.mark.parametrize("ret_val, expected", [
(ReturnValueConcept("name", True, ParserResultConcept(value=DefFormatRuleNode({}))), True),
(ReturnValueConcept("name", True, ParserResultConcept(value=DefExecRuleNode({}))), True),
(ReturnValueConcept("name", True, ParserResultConcept(value="other object")), False),
(ReturnValueConcept("name", False, ParserResultConcept(value=DefFormatRuleNode({}))), False),
(ReturnValueConcept("name", False, DefFormatRuleNode({})), False),
])
def test_i_can_match(self, ret_val, expected):
context = self.get_context()
assert DefRuleEvaluator().matches(context, ret_val) == expected
@pytest.mark.parametrize("text, expected_action_type, expected_name", [
("when True print 'hello world'", "print", None),
("when True then 'hello world'", "exec", None),
("def rule rule name as when True print 'hello world'", "print", "rule name"),
("def rule rule name as when True then 'hello world'", "exec", "rule name"),
])
def test_i_can_eval(self, text, expected_action_type, expected_name):
sheerka, context = self.init_concepts()
ret_val = DefRuleParser().parse(context, ParserInput(text))
res = DefRuleEvaluator().eval(context, ret_val)
assert res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.NEW_RULE)
assert isinstance(res.body.body, Rule)
assert res.body.body.metadata == RuleMetadata(expected_action_type,
expected_name,
"True",
"'hello world'",
id=res.body.body.metadata.id, # no need to compare the id
is_compiled=True,
is_enabled=True)
rule = res.body.body
assert rule.compiled_predicates is not None
assert rule.compiled_action is not None
assert rule.rete_disjunctions is not None
@@ -1,51 +0,0 @@
import pytest
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
from core.rule import Rule, RuleMetadata
from core.sheerka.services.SheerkaExecute import ParserInput
from core.tokenizer import Tokenizer
from evaluators.FormatRuleEvaluator import FormatRuleEvaluator
from parsers.DefFormatRuleParser import FormatRuleNode, DefFormatRuleParser
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
class TestFormatRuleEvaluator(TestUsingMemoryBasedSheerka):
@staticmethod
def get_ret_val_from_rule(rule_def):
tokens = {k: list(Tokenizer(k.value + " " + v, yield_eof=False)) for k, v in rule_def.items()}
for v in tokens.values():
del v[1]
node = FormatRuleNode(tokens)
return ReturnValueConcept("parsers.FormatRule", True, ParserResultConcept(value=node))
@pytest.mark.parametrize("ret_val, expected", [
(ReturnValueConcept("name", True, ParserResultConcept(value=FormatRuleNode({}))), True),
(ReturnValueConcept("name", True, ParserResultConcept(value="other object")), False),
(ReturnValueConcept("name", False, ParserResultConcept(value=FormatRuleNode({}))), False),
(ReturnValueConcept("name", False, FormatRuleNode({})), False),
])
def test_i_can_match(self, ret_val, expected):
context = self.get_context()
assert FormatRuleEvaluator().matches(context, ret_val) == expected
def test_i_can_eval(self):
sheerka, context = self.init_concepts()
text = "when isinstance(value, __EXPLANATION) print list(value)"
ret_val = DefFormatRuleParser().parse(context, ParserInput(text))
res = FormatRuleEvaluator().eval(context, ret_val)
assert res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.NEW_RULE)
assert isinstance(res.body.body, Rule)
assert res.body.body.metadata == RuleMetadata("print",
None,
"isinstance(value, __EXPLANATION)",
"list(value)",
id=res.body.body.metadata.id, # no need to compare the id
is_compiled=True,
is_enabled=True)
assert res.body.body.compiled_predicate is not None
assert res.body.body.compiled_action is not None
+1 -1
View File
@@ -87,6 +87,6 @@ class TestLexerNodeEvaluator(TestUsingMemoryBasedSheerka):
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
assert wrapper.source == "foo + 1"
assert return_value == PythonNode('foo + 1', ast.parse("__C__foo__C__ + 1", mode="eval"))
assert return_value == PythonNode('__C__foo__C__ + 1', ast.parse("__C__foo__C__ + 1", mode="eval"), "foo + 1")
assert return_value.objects == {"__C__foo__C__": foo}
assert result.parents == [ret_val]
+2 -1
View File
@@ -3,6 +3,7 @@ import ast
import pytest
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
from core.builtin_helpers import CreateObjectIdentifiers
from core.concept import Concept, CB
from core.sheerka.services.SheerkaConceptManager import SheerkaConceptManager
from core.sheerka.services.SheerkaExecute import ParserInput
@@ -26,7 +27,7 @@ def return_return_value(status):
def get_source_code_node(source_code, concepts=None):
if concepts:
for concept_name, concept in sorted(concepts.items(), key=lambda kv: len(kv[0]), reverse=True):
identifier = "__C__" + PythonWithConceptsParser.sanitize(concept.name)
identifier = "__C__" + CreateObjectIdentifiers.sanitize(concept.name)
if concept.id:
identifier += "__" + concept.id
identifier += "__C__"