Implemented a first and basic version of a Rete rule engine
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import core.utils
|
||||
from cache.Cache import Cache
|
||||
from cache.FastCache import FastCache
|
||||
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept
|
||||
from core.global_symbols import NotFound
|
||||
@@ -16,6 +15,8 @@ EVALUATOR_STEPS = [
|
||||
BuiltinConcepts.BEFORE_RENDERING,
|
||||
BuiltinConcepts.RENDERING,
|
||||
BuiltinConcepts.AFTER_RENDERING,
|
||||
BuiltinConcepts.BEFORE_RULES_EVALUATION,
|
||||
BuiltinConcepts.AFTER_RULES_EVALUATION,
|
||||
]
|
||||
|
||||
|
||||
@@ -120,6 +121,10 @@ class ParserInput:
|
||||
return self.pos < self.end
|
||||
|
||||
def the_token_after(self, skip_whitespace=True):
|
||||
"""
|
||||
Returns the token after the current one
|
||||
Never returns None (returns TokenKind.EOF instead)
|
||||
"""
|
||||
my_pos = self.pos + 1
|
||||
if my_pos >= self.end:
|
||||
return Token(TokenKind.EOF, "", -1, -1, -1)
|
||||
@@ -167,7 +172,8 @@ class SheerkaExecute(BaseService):
|
||||
PARSERS_INPUTS_ENTRY = "Execute:ParserInput" # entry for admin or internal variables
|
||||
|
||||
def __init__(self, sheerka):
|
||||
super().__init__(sheerka)
|
||||
# order must be after SheerkaEvaluateRules because of self.rules_evaluation_service
|
||||
super().__init__(sheerka, order=5)
|
||||
self.pi_cache = FastCache(default=lambda key: ParserInput(key), max_size=20)
|
||||
self.instantiated_evaluators = None
|
||||
self.evaluators_by_name = None
|
||||
@@ -191,12 +197,18 @@ class SheerkaExecute(BaseService):
|
||||
# Except 2 : we store the type of the parser, not its instance
|
||||
self.grouped_parsers_cache = {}
|
||||
|
||||
self.rules_eval_service = None
|
||||
|
||||
def initialize(self):
|
||||
self.sheerka.bind_service_method(self.execute, True)
|
||||
self.sheerka.bind_service_method(self.execute, True, visible=False)
|
||||
self.sheerka.bind_service_method(self.execute_rules, True, visible=False)
|
||||
|
||||
self.reset_registered_evaluators()
|
||||
self.reset_registered_parsers()
|
||||
|
||||
from core.sheerka.services.SheerkaEvaluateRules import SheerkaEvaluateRules
|
||||
self.rules_eval_service = self.sheerka.services[SheerkaEvaluateRules.NAME]
|
||||
|
||||
def reset_state(self):
|
||||
self.pi_cache.clear()
|
||||
|
||||
@@ -347,7 +359,7 @@ class SheerkaExecute(BaseService):
|
||||
if pi is NotFound: # when CacheManager.cache_only is True
|
||||
pi = ParserInput(text)
|
||||
self.pi_cache.put(text, pi)
|
||||
return pi
|
||||
return ParserInput(text, pi.tokens) # new instance, but no need to tokenize the text again
|
||||
|
||||
key = text or core.utils.get_text_from_tokens(tokens)
|
||||
pi = ParserInput(key, tokens)
|
||||
@@ -582,6 +594,55 @@ class SheerkaExecute(BaseService):
|
||||
|
||||
return return_values
|
||||
|
||||
def execute_rules(self, context, return_values, rules_steps, evaluation_steps):
|
||||
""""
|
||||
Executes the execution rules until no match is found
|
||||
:param context:
|
||||
:param return_values: input return values
|
||||
:param rules_steps: steps are configurable
|
||||
:param evaluation_steps: steps are configurable
|
||||
:return: out return_values
|
||||
"""
|
||||
continue_execution = True
|
||||
counter = 0
|
||||
in_rete_memory = None
|
||||
while continue_execution:
|
||||
with context.push(BuiltinConcepts.PROCESSING, {"counter": counter}, desc=f"{counter=}") as sub_context:
|
||||
|
||||
# apply rule evaluation steps
|
||||
for step in rules_steps:
|
||||
if step == BuiltinConcepts.RULES_EVALUATION:
|
||||
eval_res = self.rules_eval_service.evaluate_exec_rules(sub_context, return_values)
|
||||
if not eval_res:
|
||||
self.rules_eval_service.remove_from_rete_memory(return_values)
|
||||
continue_execution = False
|
||||
break
|
||||
else:
|
||||
in_rete_memory = return_values.copy()
|
||||
return_values = eval_res
|
||||
else:
|
||||
return_values = self.call_evaluators(sub_context, return_values, step)
|
||||
|
||||
if not continue_execution:
|
||||
break
|
||||
|
||||
# evaluate the result
|
||||
return_values = [r.body.body.compiled_action for r in return_values]
|
||||
while True:
|
||||
copy = return_values[:]
|
||||
for step in evaluation_steps:
|
||||
return_values = self.call_evaluators(sub_context, return_values, step)
|
||||
|
||||
if copy == return_values[:]:
|
||||
break
|
||||
|
||||
# evaluation is done. Remove object in Rete memory
|
||||
self.rules_eval_service.remove_from_rete_memory(in_rete_memory)
|
||||
|
||||
counter += 1
|
||||
|
||||
return return_values
|
||||
|
||||
def undo_preprocess(self):
|
||||
for item, var_name, value in self.old_values:
|
||||
setattr(item, var_name, value)
|
||||
|
||||
Reference in New Issue
Block a user