Implemented a first and basic version of a Rete rule engine
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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])
|
||||
@@ -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])
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user