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
+4 -4
View File
@@ -2,13 +2,13 @@ import core.utils
from core.ast_helpers import UnreferencedVariablesVisitor
from core.builtin_concepts import ParserResultConcept, ReturnValueConcept, BuiltinConcepts
from core.concept import Concept, DEFINITION_TYPE_BNF, DEFINITION_TYPE_DEF
from core.global_symbols import NotInit
from core.sheerka.services.SheerkaExecute import ParserInput
from core.tokenizer import TokenKind, Tokenizer
from evaluators.BaseEvaluator import OneReturnValueEvaluator
from parsers.BaseParser import NotInitializedNode
from parsers.BnfNodeParser import ParsingExpression, ParsingExpressionVisitor
from parsers.DefConceptParser import DefConceptNode, NameNode
from parsers.PythonParser import PythonNode, get_python_node
from parsers.PythonParser import get_python_node
class ConceptOrRuleNameVisitor(ParsingExpressionVisitor):
@@ -70,7 +70,7 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
part_ret_val = getattr(def_concept_node, prop)
# put back the sources
if isinstance(part_ret_val, NotInitializedNode):
if part_ret_val is NotInit:
continue
elif isinstance(part_ret_val, NameNode):
source = str(part_ret_val)
@@ -107,7 +107,7 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
concept.init_key(key_source)
# update the bnf definition if needed
if not isinstance(def_concept_node.definition, NotInitializedNode) and \
if def_concept_node.definition is not NotInit and \
def_concept_node.definition_type == DEFINITION_TYPE_BNF:
concept.set_bnf(def_concept_node.definition.value.value)
+56
View File
@@ -0,0 +1,56 @@
import core.utils
from core.builtin_concepts import BuiltinConcepts, ParserResultConcept
from core.global_symbols import NotInit
from core.rule import Rule, ACTION_TYPE_PRINT, ACTION_TYPE_EXEC
from core.tokenizer import Keywords
from evaluators.BaseEvaluator import OneReturnValueEvaluator
from parsers.DefRuleParser import DefRuleNode, DefFormatRuleNode
class DefRuleEvaluator(OneReturnValueEvaluator):
"""
Used to store a new format rule
"""
NAME = "DefRule"
def __init__(self):
super().__init__(self.NAME, [BuiltinConcepts.EVALUATION], 50)
def matches(self, context, return_value):
return return_value.status and \
isinstance(return_value.value, ParserResultConcept) and \
isinstance(return_value.value.value, DefRuleNode)
def eval(self, context, return_value):
"""
Creates a Rule out of a DefRule and saves it in db
:param context:
:param return_value:
:return:
"""
context.log("Adding a new rule", self.name)
rule_definition = return_value.value.value
sheerka = context.sheerka
name = None if rule_definition.name is NotInit else str(rule_definition.name)
predicate = core.utils.get_text_from_tokens(rule_definition.tokens[Keywords.WHEN][1:])
if isinstance(rule_definition, DefFormatRuleNode):
action_type = ACTION_TYPE_PRINT
action = core.utils.get_text_from_tokens(rule_definition.tokens[Keywords.PRINT][1:])
compiled_action = rule_definition.print
else:
action_type = ACTION_TYPE_EXEC
action = core.utils.get_text_from_tokens(rule_definition.tokens[Keywords.THEN][1:])
compiled_action = rule_definition.then
rule = Rule(action_type, name, predicate, action)
rule.compiled_predicates = rule_definition.when
rule.rete_disjunctions = rule_definition.rete
rule.compiled_action = compiled_action
ret = sheerka.create_new_rule(context, rule)
if not ret.status:
error_cause = sheerka.objvalue(ret.body)
context.log(f"Failed to add new rule '{rule}'. Reason: {error_cause}", self.name)
return sheerka.ret(self.name, ret.status, ret.value, parents=[return_value])
-45
View File
@@ -1,45 +0,0 @@
import core.utils
from core.builtin_concepts import BuiltinConcepts, ParserResultConcept
from core.rule import Rule
from core.tokenizer import Keywords
from evaluators.BaseEvaluator import OneReturnValueEvaluator
from parsers.DefFormatRuleParser import FormatRuleNode
class FormatRuleEvaluator(OneReturnValueEvaluator):
"""
Used to store a new format rule
"""
NAME = "FormatRule"
def __init__(self):
super().__init__(self.NAME, [BuiltinConcepts.EVALUATION], 50)
def matches(self, context, return_value):
return return_value.status and \
isinstance(return_value.value, ParserResultConcept) and \
isinstance(return_value.value.value, FormatRuleNode)
def eval(self, context, return_value):
"""
Creates a Rule out of a FormatRuleNode and saves it in db
:param context:
:param return_value:
:return:
"""
context.log("Adding a new format rule", self.name)
format_rule_node = return_value.value.value
sheerka = context.sheerka
predicate = core.utils.get_text_from_tokens(format_rule_node.tokens[Keywords.WHEN][1:])
action = core.utils.get_text_from_tokens(format_rule_node.tokens[Keywords.PRINT][1:])
rule = Rule("print", None, predicate, action)
rule.compiled_predicate = format_rule_node.rule
rule.compiled_action = format_rule_node.format_ast
ret = sheerka.create_new_rule(context, rule)
if not ret.status:
error_cause = sheerka.objvalue(ret.body)
context.log(f"Failed to add new rule '{rule}'. Reason: {error_cause}", self.name)
return sheerka.ret(self.name, ret.status, ret.value, parents=[return_value])
+11 -8
View File
@@ -36,12 +36,16 @@ def inject_context(context):
class Expando:
def __init__(self, bag):
def __init__(self, name, bag):
self.__name = name
for k, v in bag.items():
setattr(self, k, v)
def __repr__(self):
return f"{dir(self)}"
return f"{vars(self)}"
def get_name(self):
return self.__name
@dataclass
@@ -89,12 +93,12 @@ class PythonEvaluator(OneReturnValueEvaluator):
def eval(self, context, return_value):
sheerka = context.sheerka
node = get_python_node(return_value.value.value)
node = return_value.value.value.get_python_node()
debugger = context.get_debugger(PythonEvaluator.NAME, "eval")
debugger.debug_entering(node=node)
exception_debugger = context.get_debugger("Exceptions", PythonEvaluator.NAME + ".eval")
get_trace_back = context.debug_enabled or exception_debugger.is_enabled()
get_trace_back = exception_debugger.is_enabled()
context.log(f"Evaluating python node {node}.", self.name)
@@ -179,8 +183,6 @@ class PythonEvaluator(OneReturnValueEvaluator):
"""
unreferenced_names_visitor = UnreferencedNamesVisitor(context)
names = unreferenced_names_visitor.get_names(node.ast_)
if context.debug_enabled:
context.debug(self.NAME, "eval", "names", names)
return self.get_globals_by_names(context, names, node, expression_only)
def get_sheerka_method(self, context, name, expression_only):
@@ -200,6 +202,7 @@ class PythonEvaluator(OneReturnValueEvaluator):
my_globals = {
"Concept": core.concept.Concept,
"BuiltinConcepts": core.builtin_concepts.BuiltinConcepts,
"Expando": Expando,
"ExecutionContext": ExecutionContext,
"in_context": context.in_context,
}
@@ -218,14 +221,14 @@ class PythonEvaluator(OneReturnValueEvaluator):
continue
# support reference to sheerka
if name == "sheerka":
if name.lower() == "sheerka":
bag = {}
visitor = NamesWithAttributesVisitor()
for sequence in visitor.get_sequences(node.ast_, "sheerka"):
if (len(sequence) > 1 and
(method := self.get_sheerka_method(context, sequence[1], expression_only)) is not None):
bag[sequence[1]] = method
my_globals["sheerka"] = Expando(bag)
my_globals[name] = Expando("sheerka", bag)
continue
# search in local variables. To remove when local variables will be merged with memory