Refactored to allow ConceptEvaluator

This commit is contained in:
2019-11-14 22:04:38 +01:00
parent 576ce77740
commit 9e10e77737
30 changed files with 2406 additions and 1007 deletions
+60 -7
View File
@@ -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 []
+24 -4
View File
@@ -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
+35
View File
@@ -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])
-39
View File
@@ -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
+41
View File
@@ -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)
+42
View File
@@ -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)
+15 -15
View File
@@ -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()