Reimplemented explain feature

This commit is contained in:
2020-06-04 18:43:15 +02:00
parent c498b394e3
commit d7573f095f
27 changed files with 1673 additions and 1161 deletions
-150
View File
@@ -1,150 +0,0 @@
from typing import List
from core.builtin_concepts import BuiltinConcepts, ParserResultConcept
from core.sheerka.ExecutionContext import ExecutionContext
from evaluators.BaseEvaluator import OneReturnValueEvaluator
from parsers.ExplainParser import ExplanationNode, FilterNode, RecurseDefNode, FormatLNode, FormatDNode
from parsers.ExpressionParser import ExpressionVisitor, IsaNode
from printer.SheerkaPrinter import FormatInstructions
class ExplainExpressionVisitor(ExpressionVisitor):
def __init__(self):
self.instructions = FormatInstructions()
def visit_RecurseDefNode(self, expr_node):
self.instructions.set_recurse("children", expr_node.depth)
def visit_FormatLNode(self, expr_node):
self.instructions.set_format_l(ExecutionContext, expr_node.template)
class ExplainEvaluator(OneReturnValueEvaluator):
NAME = "Explain"
def __init__(self):
super().__init__(self.NAME, [BuiltinConcepts.EVALUATION], 60)
def get_event_digest(self, sheerka, explanation_node):
if explanation_node.digest and sheerka.sdp.has_result(explanation_node.digest):
return explanation_node.digest
if not explanation_node.digest and not explanation_node.record_digest:
# use a previous digest if found
digest = sheerka.load(self.name, "digest")
if digest is not None:
return digest
start = 0
while True:
events = list(sheerka.sdp.load_events(5, start))
if not events:
break
for event in events:
if not sheerka.sdp.has_result(event.get_digest()):
continue
if not explanation_node.digest or explanation_node.digest == event.message:
# maybe explanation_node.digest is not a real digest, but the command we want to explain
return event.get_digest()
start += 5
if start > 20:
break
return None
@staticmethod
def get_execution_result(sheerka, digest):
if digest is None:
# the test is done here to ease the unit tests
return None
return [sheerka.sdp.load_result(digest)]
@staticmethod
def get_instructions(filter_node: FilterNode):
instructions = FormatInstructions()
for directive in filter_node.directives:
if isinstance(directive, RecurseDefNode):
instructions.set_recurse("children", directive.depth)
elif isinstance(directive, FormatLNode):
instructions.set_format_l(ExecutionContext, directive.template)
elif isinstance(directive, FormatDNode):
instructions.add_format_d(IsaNode(ExecutionContext), directive.properties)
return instructions
@staticmethod
def get_title(filter_node):
return "<title>"
def matches(self, context, return_value):
if not return_value.status:
return False
if not isinstance(return_value.value, ParserResultConcept):
return False
return isinstance(return_value.value.value, ExplanationNode)
def eval(self, context, return_value):
sheerka = context.sheerka
explanation_node = return_value.value.value
if explanation_node.digest and not explanation_node.record_digest:
context.log(f"Deleting recorded digest")
sheerka.delete(context, self.name, "digest")
digest = self.get_event_digest(sheerka, explanation_node)
executions_results = self.get_execution_result(sheerka, digest)
if executions_results is None and not digest:
res = sheerka.new(BuiltinConcepts.ERROR, body=f"No result found (digest={explanation_node.digest})")
else:
# record the digest if needed
if explanation_node.record_digest:
context.log(f"Recording digest '{digest}'")
sheerka.record(context, self.name, "digest", digest)
filter_nodes = explanation_node.expr.filters
global_instructions = self.get_instructions(filter_nodes[0])
if len(filter_nodes) == 1:
filtered = [[]]
self.filter(executions_results, filter_nodes, filtered)
res = sheerka.new(BuiltinConcepts.EXPLANATION,
digest=digest,
command=explanation_node.command,
title="<all>",
body=filtered[0],
instructions=global_instructions)
else:
res = []
filter_nodes = filter_nodes[1:] # remove the first filter_node (which always returns True)
filtered = []
for i in range(len(filter_nodes)):
filtered.append([])
self.filter(executions_results, filter_nodes, filtered)
for i, filter_node in enumerate(filter_nodes):
instructions = global_instructions.clone().merge(self.get_instructions(filter_node))
res.append(sheerka.new(BuiltinConcepts.EXPLANATION,
digest=digest,
command=explanation_node.command,
title=self.get_title(filter_node),
body=filtered[i],
instructions=instructions))
if len(res) == 1:
res = res[0]
return sheerka.ret(self.name, not sheerka.isinstance(res, BuiltinConcepts.ERROR), res, parents=[return_value])
def filter(self, executions_results, filter_nodes: List[FilterNode], res):
for execution_result in executions_results:
for i, filter_node in enumerate(filter_nodes):
if filter_node.expr.eval(execution_result):
res[i].append(execution_result)
if execution_result.children:
self.filter(execution_result.children, filter_nodes, res)
return res
+15 -9
View File
@@ -1,8 +1,10 @@
import ast
import copy
import traceback
from functools import partial, update_wrapper
import core.ast.nodes
from core.sheerka.services.SheerkaFilter import Pipe
import core.utils
from core.ast.visitors import UnreferencedNamesVisitor
from core.builtin_concepts import BuiltinConcepts, ParserResultConcept
@@ -86,22 +88,22 @@ class PythonEvaluator(OneReturnValueEvaluator):
return sheerka.ret(self.name, False, error, parents=[return_value])
def get_globals(self, context, node):
my_locals = {
my_globals = {
"Concept": core.concept.Concept,
"BuiltinConcepts": core.builtin_concepts.BuiltinConcepts,
}
# has to tbe the first, to allow override
method_from_sheerka = self.update_globals_with_sheerka_methods(my_locals, context)
# has to be the first, to allow override
method_from_sheerka = self.update_globals_with_sheerka_methods(my_globals, context)
self.update_globals_with_context(my_locals, context)
self.update_globals_with_node(my_locals, context, node)
self.update_globals_with_context(my_globals, context)
self.update_globals_with_node(my_globals, context, node)
if self.locals: # when exta values are given. Add them
my_locals.update(self.locals)
if self.locals: # when extra values are given. Add them
my_globals.update(self.locals)
my_locals["sheerka"] = Expando(method_from_sheerka) # it's the last, so I cannot be overridden
return my_locals
my_globals["sheerka"] = Expando(method_from_sheerka) # it's the last, so I cannot be overridden
return my_globals
@staticmethod
def update_globals_with_sheerka_methods(my_locals, context):
@@ -118,6 +120,10 @@ class PythonEvaluator(OneReturnValueEvaluator):
for method_name, method in methods_from_sheerka.items():
my_locals[method_name] = method
# Add pipeable functions
for func_name, function in context.sheerka.sheerka_pipeables.items():
my_locals[func_name] = Pipe(function, context)
return methods_from_sheerka # to allow access using prefix "sheerka."
def update_globals_with_context(self, my_locals, context):