EvalEvaluator is called only if in root context. Added action and action_context to ExecutionContext

This commit is contained in:
2020-06-12 17:47:29 +02:00
parent c43a3ef946
commit 912455c343
27 changed files with 292 additions and 117 deletions
+13
View File
@@ -15,6 +15,10 @@ class BuiltinConcepts(Enum):
"""
SHEERKA = "sheerka"
# Execution context actions
INIT_SHEERKA = "init sheerka" #
PROCESS_INPUT = "process input" # Processing user input or other input
PROCESSING = "processing input" # Processing user input or other input
BEFORE_PARSING = "before parsing" # activated before evaluation by the parsers
PARSING = "parsing" # activated during the parsing. It contains the text to parse
AFTER_PARSING = "after parsing" # after parsing
@@ -24,6 +28,15 @@ class BuiltinConcepts(Enum):
BEFORE_RENDERING = "before rendering" # activate before the output is rendered
RENDERING = "rendering" # rendering the response from sheerka
AFTER_RENDERING = "after rendering" # rendering the response from sheerka
EVALUATE_CONCEPT = "evaluate concept" # a concept will be evaluated
EVALUATING_CONCEPT = "evaluating concept" # a concept will be evaluated
VALIDATE_CONCEPT = "validate concept"
VALIDATING_CONCEPT = "validating concept"
INIT_COMPILED = "initializing concept compiled"
INIT_BNF = "ensure bnf"
MANAGE_INFINITE_RECURSION = "manage infinite recursion"
PARSE_CODE = "execute source code"
EXEC_CODE = "execute source code"
USER_INPUT = "user input" # represent an input from an user
SUCCESS = "success"
+5 -3
View File
@@ -25,7 +25,9 @@ def is_same_success(context, return_values):
if isinstance(ret_val.body, Concept):
if not ret_val.body.metadata.is_evaluated:
with context.push(desc=f"Evaluating concept '{ret_val.body}'") as sub_context:
with context.push(BuiltinConcepts.EVALUATE_CONCEPT,
ret_val.body,
desc=f"Evaluating concept '{ret_val.body}'") as sub_context:
sub_context.local_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
evaluated = context.sheerka.evaluate_concept(sub_context, ret_val.body)
if evaluated.key != ret_val.body.key:
@@ -237,7 +239,7 @@ def parse_unrecognized(context, source, parsers):
steps = [BuiltinConcepts.BEFORE_PARSING, BuiltinConcepts.PARSING, BuiltinConcepts.AFTER_PARSING]
sheerka = context.sheerka
with context.push(desc=f"Parsing unrecognized '{source}'") as sub_context:
with context.push(BuiltinConcepts.PARSING, source, desc=f"Parsing unrecognized '{source}'") as sub_context:
# disable all parsers but the following ones
sub_context.add_preprocess(BaseParser.PREFIX + "*", enabled=False)
for parser in parsers:
@@ -322,7 +324,7 @@ def ensure_evaluated(context, concept):
if concept.metadata.is_evaluated:
return concept
with context.push(desc=f"Evaluating concept {concept}") as sub_context:
with context.push(BuiltinConcepts.EVALUATE_CONCEPT, concept, desc=f"Evaluating concept {concept}") as sub_context:
sub_context.local_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
evaluated = context.sheerka.evaluate_concept(sub_context, concept)
sub_context.add_values(return_values=evaluated)
+30 -2
View File
@@ -115,7 +115,7 @@ class Concept:
if isinstance(other, simplec):
return self.name == other.name and self.body == other.body
if isinstance(other, (CC, CB, CMV)):
if isinstance(other, (CC, CB, CV, CMV)):
return other == self
if not isinstance(other, Concept):
@@ -589,7 +589,7 @@ class CC:
class CB:
"""
Concept with body only
Test class that test only the body of the concept
Test class that tests only the body of the concept
"""
concept: Union[str, Concept]
body: object
@@ -611,6 +611,34 @@ class CB:
return f"CB({self.body})"
class CV:
"""
Concept with all values
Test class that tests all the values (not the metadata, so not the properties) of a concept
"""
def __init__(self, concept, body, **kwargs):
self.concept_key = concept.key if isinstance(concept, Concept) else concept
self.concept = concept if isinstance(concept, Concept) else None
self.values = kwargs
self.values[ConceptParts.BODY] = body
def __eq__(self, other):
if isinstance(other, Concept):
return self.concept_key == other.key and self.values == other.values
if not isinstance(other, CV):
return False
return self.concept_key == other.concept_key and self.values == other.values
def __hash__(self):
return hash((self.concept_key, self.values))
def __repr__(self):
return f"CV(key={self.concept_key}, values={self.values})"
class CMV:
"""
Concept with metadata variables
+57 -30
View File
@@ -41,6 +41,8 @@ class ExecutionContext:
who,
event: Event,
sheerka,
action: BuiltinConcepts,
action_context,
desc: str = None,
logger=None,
global_hints=None,
@@ -53,11 +55,13 @@ class ExecutionContext:
self._bag = {} # context variables
self._start = 0 # when the execution starts (to measure elapsed time)
self._stop = 0 # when the execution stops (to measure elapses time)
self._format_instructions = None # how to print the execution context
self._format_instructions = None # how to print the execution context
self.who = who # who is asking
self.event = event # what was the (original) trigger
self.sheerka = sheerka # sheerka
self.action = action
self.action_context = action_context
self.desc = desc # human description of what is going on
self.children = []
self.preprocess = None
@@ -66,7 +70,6 @@ class ExecutionContext:
self.global_hints = set() if global_hints is None else global_hints
self.global_errors = [] if global_errors is None else global_errors
self.inputs = {} # what was the parameters of the execution context
self.values = {} # what was produced by the execution context
@@ -114,16 +117,16 @@ class ExecutionContext:
self.stat_log.debug(f"[{self._id:2}]" + self._tab + "Execution time: " + self.elapsed_str)
def __repr__(self):
msg = f"ExecutionContext(who={self.who}, id={self._id}"
msg = f"ExecutionContext(who={self.who}, id={self._id}, action={self.action}, context={self.action_context}"
if self.desc:
msg += f", desc='{self.desc}'"
msg += ")"
return msg
def __str__(self):
msg = self.desc or "New Context"
msg += f", who={self.who}, id={self.id}"
return msg
# def __str__(self):
# msg = self.desc or "New Context"
# msg += f", who={self.who}, id={self.id}"
# return msg
def __eq__(self, other):
if id(self) == id(other):
@@ -145,6 +148,31 @@ class ExecutionContext:
return True
def push(self, action: BuiltinConcepts, action_context, who=None, desc=None, logger=None, **kwargs):
who = who or self.who
logger = logger or self.logger
_kwargs = {"obj": self.obj, "concepts": self.concepts}
_kwargs.update(self._bag)
_kwargs.update(kwargs)
new = ExecutionContext(
who,
self.event,
self.sheerka,
action,
action_context,
desc,
logger,
self.global_hints,
self.global_errors,
**_kwargs)
new._parent = self
new._tab = self._tab + " " * DEBUG_TAB_SIZE
new.preprocess = self.preprocess
new.local_hints.update(self.local_hints)
self.children.append(new)
return new
def add_preprocess(self, name, **kwargs):
preprocess = self.sheerka.new(BuiltinConcepts.EVALUATOR_PRE_PROCESS)
preprocess.set_value("name", name)
@@ -205,29 +233,6 @@ class ExecutionContext:
return self.sheerka.new(key, **kwargs)
def push(self, who=None, desc=None, logger=None, **kwargs):
who = who or self.who
logger = logger or self.logger
_kwargs = {"obj": self.obj, "concepts": self.concepts}
_kwargs.update(self._bag)
_kwargs.update(kwargs)
new = ExecutionContext(
who,
self.event,
self.sheerka,
desc,
logger,
self.global_hints,
self.global_errors,
**_kwargs)
new._parent = self
new._tab = self._tab + " " * DEBUG_TAB_SIZE
new.preprocess = self.preprocess
new.local_hints.update(self.local_hints)
self.children.append(new)
return new
def log_new(self):
if self.logger and not self.logger.disabled:
self.logger.debug(f"[{self._id:2}]" + self._tab + str(self))
@@ -265,6 +270,9 @@ class ExecutionContext:
return False
def in_current_context(self, concept_key):
return concept_key in self.local_hints
@staticmethod
def _is_return_value(obj):
return isinstance(obj, Concept) and obj.key == str(BuiltinConcepts.RETURN_VALUE)
@@ -347,3 +355,22 @@ class ExecutionContext:
def set_format_instructions(self, instructions):
self._format_instructions = instructions
def get_parents(self, predicate=None):
"""
Gets all the parents that match the given predicate
:param predicate:
:return:
"""
res = []
current = self
while True:
parent = current._parent
if parent:
if predicate is None or predicate(parent):
res.append(parent)
current = parent
else:
break
return res
+14 -23
View File
@@ -18,7 +18,6 @@ from printer.SheerkaPrinter import SheerkaPrinter
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event
BASE_NODE_PARSER_CLASS = "parsers.BaseNodeParser.BaseNodeParser"
CONCEPTS_FILE = "_concepts.txt"
EXIT_COMMANDS = ("quit", "exit", "bye")
@@ -156,7 +155,13 @@ class Sheerka(Concept):
event = Event("Initializing Sheerka.", user_id=self.name)
self.sdp.save_event(event)
with ExecutionContext(self.key, event, self, "Initializing Sheerka.", self.init_log) as exec_context:
with ExecutionContext(self.key,
event,
self,
BuiltinConcepts.INIT_SHEERKA,
None,
desc="Initializing Sheerka.",
logger=self.init_log) as exec_context:
if self.sdp.first_time:
self.first_time_initialisation(exec_context)
@@ -355,7 +360,13 @@ class Sheerka(Concept):
evt_digest = self.sdp.save_event(event)
self.log.debug(f"{evt_digest=}")
with ExecutionContext(self.key, event, self, f"Evaluating '{text}'", self.log) as execution_context:
with ExecutionContext(self.key,
event,
self,
BuiltinConcepts.PROCESS_INPUT,
text,
desc=f"Evaluating '{text}'",
logger=self.log) as execution_context:
user_input = self.ret(self.name, True, self.new(BuiltinConcepts.USER_INPUT, body=text, user_name=user_name))
reduce_requested = self.ret(self.name, True, self.new(BuiltinConcepts.REDUCE_REQUESTED))
@@ -757,26 +768,6 @@ class Sheerka(Concept):
return sorted(res, key=lambda i: int(i.id))
def restore(self):
"""
Restore the state with all previous valid concept definitions
:return:
"""
try:
self.during_restore = True
with open(CONCEPTS_FILE, "r") as f:
for line in f.readlines():
line = line.strip()
if line == "" or line.startswith("#"):
continue
self.log.info(line)
res = self.evaluate_user_input(line)
if len(res) > 1 or not res[0].status:
self.log.error("Error detected !")
self.during_restore = False
except IOError:
pass
def get_last_execution(self):
return self._last_execution
+23
View File
@@ -1,6 +1,8 @@
from core.builtin_concepts import BuiltinConcepts
from core.sheerka.services.sheerka_service import BaseService
CONCEPTS_FILE = "_concepts.txt"
class SheerkaAdmin(BaseService):
NAME = "Admin"
@@ -11,6 +13,7 @@ class SheerkaAdmin(BaseService):
def initialize(self):
self.sheerka.bind_service_method(self.caches_names)
self.sheerka.bind_service_method(self.cache)
self.sheerka.bind_service_method(self.restore)
def caches_names(self):
"""
@@ -29,3 +32,23 @@ class SheerkaAdmin(BaseService):
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"cache": name})
return self.sheerka.cache_manager.caches[name].cache.copy()
def restore(self):
"""
Restore the state with all previous valid concept definitions
:return:
"""
try:
self.sheerka.during_restore = True
with open(CONCEPTS_FILE, "r") as f:
for line in f.readlines():
line = line.strip()
if line == "" or line.startswith("#"):
continue
self.sheerka.log.info(line)
res = self.sheerka.evaluate_user_input(line)
if len(res) > 1 or not res[0].status:
self.sheerka.log.error("Error detected !")
self.sheerka.during_restore = False
except IOError:
pass
+1 -1
View File
@@ -38,7 +38,7 @@ class SheerkaDump(BaseService):
def dump_desc(self, *concept_names, eval=False):
first = True
event = Event(f"Dumping description", "")
context = ExecutionContext("dump_desc", event, self.sheerka)
context = ExecutionContext("dump_desc", event, self.sheerka, BuiltinConcepts.RENDERING, concept_names)
for concept_name in concept_names:
if isinstance(concept_name, Concept):
concepts = concept_name
@@ -104,7 +104,9 @@ class SheerkaEvaluateConcept(BaseService):
context.log(f"Recognized concept '{concept_found}'", self.NAME)
concept.compiled[part_key] = concept_found
else:
with context.push(desc=f"Initializing *compiled* for {part_key}") as sub_context:
with context.push(BuiltinConcepts.INIT_COMPILED,
{"part": part_key, "source": source},
desc=f"Initializing *compiled* for {part_key}") as sub_context:
sub_context.add_inputs(source=source)
to_parse = self.sheerka.ret(context.who, True,
self.sheerka.new(BuiltinConcepts.USER_INPUT, body=source))
@@ -128,7 +130,9 @@ class SheerkaEvaluateConcept(BaseService):
context.log(f"Recognized concept '{concept_found}'", self.NAME)
concept.compiled[var_name] = concept_found
else:
with context.push(desc=f"Initializing *compiled* for property {var_name}") as sub_context:
with context.push(BuiltinConcepts.INIT_COMPILED,
{"property": var_name, "source": default_value},
desc=f"Initializing *compiled* for property {var_name}") as sub_context:
sub_context.add_inputs(source=default_value)
to_parse = self.sheerka.ret(context.who, True,
self.sheerka.new(BuiltinConcepts.USER_INPUT, body=default_value))
@@ -148,7 +152,10 @@ class SheerkaEvaluateConcept(BaseService):
# manage infinite loop
if self.infinite_recursion_detected(context, current_concept):
with context.push(desc="Infinite recursion detected", obj=current_concept) as sub_context:
with context.push(BuiltinConcepts.MANAGE_INFINITE_RECURSION,
current_concept,
desc="Infinite recursion detected",
obj=current_concept) as sub_context:
# I create a sub context in order to log what happened
ret_val = self.manage_infinite_recursion(context)
sub_context.add_values(return_values=ret_val)
@@ -156,7 +163,10 @@ class SheerkaEvaluateConcept(BaseService):
desc = f"Evaluating {current_prop} (concept={current_concept})"
context.log(desc, self.NAME)
with context.push(desc=desc, obj=current_concept) as sub_context:
with context.push(BuiltinConcepts.EVALUATING_CONCEPT,
current_prop,
desc=desc,
obj=current_concept) as sub_context:
if force_evaluation:
sub_context.local_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
+14 -4
View File
@@ -219,7 +219,9 @@ class SheerkaExecute(BaseService):
# else "'" + BaseParser.get_text_from_tokens(to_parse) + "' as tokens"
# context.log(f"Parsing {debug_text}")
with context.push(desc=f"Parsing using {parser.name}",
with context.push(BuiltinConcepts.PARSING,
{"parser": parser.name},
desc=f"Parsing using {parser.name}",
logger=parser.verbose_log) as sub_context:
sub_context.add_inputs(to_parse=to_parse)
res = parser.parse(sub_context, to_parse)
@@ -277,7 +279,10 @@ class SheerkaExecute(BaseService):
# process
iteration = 0
while True:
with context.push(desc=f"iteration #{iteration}", iteration=iteration) as iteration_context:
with context.push(process_step,
{"iteration": iteration},
desc=f"iteration #{iteration}",
iteration=iteration) as iteration_context:
simple_digest = return_values[:]
iteration_context.add_inputs(return_values=simple_digest)
@@ -290,7 +295,10 @@ class SheerkaExecute(BaseService):
evaluator = self.preprocess(context, evaluator.__class__()) # fresh copy
sub_context_desc = f"Evaluating using {evaluator.name} ({priority=})"
with iteration_context.push(desc=sub_context_desc, logger=evaluator.verbose_log) as sub_context:
with iteration_context.push(process_step,
{"iteration": iteration, "evaluator": evaluator.name},
desc=sub_context_desc,
logger=evaluator.verbose_log) as sub_context:
sub_context.add_inputs(return_values=original_items)
# process evaluators that work on one simple return value at the time
@@ -375,7 +383,9 @@ class SheerkaExecute(BaseService):
for step in execution_steps:
copy = return_values[:] if hasattr(return_values, "__iter__") else [return_values]
with context.push(step=step, iteration=0, desc=f"{step=}") as sub_context:
with context.push(BuiltinConcepts.PROCESSING,
{"step": step},
step=step, iteration=0, desc=f"{step=}") as sub_context:
if step == BuiltinConcepts.PARSING:
return_values = self.call_parsers(sub_context, return_values)
@@ -235,7 +235,9 @@ for x in xx__concepts__xx:
return [self.sheerka.get_by_id(element_id) for element_id in ids]
result = []
with context.push(desc=f"Evaluating concepts of a set") as sub_context:
with context.push(BuiltinConcepts.EVALUATE_CONCEPT,
{"ids": ids},
desc=f"Evaluating concepts of a set") as sub_context:
sub_context.add_inputs(ids=ids)
sub_context.local_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
for element_id in ids:
+1 -1
View File
@@ -35,7 +35,7 @@ class AddConceptInSetEvaluator(OneReturnValueEvaluator):
True,
sheerka.new(BuiltinConcepts.USER_INPUT, body=parser_input, user_name="N/A"))
with context.push(desc=f"Recognizing '{name_node}'") as sub_context:
with context.push(BuiltinConcepts.PROCESS_INPUT, name_node, desc=f"Recognizing '{name_node}'") as sub_context:
r = sheerka.execute(sub_context, ret_val, ALL_STEPS)
one_r = core.builtin_helpers.expect_one(context, r)
sub_context.add_values(return_values=one_r)
+4 -3
View File
@@ -62,7 +62,8 @@ class ConceptEvaluator(OneReturnValueEvaluator):
evaluated,
parents=[return_value])
if not self.return_body or ConceptParts.BODY not in evaluated.compiled:
return sheerka.ret(self.name, True, evaluated, parents=[return_value])
else:
if self.return_body and ConceptParts.BODY in evaluated.compiled:
return sheerka.ret(self.name, True, evaluated.body, parents=[return_value])
else:
return sheerka.ret(self.name, True, evaluated, parents=[return_value])
+3 -1
View File
@@ -14,7 +14,9 @@ class EvalEvaluator(AllReturnValuesEvaluator):
super().__init__(self.NAME, [BuiltinConcepts.AFTER_EVALUATION], 80)
def matches(self, context, return_values):
return context.in_context(BuiltinConcepts.CONCEPT_VALUE_REQUESTED)
evaluation_parents = context.get_parents(lambda c: c.action == BuiltinConcepts.PROCESSING)
is_root = len(evaluation_parents) <= 1
return context.in_context(BuiltinConcepts.CONCEPT_VALUE_REQUESTED) and is_root
def eval(self, context, return_values):
sheerka = context.sheerka
+5 -1
View File
@@ -200,7 +200,11 @@ class PythonEvaluator(OneReturnValueEvaluator):
context.log(f"Concept {name} is already evaluated.", self.name)
else:
context.log(f"Evaluating '{concept}'", self.name)
with context.push(self.name, desc=f"Evaluating '{concept}'", obj=concept) as sub_context:
with context.push(BuiltinConcepts.EVALUATE_CONCEPT,
concept,
who=self.name,
desc=f"Evaluating '{concept}'",
obj=concept) as sub_context:
sub_context.local_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
evaluated = context.sheerka.evaluate_concept(sub_context, concept)
sub_context.add_values(return_values=evaluated)
+5 -1
View File
@@ -798,7 +798,11 @@ class BaseNodeParser(BaseParser):
from parsers.BnfParser import BnfParser
regex_parser = BnfParser()
desc = f"Resolving BNF {concept.metadata.definition}"
with context.push(parser_name, obj=concept, desc=desc) as sub_context:
with context.push(BuiltinConcepts.INIT_BNF,
concept,
who=parser_name,
obj=concept,
desc=desc) as sub_context:
sub_context.add_inputs(parser_input=concept.metadata.definition)
bnf_parsing_ret_val = regex_parser.parse(sub_context, concept.metadata.definition)
sub_context.add_values(return_values=bnf_parsing_ret_val)
+1 -1
View File
@@ -845,7 +845,7 @@ class BnfNodeParser(BaseNodeParser):
expression = concept.bnf
desc = f"Resolving parsing expression {expression}"
with self.context.push(self.name, obj=concept, desc=desc) as sub_context:
with self.context.push(BuiltinConcepts.INIT_BNF, concept, who=self.name, obj=concept, desc=desc) as sub_context:
sub_context.add_inputs(expression=expression)
resolved = self.resolve_parsing_expression(expression, already_seen or set())
sub_context.add_values(return_values=resolved)
+6 -2
View File
@@ -357,7 +357,11 @@ class DefaultParser(BaseParser):
regex_parser = BnfParser()
desc = f"Resolving BNF {current_concept_def.definition}"
with self.context.push(self.name, obj=current_concept_def, desc=desc) as sub_context:
with self.context.push(BuiltinConcepts.INIT_BNF,
current_concept_def,
who=self.name,
obj=current_concept_def,
desc=desc) as sub_context:
parsing_result = regex_parser.parse(sub_context, tokens)
sub_context.add_values(return_values=parsing_result)
@@ -402,7 +406,7 @@ class DefaultParser(BaseParser):
continue
# ask the other parsers if they recognize the tokens
with self.context.push(self.name, desc=f"Parsing {keyword}") as sub_context:
with self.context.push(BuiltinConcepts.PARSING, keyword, who=self.name, desc=f"Parsing {keyword}") as sub_context:
parser_input = self.sheerka.services[SheerkaExecute.NAME].get_parser_input(None, tokens)
to_parse = self.sheerka.ret(
sub_context.who,
+3 -1
View File
@@ -133,7 +133,9 @@ class LexerNodeParserHelperForPython:
source += node.source
to_parse += node.source
with context.push(self, desc="Trying Python for '" + to_parse + "'") as sub_context:
with context.push(BuiltinConcepts.PARSE_CODE,
{"language": "Python", "source": to_parse},
desc="Trying Python for '" + to_parse + "'") as sub_context:
sub_context.add_inputs(to_parse=to_parse)
python_parser = PythonParser()
parser_input = context.sheerka.services[SheerkaExecute.NAME].get_parser_input(to_parse)
+3 -1
View File
@@ -77,7 +77,9 @@ class PythonWithConceptsParser(BaseParser):
source += node.source
to_parse += node.source
with context.push(self, "Trying Python for '" + to_parse + "'") as sub_context:
with context.push(BuiltinConcepts.PARSE_CODE,
{"language": "Python", "source": to_parse},
"Trying Python for '" + to_parse + "'") as sub_context:
parser_input = context.sheerka.services[SheerkaExecute.NAME].get_parser_input(to_parse)
python_parser = PythonParser()
result = python_parser.parse(sub_context, parser_input)
+2 -2
View File
@@ -145,7 +145,7 @@ class ExecutionContextHandler(BaseHandler):
pickler = self.context
for prop in CONTEXT_PROPERTIES_TO_SERIALIZE:
if prop == "who":
if prop in ("who", "action", "action_context"):
value = str(getattr(obj, prop))
else:
value = getattr(obj, prop)
@@ -156,7 +156,7 @@ class ExecutionContextHandler(BaseHandler):
return data
def new(self, data):
return ExecutionContext(data["who"], None, None)
return ExecutionContext(data["who"], None, None, BuiltinConcepts.NOP, None)
def restore(self, data, instance):
pickler = self.context