Refactored to allow ConceptEvaluator
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
from evaluators.BaseEvaluator import BaseEvaluator
|
||||
from core.builtin_concepts import ParserResultConcept, ReturnValueConcept
|
||||
from core.concept import Concept
|
||||
from evaluators.BaseEvaluator import OneReturnValueEvaluator
|
||||
from parsers.DefaultParser import DefConceptNode
|
||||
import logging
|
||||
|
||||
from parsers.PythonParser import PythonGetNamesVisitor, PythonNode
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AddConceptEvaluator(BaseEvaluator):
|
||||
class AddConceptEvaluator(OneReturnValueEvaluator):
|
||||
"""
|
||||
Used to add a new concept
|
||||
"""
|
||||
@@ -13,11 +17,60 @@ class AddConceptEvaluator(BaseEvaluator):
|
||||
def __init__(self):
|
||||
super().__init__("Add new Concept", 50)
|
||||
|
||||
def matches(self, context, items):
|
||||
return len(items) == 1 and items[0].status and isinstance(items[0].value, DefConceptNode)
|
||||
def matches(self, context, return_value):
|
||||
return return_value.status and \
|
||||
isinstance(return_value.value, ParserResultConcept) and \
|
||||
isinstance(return_value.value.value, DefConceptNode)
|
||||
|
||||
def eval(self, context, items):
|
||||
def eval(self, context, return_value):
|
||||
log.debug("Adding a new concept")
|
||||
node = items[0].value
|
||||
def_concept_node = return_value.value.value
|
||||
sheerka = context.sheerka
|
||||
return sheerka.add_concept(context, node)
|
||||
|
||||
# validate the node
|
||||
props_found = set()
|
||||
|
||||
concept = Concept(def_concept_node.name)
|
||||
for prop in ("where", "pre", "post", "body"):
|
||||
# put back the sources
|
||||
part_ret_val = getattr(def_concept_node, prop)
|
||||
if not isinstance(part_ret_val, ReturnValueConcept) or not part_ret_val.status:
|
||||
continue # not quite sure that it's possible
|
||||
|
||||
# update the parts
|
||||
source = self.get_source(part_ret_val)
|
||||
setattr(concept, prop, source)
|
||||
|
||||
# try to find what can be a property
|
||||
for p in self.get_props(part_ret_val):
|
||||
props_found.add(p)
|
||||
|
||||
# Auto discovered properties must be referenced in the name
|
||||
# Note that with this method, the variables will be created in the order of appearance
|
||||
for token in def_concept_node.name.tokens:
|
||||
if token.value in props_found:
|
||||
concept.set_prop(token.value, None)
|
||||
|
||||
# finish initialisation
|
||||
concept.init_key(def_concept_node.name.tokens)
|
||||
concept.add_codes(def_concept_node.get_codes())
|
||||
|
||||
ret = sheerka.create_new_concept(context, concept)
|
||||
return sheerka.ret(self.name, ret.status, ret.value, parents=[return_value])
|
||||
|
||||
@staticmethod
|
||||
def get_source(ret_value):
|
||||
return ret_value.value.source if isinstance(ret_value.value, ParserResultConcept) \
|
||||
else ret_value.value.name
|
||||
|
||||
@staticmethod
|
||||
def get_props(ret_value):
|
||||
if isinstance(ret_value.value, ParserResultConcept) and isinstance(ret_value.value.value, PythonNode):
|
||||
get_names_visitor = PythonGetNamesVisitor()
|
||||
get_names_visitor.visit(ret_value.value.value.ast_)
|
||||
return get_names_visitor.names
|
||||
|
||||
if isinstance(ret_value.value, Concept):
|
||||
return list(ret_value.value.props.keys())
|
||||
|
||||
return []
|
||||
|
||||
@@ -1,14 +1,34 @@
|
||||
class BaseEvaluator:
|
||||
"""
|
||||
base class to evaluate concepts or nodes
|
||||
Base class to evaluate ReturnValues
|
||||
"""
|
||||
|
||||
PREFIX = "Evaluators:"
|
||||
|
||||
def __init__(self, name, priority: int):
|
||||
self.name = name
|
||||
self.name = self.PREFIX + name
|
||||
self.priority = priority
|
||||
|
||||
def matches(self, context, items):
|
||||
|
||||
class OneReturnValueEvaluator(BaseEvaluator):
|
||||
"""
|
||||
Evaluate one specific return value
|
||||
"""
|
||||
|
||||
def matches(self, context, return_value):
|
||||
pass
|
||||
|
||||
def eval(self, context, items):
|
||||
def eval(self, context, return_value):
|
||||
pass
|
||||
|
||||
|
||||
class AllReturnValuesEvaluator(BaseEvaluator):
|
||||
"""
|
||||
Evaluates the groups of ReturnValues
|
||||
"""
|
||||
|
||||
def matches(self, context, return_values):
|
||||
pass
|
||||
|
||||
def eval(self, context, return_values):
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
from core.concept import Concept, ConceptParts
|
||||
from evaluators.BaseEvaluator import OneReturnValueEvaluator
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ConceptEvaluator(OneReturnValueEvaluator):
|
||||
def __init__(self):
|
||||
super().__init__("Concept Evaluator", 50)
|
||||
|
||||
def matches(self, context, return_value):
|
||||
return return_value.status and \
|
||||
return_value.who == "Parsers:ConceptParser" and \
|
||||
isinstance(return_value.value, Concept)
|
||||
|
||||
def eval(self, context, return_value):
|
||||
sheerka = context.sheerka
|
||||
concept = return_value.value
|
||||
|
||||
# pre condition should already be validated by the parser.
|
||||
# It's a mandatory condition for the concept before it can be recognized
|
||||
|
||||
if len(concept.codes) == 0:
|
||||
sheerka.add_codes_to_concept(context, concept)
|
||||
|
||||
# TODO; check pre
|
||||
# if pre is not true, return Concept with a false value
|
||||
|
||||
body = concept.codes[ConceptParts.BODY]
|
||||
if body is None:
|
||||
return None # nothing to do
|
||||
|
||||
return sheerka.ret(self.name, True, body.value, parents=[return_value])
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
from core.concept import TooManySuccessConcept
|
||||
from core.sheerka import ReturnValue
|
||||
from evaluators.BaseEvaluator import BaseEvaluator
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DefaultEvaluator(BaseEvaluator):
|
||||
"""
|
||||
Used to filter the responses of the parsers
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Default Evaluator", 90)
|
||||
|
||||
def matches(self, context, items):
|
||||
return True
|
||||
|
||||
def eval(self, context, items):
|
||||
successful_results = [item for item in items if item.status]
|
||||
number_of_successful = len(successful_results)
|
||||
total_items = len(items)
|
||||
|
||||
# remove errors when a winner is found
|
||||
if number_of_successful == 1:
|
||||
log.debug(f"1 / {total_items} good item found.")
|
||||
return successful_results
|
||||
|
||||
# too many winners, which one to choose ?
|
||||
if number_of_successful > 1:
|
||||
log.debug(f"{number_of_successful} / {total_items} good items. Too many success")
|
||||
return ReturnValue(self.name,
|
||||
False,
|
||||
context.sheerka.new(TooManySuccessConcept.NAME, body=items))
|
||||
|
||||
# only errors, i cannot help you
|
||||
log.debug(f"{total_items} items. Only errors")
|
||||
return items
|
||||
@@ -0,0 +1,41 @@
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from evaluators.BaseEvaluator import AllReturnValuesEvaluator
|
||||
from parsers.BaseParser import BaseParser
|
||||
|
||||
|
||||
class DuplicateConceptEvaluator(AllReturnValuesEvaluator):
|
||||
"""
|
||||
Use to recognize when we tried to add the same concept twice
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Duplicate Concept Evaluator", 10)
|
||||
self.already_defined = None
|
||||
|
||||
def matches(self, context, return_values):
|
||||
sheerka = context.sheerka
|
||||
parsing = False
|
||||
add_concept_in_error = False
|
||||
only_parsers = True
|
||||
|
||||
for ret in return_values:
|
||||
if sheerka.isinstance(ret.value, BuiltinConcepts.PARSING):
|
||||
if ret.status:
|
||||
parsing = True
|
||||
elif ret.who == "Evaluators:Add new Concept":
|
||||
if not ret.status and ret.value.body.args[0] == "Duplicate object.":
|
||||
add_concept_in_error = True
|
||||
self.already_defined = ret.value.body.obj
|
||||
else:
|
||||
if not ret.who.startswith(BaseParser.PREFIX):
|
||||
only_parsers = False
|
||||
|
||||
return parsing and add_concept_in_error and only_parsers
|
||||
|
||||
def eval(self, context, return_values):
|
||||
sheerka = context.sheerka
|
||||
return sheerka.ret(
|
||||
self.name,
|
||||
False,
|
||||
sheerka.new(BuiltinConcepts.CONCEPT_ALREADY_DEFINED, obj=self.already_defined),
|
||||
parents=return_values)
|
||||
@@ -0,0 +1,42 @@
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from evaluators.BaseEvaluator import AllReturnValuesEvaluator
|
||||
import logging
|
||||
|
||||
from parsers.BaseParser import BaseParser
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ParsersEvaluator(AllReturnValuesEvaluator):
|
||||
"""
|
||||
Used to filter the responses
|
||||
It has a low priority to let other evaluators try to resolve the errors
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Parsers Evaluator", 10)
|
||||
self.successful_return_value = None
|
||||
|
||||
def matches(self, context, return_values):
|
||||
sheerka = context.sheerka
|
||||
after_parsing = False
|
||||
nb_successful_evaluators = 0
|
||||
only_parsers = True
|
||||
for ret in return_values:
|
||||
if sheerka.isinstance(ret.value, BuiltinConcepts.AFTER_PARSING):
|
||||
if ret.status:
|
||||
after_parsing = True
|
||||
elif ret.who.startswith(self.PREFIX):
|
||||
if ret.status:
|
||||
nb_successful_evaluators += 1
|
||||
self.successful_return_value = ret
|
||||
else:
|
||||
if not ret.who.startswith(BaseParser.PREFIX):
|
||||
only_parsers = False
|
||||
|
||||
return after_parsing and nb_successful_evaluators == 1 and only_parsers
|
||||
|
||||
def eval(self, context, return_values):
|
||||
sheerka = context.sheerka
|
||||
return sheerka.ret(self.name, True, self.successful_return_value.value, parents=return_values)
|
||||
@@ -1,32 +1,32 @@
|
||||
from core.concept import ReturnValueConcept, ErrorConcept
|
||||
from evaluators.BaseEvaluator import BaseEvaluator
|
||||
from core.builtin_concepts import BuiltinConcepts, ParserResultConcept
|
||||
from evaluators.BaseEvaluator import OneReturnValueEvaluator
|
||||
from parsers.PythonParser import PythonNode
|
||||
import ast
|
||||
from core.sheerka import ReturnValue, Sheerka
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PythonEvaluator(BaseEvaluator):
|
||||
class PythonEvaluator(OneReturnValueEvaluator):
|
||||
def __init__(self):
|
||||
super().__init__("Python Evaluator", 50)
|
||||
|
||||
def matches(self, context, items):
|
||||
return len(items) == 1 and isinstance(items[0].value, PythonNode)
|
||||
def matches(self, context, return_value):
|
||||
return return_value.status and \
|
||||
isinstance(return_value.value, ParserResultConcept) and \
|
||||
isinstance(return_value.value.value, PythonNode)
|
||||
|
||||
def eval(self, context, items):
|
||||
def eval(self, context, return_value):
|
||||
sheerka = context.sheerka
|
||||
node = items[0].value
|
||||
if isinstance(node.ast, ast.Expression):
|
||||
node = return_value.value.value
|
||||
if isinstance(node.ast_, ast.Expression):
|
||||
try:
|
||||
log.debug("Evaluating python expression")
|
||||
compiled = compile(node.ast, "<string>", "eval")
|
||||
log.debug(f"Evaluating python node {node}")
|
||||
compiled = compile(node.ast_, "<string>", "eval")
|
||||
evaluated = eval(compiled, {}, {"sheerka": context.sheerka})
|
||||
concept = sheerka.new(ReturnValueConcept.NAME, body=evaluated)
|
||||
return ReturnValue(self.name, True, concept)
|
||||
return sheerka.ret(self.name, True, evaluated, parents=[return_value])
|
||||
except Exception as error:
|
||||
error = sheerka.new(ErrorConcept.NAME, body=error)
|
||||
return ReturnValue(self.name, False, error)
|
||||
error = sheerka.new(BuiltinConcepts.ERROR, body=error)
|
||||
return sheerka.ret(self.name, False, error, parents=[return_value])
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
Reference in New Issue
Block a user