Added RET keyword
This commit is contained in:
@@ -68,6 +68,7 @@ class BuiltinConcepts(Enum):
|
||||
EVAL_WHERE_REQUESTED = "eval where requested" # to evaluate the where clause
|
||||
CONCEPT_VALUE_REQUESTED = "concept value requested" # returns the body of the concept instead of the concept itself
|
||||
REDUCE_REQUESTED = "reduce requested" # remove meaningless error when possible
|
||||
EVAL_SUCCESS_REQUESTED = "Try to find a successful evaluation" # PyhtonEvaluator tries combination until True is found
|
||||
NOT_A_SET = "not a set" # the concept has no entry in sets
|
||||
WHERE_CLAUSE_FAILED = "where clause failed" # failed to validate where clause during evaluation
|
||||
CHICKEN_AND_EGG = "chicken and egg" # infinite recursion when declaring concept
|
||||
@@ -78,6 +79,7 @@ class BuiltinConcepts(Enum):
|
||||
NOT_INITIALIZED = "not initialized"
|
||||
NOT_FOUND = "not found" # when the wanted resource is not found
|
||||
FORMAT_INSTRUCTIONS = "format instructions" # to express how to print the concept
|
||||
NOT_IMPLEMENTED = "not implemented" # instead of raise an error
|
||||
|
||||
NODE = "node"
|
||||
GENERIC_NODE = "generic node"
|
||||
|
||||
@@ -314,11 +314,12 @@ def get_lexer_nodes(return_values, start, tokens):
|
||||
return lexer_nodes
|
||||
|
||||
|
||||
def ensure_evaluated(context, concept):
|
||||
def ensure_evaluated(context, concept, eval_body=True):
|
||||
"""
|
||||
Evaluate a concept is not already evaluated
|
||||
:param context:
|
||||
:param concept:
|
||||
:param eval_body:
|
||||
:return:
|
||||
"""
|
||||
if concept.metadata.is_evaluated:
|
||||
@@ -326,10 +327,14 @@ def ensure_evaluated(context, concept):
|
||||
|
||||
# do not try to evaluate concept that are not fully initialized
|
||||
for var in concept.metadata.variables:
|
||||
if var[0] not in concept.values or concept.get_value(var[0]) == NotInit:
|
||||
# to code
|
||||
if var[1] is None and \
|
||||
var[0] not in concept.compiled and \
|
||||
(var[0] not in concept.values or concept.get_value(var[0]) == NotInit):
|
||||
return concept
|
||||
|
||||
with context.push(BuiltinConcepts.EVALUATE_CONCEPT, concept, desc=f"Evaluating concept {concept}") as sub_context:
|
||||
if eval_body:
|
||||
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
@@ -12,7 +12,7 @@ from core.tokenizer import Tokenizer, TokenKind
|
||||
PROPERTIES_FOR_DIGEST = ("name", "key",
|
||||
"definition", "definition_type",
|
||||
"is_builtin", "is_unique",
|
||||
"where", "pre", "post", "body",
|
||||
"where", "pre", "post", "body", "ret",
|
||||
"desc", "props", "variables")
|
||||
PROPERTIES_TO_SERIALIZE = PROPERTIES_FOR_DIGEST + tuple(["id"])
|
||||
PROPERTIES_FOR_NEW = ("where", "pre", "post", "body", "desc")
|
||||
@@ -40,6 +40,7 @@ class ConceptParts(Enum):
|
||||
PRE = "pre"
|
||||
POST = "post"
|
||||
BODY = "body"
|
||||
RET = "ret"
|
||||
|
||||
@staticmethod
|
||||
def get_parts():
|
||||
@@ -56,6 +57,7 @@ class ConceptMetadata:
|
||||
where: str # condition to recognize variables in name
|
||||
pre: str # list of pre conditions before calling the main function
|
||||
post: str # list of post conditions after calling the main function
|
||||
ret: str # variable to return when a concept is recognized
|
||||
definition: str # regex used to define the concept
|
||||
definition_type: str # definition can be done with something else than regex
|
||||
desc: str # possible description for the concept
|
||||
@@ -82,6 +84,7 @@ class Concept:
|
||||
where=None,
|
||||
pre=None,
|
||||
post=None,
|
||||
ret=None,
|
||||
definition=None,
|
||||
definition_type=None,
|
||||
desc=None,
|
||||
@@ -98,6 +101,7 @@ class Concept:
|
||||
where,
|
||||
pre,
|
||||
post,
|
||||
ret,
|
||||
definition,
|
||||
definition_type,
|
||||
desc,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# ############################
|
||||
# from github: nealtodd/decorator.py
|
||||
# from github gist: nealtodd/decorator.py (https://gist.github.com/nealtodd/2489618)
|
||||
# ############################
|
||||
|
||||
import pstats
|
||||
@@ -8,7 +8,7 @@ from cProfile import Profile
|
||||
|
||||
def profile(sort_args=None, print_args=None):
|
||||
sort_args = sort_args or ['cumulative']
|
||||
print_args = print_args or [10]
|
||||
print_args = print_args or [20]
|
||||
profiler = Profile()
|
||||
|
||||
def decorator(fn):
|
||||
|
||||
@@ -365,6 +365,7 @@ class Sheerka(Concept):
|
||||
self.sdp.reset()
|
||||
self.locals = {}
|
||||
|
||||
# @profile()
|
||||
def evaluate_user_input(self, text: str, user_name="kodjo"):
|
||||
"""
|
||||
Note to KSI: If you try to add execution context to this function,
|
||||
@@ -676,7 +677,7 @@ class Sheerka(Concept):
|
||||
return self.new(BuiltinConcepts.UNKNOWN_PROPERTY, body=k, concept=concept)
|
||||
|
||||
# TODO : add the concept to the list of known concepts (self.instances)
|
||||
concept.metadata.is_evaluated = True
|
||||
concept.metadata.is_evaluated = True # because we have manually set the variables
|
||||
return concept
|
||||
|
||||
def ret(self, who: str, status: bool, value, message=None, parents=None):
|
||||
|
||||
@@ -22,7 +22,7 @@ class SheerkaDump(BaseService):
|
||||
|
||||
def initialize(self):
|
||||
self.sheerka.bind_service_method(self.dump_desc, "desc")
|
||||
self.sheerka.bind_service_method(self.dump_state, "state")
|
||||
self.sheerka.bind_service_method(self.dump_sdp, "dump_sdp")
|
||||
|
||||
def dump_desc(self, *concept_names, eval=False):
|
||||
first = True
|
||||
@@ -49,15 +49,22 @@ class SheerkaDump(BaseService):
|
||||
self.sheerka.log.info("")
|
||||
self.sheerka.log.info(f"name : {c.name}")
|
||||
self.sheerka.log.info(f"key : {c.key}")
|
||||
self.sheerka.log.info(f"bnf : {c.metadata.definition}")
|
||||
self.sheerka.log.info(f"definition : {c.metadata.definition}")
|
||||
self.sheerka.log.info(f"type : {c.metadata.definition_type}")
|
||||
self.sheerka.log.info(f"body : {c.metadata.body}")
|
||||
self.sheerka.log.info(f"where : {c.metadata.where}")
|
||||
self.sheerka.log.info(f"pre : {c.metadata.pre}")
|
||||
self.sheerka.log.info(f"post : {c.metadata.post}")
|
||||
self.sheerka.log.info(f"ret : {c.metadata.ret}")
|
||||
self.sheerka.log.info(f"vars : {c.metadata.variables}")
|
||||
self.sheerka.log.info(f"props : {c.metadata.props}")
|
||||
if eval:
|
||||
self.sheerka.log.info(f"value : {value}")
|
||||
if c.values:
|
||||
for v in c.values:
|
||||
self.sheerka.log.info(f"{v}: {c.get_value(v)}")
|
||||
else:
|
||||
self.sheerka.log.info("No property")
|
||||
self.sheerka.log.info("No variable")
|
||||
|
||||
self.sheerka.log.info(f"digest : {c.get_origin()}")
|
||||
|
||||
@@ -67,30 +74,7 @@ class SheerkaDump(BaseService):
|
||||
|
||||
first = False
|
||||
|
||||
# def dump_history(self, page=20, start=0):
|
||||
# count = 0
|
||||
# resolved_page = page if page > 0 else 50
|
||||
# page_count = 0
|
||||
#
|
||||
# while count < page if page > 0 else True:
|
||||
# history = self.sheerka.history(resolved_page, start + page_count * resolved_page)
|
||||
# try:
|
||||
# h = next(history)
|
||||
# except StopIteration:
|
||||
# break
|
||||
#
|
||||
# while True:
|
||||
# try:
|
||||
# if h.result:
|
||||
# self.sheerka.log.info(h)
|
||||
# count += 1
|
||||
# h = next(history)
|
||||
# except StopIteration:
|
||||
# break
|
||||
#
|
||||
# page_count += 1
|
||||
|
||||
def dump_state(self):
|
||||
def dump_sdp(self):
|
||||
snapshot = self.sheerka.sdp.get_snapshot(SheerkaDataProvider.HeadFile)
|
||||
state = self.sheerka.sdp.load_state(snapshot)
|
||||
self.sheerka.log.info(get_pp().pformat(state.data))
|
||||
|
||||
@@ -145,10 +145,12 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
if self.sheerka.has_id(concept.id):
|
||||
self.sheerka.get_by_id(concept.id).compiled = concept.compiled
|
||||
|
||||
def resolve(self, context, to_resolve, current_prop, current_concept, force_evaluation):
|
||||
def resolve(self, context, to_resolve, current_prop, current_concept, force_evaluation, expect_success):
|
||||
|
||||
def get_path(context_, prop_name):
|
||||
prefix = context_.path if hasattr(context_, "path") else "<N/A>"
|
||||
concept_name = f'"{context_.action_context.name}"' if isinstance(context_.action_context, Concept) \
|
||||
else "'N/A'"
|
||||
prefix = context_.path if hasattr(context_, "path") else concept_name
|
||||
value = prop_name.name if isinstance(current_prop, ConceptParts) else prop_name
|
||||
return prefix + "." + value
|
||||
|
||||
@@ -178,6 +180,9 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
if force_evaluation:
|
||||
sub_context.local_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
|
||||
|
||||
if expect_success:
|
||||
sub_context.local_hints.add(BuiltinConcepts.EVAL_SUCCESS_REQUESTED)
|
||||
|
||||
# when it's a concept, evaluate it
|
||||
if isinstance(to_resolve, Concept) and \
|
||||
not context.sheerka.isinstance(to_resolve, BuiltinConcepts.RETURN_VALUE):
|
||||
@@ -206,7 +211,7 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
concept=current_concept,
|
||||
property_name=current_prop)
|
||||
|
||||
def resolve_list(self, context, list_to_resolve, current_prop, current_concept, force_evaluation):
|
||||
def resolve_list(self, context, list_to_resolve, current_prop, current_concept, force_evaluation, expect_success):
|
||||
"""When dealing with a list, there are two possibilities"""
|
||||
# It may be a list of ReturnValueConcept to execute (always the case for metadata)
|
||||
# or a list of single values (may be the case for properties)
|
||||
@@ -215,7 +220,12 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
return []
|
||||
|
||||
if self.sheerka.isinstance(list_to_resolve[0], BuiltinConcepts.RETURN_VALUE):
|
||||
return self.resolve(context, list_to_resolve, current_prop, current_concept, force_evaluation)
|
||||
return self.resolve(context,
|
||||
list_to_resolve,
|
||||
current_prop,
|
||||
current_concept,
|
||||
force_evaluation,
|
||||
expect_success)
|
||||
|
||||
res = []
|
||||
for to_resolve in list_to_resolve:
|
||||
@@ -226,7 +236,7 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
concept=current_concept,
|
||||
property_name=current_prop)
|
||||
|
||||
r = self.resolve(context, to_resolve, current_prop, current_concept, force_evaluation)
|
||||
r = self.resolve(context, to_resolve, current_prop, current_concept, force_evaluation, expect_success)
|
||||
if self.sheerka.isinstance(r, BuiltinConcepts.CONCEPT_EVAL_ERROR):
|
||||
return r
|
||||
res.append(r)
|
||||
@@ -258,10 +268,10 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
|
||||
if isinstance(prop_ast, list):
|
||||
# Do not send the current concept for the properties
|
||||
resolved = self.resolve_list(context, prop_ast, var_name, None, True)
|
||||
resolved = self.resolve_list(context, prop_ast, var_name, None, True, False)
|
||||
else:
|
||||
# Do not send the current concept for the properties
|
||||
resolved = self.resolve(context, prop_ast, var_name, None, True)
|
||||
resolved = self.resolve(context, prop_ast, var_name, None, True, False)
|
||||
|
||||
if isinstance(resolved, Concept) and not context.sheerka.is_success(resolved):
|
||||
resolved.set_value("concept", concept) # since current concept was not sent
|
||||
@@ -278,10 +288,20 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
|
||||
if part_key in concept.compiled and concept.compiled[part_key] is not None:
|
||||
metadata_ast = concept.compiled[part_key]
|
||||
# if part_key is PRE, POST or WHERE, the concepts need to be evaluated
|
||||
# otherwise the predicates cannot be resolved
|
||||
# if part_key is PRE, POST or WHERE, the concept need to be evaluated
|
||||
# if we want the predicates to be resolved => so force_eval = True
|
||||
# otherwise no need to force
|
||||
force_concept_eval = False if part_key == ConceptParts.BODY else True
|
||||
resolved = self.resolve(context, metadata_ast, part_key, concept, force_concept_eval)
|
||||
|
||||
# when resolving predicate (where or pre), we need to make sure that PythonEvaluator
|
||||
# will try every possibilities before returning False
|
||||
expect_success = part_key in (ConceptParts.WHERE, ConceptParts.PRE)
|
||||
resolved = self.resolve(context,
|
||||
metadata_ast,
|
||||
part_key,
|
||||
concept,
|
||||
force_concept_eval,
|
||||
expect_success)
|
||||
if isinstance(resolved, Concept) and not context.sheerka.is_success(resolved):
|
||||
return resolved
|
||||
else:
|
||||
@@ -312,9 +332,9 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
|
||||
def choose_metadata_to_eval(self, context, concept):
|
||||
if context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED):
|
||||
return ["pre", "post", "variables", "body", "where"]
|
||||
return ["pre", "post", "variables", "body", "where", "ret"]
|
||||
|
||||
metadata = ["pre", "post"]
|
||||
metadata = ["pre", "post", "ret"]
|
||||
if context.in_context(BuiltinConcepts.EVAL_WHERE_REQUESTED) or concept.metadata.need_validation:
|
||||
needed = self.needed_metadata(concept, ConceptParts.WHERE)
|
||||
for e in needed:
|
||||
|
||||
@@ -326,13 +326,13 @@ class SheerkaExecute(BaseService):
|
||||
result = evaluator.eval(sub_context, item)
|
||||
if result is None:
|
||||
# match() was successful but nothing was done in eval
|
||||
# most of the time, it's because checks made in eval were unsuccessful
|
||||
# most of the time, it's because extra checks are unsuccessful
|
||||
debug_result.append({"input": item, "return_value": None})
|
||||
continue
|
||||
|
||||
if id(result) == id(item):
|
||||
# eval was successful, but we don't want to alter the processing flow
|
||||
debug_result.append({"input": item, "return_value": item})
|
||||
debug_result.append({"input": item, "return_value": result})
|
||||
continue
|
||||
|
||||
# otherwise, item will be removed and replaced by result
|
||||
|
||||
@@ -128,6 +128,7 @@ class Keywords(Enum):
|
||||
PRE = "pre"
|
||||
POST = "post"
|
||||
ISA = "isa"
|
||||
RET = "ret"
|
||||
|
||||
|
||||
class Tokenizer:
|
||||
|
||||
@@ -59,7 +59,7 @@ class AddConceptEvaluator(OneReturnValueEvaluator):
|
||||
concept.metadata.definition_type = def_concept_node.definition_type
|
||||
name_to_use = self.get_name_to_use(def_concept_node)
|
||||
|
||||
for prop in ("definition", "where", "pre", "post", "body"):
|
||||
for prop in ("definition", "where", "pre", "post", "body", "ret"):
|
||||
|
||||
part_ret_val = getattr(def_concept_node, prop)
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ class PythonEvaluator(OneReturnValueEvaluator):
|
||||
concepts_entries = None
|
||||
evaluated = BuiltinConcepts.NOT_INITIALIZED
|
||||
errors = []
|
||||
expect_success = BuiltinConcepts.EVAL_SUCCESS_REQUESTED in context.local_hints
|
||||
for globals_ in all_possible_globals:
|
||||
try:
|
||||
# eval
|
||||
@@ -92,6 +93,7 @@ class PythonEvaluator(OneReturnValueEvaluator):
|
||||
context.log("Evaluating using 'exec'.", self.name)
|
||||
evaluated = self.exec_with_return(node.ast_, globals_, sheerka.locals)
|
||||
|
||||
if not expect_success or evaluated:
|
||||
break # in this first version, we stop once a success is found
|
||||
except Exception as ex:
|
||||
if concepts_entries is None:
|
||||
@@ -246,7 +248,7 @@ class PythonEvaluator(OneReturnValueEvaluator):
|
||||
# make the product the rest as cartesian product
|
||||
res = [fixed_values]
|
||||
for k, v in concepts_with_body.items():
|
||||
res = core.utils.dict_product(res, [{k: context.sheerka.objvalue(v)}, {k: v}])
|
||||
res = core.utils.dict_product(res, [{k: v}, {k: context.sheerka.objvalue(v)}])
|
||||
|
||||
return res
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.builtin_helpers import ensure_evaluated
|
||||
from core.concept import Concept, ConceptParts
|
||||
from evaluators.BaseEvaluator import OneReturnValueEvaluator
|
||||
|
||||
|
||||
class RetEvaluator(OneReturnValueEvaluator):
|
||||
"""
|
||||
The evaluator transform the a concept, using the ret value
|
||||
"""
|
||||
|
||||
NAME = "Ret"
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(self.NAME, [BuiltinConcepts.EVALUATION], 10)
|
||||
|
||||
def matches(self, context, return_value):
|
||||
return return_value.status and \
|
||||
isinstance(return_value.value, Concept) and \
|
||||
return_value.value.metadata.ret is not None
|
||||
|
||||
def eval(self, context, return_value):
|
||||
sheerka = context.sheerka
|
||||
concept = return_value.value
|
||||
context.log(f"Processing ret value for concept {concept}.", self.name)
|
||||
|
||||
if not concept.metadata.is_evaluated:
|
||||
evaluated = ensure_evaluated(context, concept)
|
||||
if evaluated.key != concept.key:
|
||||
context.log(f"Failed to evaluate concept '{concept}'")
|
||||
return None
|
||||
ret = evaluated.get_value(ConceptParts.RET)
|
||||
else:
|
||||
ret = concept.get_value(ConceptParts.RET)
|
||||
|
||||
if isinstance(ret, Concept) and sheerka.is_known(ret):
|
||||
return sheerka.ret(self.name, True, ret, parents=[return_value])
|
||||
|
||||
context.log(f"ret '{ret}' is not a concept!")
|
||||
return None
|
||||
@@ -84,6 +84,7 @@ class DefConceptNode(DefaultParserNode):
|
||||
pre: ReturnValueConcept = NotInitializedNode()
|
||||
post: ReturnValueConcept = NotInitializedNode()
|
||||
body: ReturnValueConcept = NotInitializedNode()
|
||||
ret: ReturnValueConcept = NotInitializedNode()
|
||||
definition: ReturnValueConcept = NotInitializedNode()
|
||||
definition_type: str = None
|
||||
|
||||
@@ -245,6 +246,7 @@ class DefaultParser(BaseParser):
|
||||
concept_found.pre = asts_found_by_parts[Keywords.PRE]
|
||||
concept_found.post = asts_found_by_parts[Keywords.POST]
|
||||
concept_found.body = asts_found_by_parts[Keywords.AS]
|
||||
concept_found.ret = asts_found_by_parts[Keywords.RET]
|
||||
|
||||
return concept_found
|
||||
|
||||
@@ -279,7 +281,13 @@ class DefaultParser(BaseParser):
|
||||
|
||||
def regroup_tokens_by_parts(self, keywords_tokens):
|
||||
|
||||
def_concept_parts = [Keywords.CONCEPT, Keywords.FROM, Keywords.AS, Keywords.WHERE, Keywords.PRE, Keywords.POST]
|
||||
def_concept_parts = [Keywords.CONCEPT,
|
||||
Keywords.FROM,
|
||||
Keywords.AS,
|
||||
Keywords.WHERE,
|
||||
Keywords.PRE,
|
||||
Keywords.POST,
|
||||
Keywords.RET]
|
||||
|
||||
# tokens found, when trying to recognize the parts
|
||||
tokens_found_by_parts = {
|
||||
@@ -289,6 +297,7 @@ class DefaultParser(BaseParser):
|
||||
Keywords.WHERE: None,
|
||||
Keywords.PRE: None,
|
||||
Keywords.POST: None,
|
||||
Keywords.RET: None,
|
||||
}
|
||||
current_part = Keywords.CONCEPT
|
||||
token = self.parser_input.token
|
||||
@@ -386,6 +395,7 @@ class DefaultParser(BaseParser):
|
||||
Keywords.WHERE: NotInitializedNode(),
|
||||
Keywords.PRE: NotInitializedNode(),
|
||||
Keywords.POST: NotInitializedNode(),
|
||||
Keywords.RET: NotInitializedNode()
|
||||
}
|
||||
|
||||
for keyword in tokens_found_by_parts:
|
||||
|
||||
@@ -67,7 +67,7 @@ class UnrecognizedNodeParser(BaseParser):
|
||||
has_unrecognized = True # never trust source code not. I may be an invalid source code
|
||||
|
||||
else: # cannot happen as of today :-)
|
||||
raise NotImplementedError()
|
||||
raise NotImplementedError(f"Node is {type(node)}, which is not supported yet")
|
||||
|
||||
# concept with UnrecognizedToken in their properties is considered as fatal error
|
||||
if self.has_error:
|
||||
|
||||
@@ -130,8 +130,15 @@ class SheerkaPickler:
|
||||
return None
|
||||
|
||||
def exist(self, obj):
|
||||
for k, v in self.ids.items():
|
||||
if k == id(obj):
|
||||
try:
|
||||
v = self.ids[id(obj)]
|
||||
return True, v
|
||||
|
||||
except KeyError:
|
||||
return False, None
|
||||
|
||||
# def exist(self, obj):
|
||||
# for k, v in self.ids.items():
|
||||
# if k == id(obj):
|
||||
# return True, v
|
||||
#
|
||||
# return False, None
|
||||
|
||||
@@ -69,6 +69,7 @@ def test_i_can_serialize():
|
||||
where="definition of the where",
|
||||
pre="definition of the pre",
|
||||
post="definition of the post",
|
||||
ret="concept to return",
|
||||
definition="bnf definition",
|
||||
definition_type="def type",
|
||||
desc="this this the desc",
|
||||
@@ -88,6 +89,7 @@ def test_i_can_serialize():
|
||||
'name': 'concept_name',
|
||||
'post': 'definition of the post',
|
||||
'pre': 'definition of the pre',
|
||||
'ret': "concept to return",
|
||||
'props': {},
|
||||
'variables': [('a', "10"), ('b', None)],
|
||||
'where': 'definition of the where'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import ast
|
||||
import pytest
|
||||
|
||||
import pytest
|
||||
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
|
||||
from core.concept import VARIABLE_PREFIX, Concept, DEFINITION_TYPE_BNF, DEFINITION_TYPE_DEF
|
||||
from core.tokenizer import Tokenizer
|
||||
@@ -52,7 +52,8 @@ class TestAddConceptEvaluator(TestUsingMemoryBasedSheerka):
|
||||
)
|
||||
)
|
||||
|
||||
def get_def_concept(self, name, where=None, pre=None, post=None, body=None, definition=None, bnf_def=None):
|
||||
def get_def_concept(self, name, where=None, pre=None, post=None, body=None, ret=None,
|
||||
definition=None, bnf_def=None):
|
||||
def_concept = DefConceptNode([], name=NameNode(list(Tokenizer(name))))
|
||||
|
||||
if body:
|
||||
@@ -63,6 +64,8 @@ class TestAddConceptEvaluator(TestUsingMemoryBasedSheerka):
|
||||
def_concept.pre = self.get_concept_part(pre)
|
||||
if post:
|
||||
def_concept.post = self.get_concept_part(post)
|
||||
if ret:
|
||||
def_concept.ret = self.get_concept_part(ret)
|
||||
if bnf_def:
|
||||
def_concept.definition = bnf_def
|
||||
def_concept.definition_type = DEFINITION_TYPE_BNF
|
||||
@@ -91,7 +94,8 @@ class TestAddConceptEvaluator(TestUsingMemoryBasedSheerka):
|
||||
bnf_def=self.get_return_value("hello a", Sequence(StrMatch("hello"), StrMatch("a"))),
|
||||
where="isinstance(a, str )",
|
||||
pre="a is not None",
|
||||
body="print('hello' + a)")
|
||||
body="print('hello' + a)",
|
||||
ret="a")
|
||||
|
||||
evaluated = AddConceptEvaluator().eval(context, def_concept_return_value)
|
||||
|
||||
@@ -103,8 +107,9 @@ class TestAddConceptEvaluator(TestUsingMemoryBasedSheerka):
|
||||
assert created_concept.metadata.key == "hello __var__0"
|
||||
assert created_concept.metadata.where == "isinstance(a, str )"
|
||||
assert created_concept.metadata.pre == "a is not None"
|
||||
assert created_concept.metadata.post is None
|
||||
assert created_concept.metadata.post is None # test that NotInitialized is mapped into None
|
||||
assert created_concept.metadata.body == "print('hello' + a)"
|
||||
assert created_concept.metadata.ret == "a"
|
||||
assert created_concept.metadata.definition == "hello a"
|
||||
assert created_concept.metadata.definition_type == "bnf"
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ class TestPythonEvaluator(TestUsingMemoryBasedSheerka):
|
||||
evaluated = PythonEvaluator().eval(context, parsed)
|
||||
|
||||
assert evaluated.status
|
||||
assert evaluated.value == 2
|
||||
assert evaluated.value == CB("foo", 2)
|
||||
|
||||
def test_i_can_eval_concept_token(self):
|
||||
context = self.get_context()
|
||||
@@ -126,13 +126,35 @@ class TestPythonEvaluator(TestUsingMemoryBasedSheerka):
|
||||
assert evaluated.status
|
||||
assert evaluated.value == "foo"
|
||||
|
||||
def test_i_can_eval_when_expect_success(self):
|
||||
context = self.get_context()
|
||||
context.sheerka.add_in_cache(Concept("foo", body="2"))
|
||||
|
||||
parsed = PythonParser().parse(context, ParserInput("foo==2"))
|
||||
python_evaluator = PythonEvaluator()
|
||||
|
||||
evaluated = python_evaluator.eval(context, parsed)
|
||||
assert evaluated.status
|
||||
assert not evaluated.value # the first test is between Concept(foo) and int(2)
|
||||
|
||||
context.local_hints.add(BuiltinConcepts.EVAL_SUCCESS_REQUESTED)
|
||||
evaluated = python_evaluator.eval(context, parsed)
|
||||
assert evaluated.status
|
||||
assert evaluated.value # we test until we compare foo.body and 2
|
||||
|
||||
parsed = PythonParser().parse(context, ParserInput("foo==3"))
|
||||
evaluated = python_evaluator.eval(context, parsed)
|
||||
assert evaluated.status
|
||||
assert not evaluated.value # neither foo or foo.body ==3
|
||||
|
||||
def test_i_can_call_function_with_complex_concepts(self):
|
||||
sheerka, context, plus, mult = self.init_concepts(
|
||||
self.from_def_concept("plus", "a plus b", ["a", "b"]),
|
||||
self.from_def_concept("mult", "a mult b", ["a", "b"]),
|
||||
)
|
||||
|
||||
parsed = PythonParser().parse(context, ParserInput("set_is_greater_than(BuiltinConcepts.PRECEDENCE, mult, plus)"))
|
||||
parsed = PythonParser().parse(context,
|
||||
ParserInput("set_is_greater_than(BuiltinConcepts.PRECEDENCE, mult, plus)"))
|
||||
python_evaluator = PythonEvaluator()
|
||||
|
||||
evaluated = python_evaluator.eval(context, parsed)
|
||||
@@ -164,8 +186,8 @@ class TestPythonEvaluator(TestUsingMemoryBasedSheerka):
|
||||
|
||||
all_globals = python_evaluator.get_all_possible_globals(context, my_globals)
|
||||
assert len(all_globals) == 2
|
||||
assert all_globals[0]["foo"] == 'foo'
|
||||
assert all_globals[1]["foo"] == CB(foo, "foo") # body is evaluated
|
||||
assert all_globals[0]["foo"] == CB(foo, "foo")
|
||||
assert all_globals[1]["foo"] == 'foo' # body is evaluated
|
||||
|
||||
def test_i_can_detect_one_error(self):
|
||||
sheerka, context, foo = self.init_concepts("foo")
|
||||
@@ -194,11 +216,11 @@ class TestPythonEvaluator(TestUsingMemoryBasedSheerka):
|
||||
error0 = evaluated.body.body[0]
|
||||
assert isinstance(error0, PythonEvalError)
|
||||
assert isinstance(error0.error, TypeError)
|
||||
assert error0.error.args[0] == 'can only concatenate str (not "int") to str'
|
||||
assert error0.concepts == {'foo': 'string'}
|
||||
assert error0.error.args[0] == "unsupported operand type(s) for +: 'Concept' and 'int'"
|
||||
assert error0.concepts == {'foo': CB(foo, 'string')}
|
||||
|
||||
error1 = evaluated.body.body[1]
|
||||
assert isinstance(error1, PythonEvalError)
|
||||
assert isinstance(error1.error, TypeError)
|
||||
assert error1.error.args[0] == "unsupported operand type(s) for +: 'Concept' and 'int'"
|
||||
assert error1.concepts == {'foo': CB(foo, 'string')}
|
||||
assert error1.error.args[0] == 'can only concatenate str (not "int") to str'
|
||||
assert error1.concepts == {'foo': 'string'}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import pytest
|
||||
from core.builtin_concepts import ReturnValueConcept
|
||||
from core.concept import Concept
|
||||
from evaluators.RetEvaluator import RetEvaluator
|
||||
|
||||
from tests.BaseTest import BaseTest
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
class TestRetEvaluator(TestUsingMemoryBasedSheerka):
|
||||
|
||||
@pytest.mark.parametrize("ret_val, expected", [
|
||||
(ReturnValueConcept("who", True, Concept("foo", ret="bar")), True),
|
||||
(ReturnValueConcept("who", False, Concept("foo", ret="bar")), False),
|
||||
(ReturnValueConcept("who", True, Concept("foo")), False),
|
||||
(BaseTest.pretval(Concept("foo", ret="bar")), False),
|
||||
(ReturnValueConcept("who", True, "not even a concept"), False),
|
||||
])
|
||||
def test_i_can_match(self, ret_val, expected):
|
||||
context = self.get_context()
|
||||
assert RetEvaluator().matches(context, ret_val) == expected
|
||||
|
||||
def test_i_can_evaluate_fully_initialized(self):
|
||||
sheerka, context, foo, the_concept = self.init_concepts("foo", Concept("the concept", ret="a").def_var("a"))
|
||||
|
||||
instance = sheerka.new("the concept")
|
||||
instance.set_value("a", sheerka.new("foo"))
|
||||
ret_value = self.tretval(sheerka, instance)
|
||||
|
||||
res = RetEvaluator().eval(context, ret_value)
|
||||
assert res.status
|
||||
assert sheerka.isinstance(res.body, "foo")
|
||||
|
||||
@pytest.mark.parametrize("instance, expected", [
|
||||
(Concept("with ret", ret="a").def_var("a", "foo"), "foo"),
|
||||
(Concept("with ret", ret="a", body="10").def_var("a", "foo"), "foo"),
|
||||
(Concept("with ret", ret="a").def_var("a", "bar"), "bar"),
|
||||
])
|
||||
def test_i_can_evaluate(self, instance, expected):
|
||||
sheerka, context, foo, bar = self.init_concepts("foo", Concept("bar", body="10"))
|
||||
|
||||
ret_value = self.tretval(sheerka, instance)
|
||||
|
||||
res = RetEvaluator().eval(context, ret_value)
|
||||
assert res.status
|
||||
assert sheerka.isinstance(res.body, expected)
|
||||
@@ -929,12 +929,12 @@ as:
|
||||
error0 = res[0].body.body.body[0]
|
||||
assert isinstance(error0, PythonEvalError)
|
||||
assert isinstance(error0.error, TypeError)
|
||||
assert error0.error.args[0] == 'can only concatenate str (not "int") to str'
|
||||
assert error0.error.args[0] == "unsupported operand type(s) for +: 'Concept' and 'int'"
|
||||
|
||||
error1 = res[0].body.body.body[1]
|
||||
assert isinstance(error1, PythonEvalError)
|
||||
assert isinstance(error1.error, TypeError)
|
||||
assert error1.error.args[0] == "unsupported operand type(s) for +: 'Concept' and 'int'"
|
||||
assert error1.error.args[0] == 'can only concatenate str (not "int") to str'
|
||||
|
||||
def test_i_can_evaluate_bnf_concept_defined_with_group_after_restart(self):
|
||||
"""
|
||||
|
||||
@@ -15,7 +15,7 @@ from parsers.PythonParser import PythonParser, PythonNode
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
def get_def_concept(name, where=None, pre=None, post=None, body=None, definition=None, bnf_def=None):
|
||||
def get_def_concept(name, where=None, pre=None, post=None, body=None, definition=None, bnf_def=None, ret=None):
|
||||
def_concept = DefConceptNode([], name=NameNode(list(Tokenizer(name))))
|
||||
|
||||
if body:
|
||||
@@ -26,6 +26,8 @@ def get_def_concept(name, where=None, pre=None, post=None, body=None, definition
|
||||
def_concept.pre = get_concept_part(pre)
|
||||
if post:
|
||||
def_concept.post = get_concept_part(post)
|
||||
if ret:
|
||||
def_concept.ret = get_concept_part(ret)
|
||||
if bnf_def:
|
||||
def_concept.definition = ReturnValueConcept(
|
||||
"parsers.Bnf",
|
||||
@@ -75,6 +77,9 @@ def get_concept_part(part):
|
||||
|
||||
@dataclass
|
||||
class PN:
|
||||
"""
|
||||
Python Node
|
||||
"""
|
||||
source: str # parser result source
|
||||
mode: str # compilation mode
|
||||
|
||||
@@ -110,21 +115,23 @@ class TestDefaultParser(TestUsingMemoryBasedSheerka):
|
||||
assert node == expected
|
||||
|
||||
def test_i_can_parse_complex_def_concept_statement(self):
|
||||
text = """def concept a plus b
|
||||
text = """def concept a mult b
|
||||
where a,b
|
||||
pre isinstance(a, int) and isinstance(b, float)
|
||||
post isinstance(res, int)
|
||||
as res = a + b
|
||||
pre isinstance(b, int)
|
||||
post isinstance(res, a)
|
||||
as res = a * b
|
||||
ret a if isinstance(a, Concept) else self
|
||||
"""
|
||||
sheerka, context, parser = self.init_parser()
|
||||
res = parser.parse(context, ParserInput(text))
|
||||
return_value = res.value
|
||||
expected_concept = get_def_concept(
|
||||
name="a plus b",
|
||||
name="a mult b",
|
||||
where="a,b\n",
|
||||
pre="isinstance(a, int) and isinstance(b, float)\n",
|
||||
post="isinstance(res, int)\n",
|
||||
body=PN("res = a + b\n ", "exec")
|
||||
pre="isinstance(b, int)\n",
|
||||
post="isinstance(res, a)\n",
|
||||
body=PN("res = a * b\n", "exec"),
|
||||
ret="a if isinstance(a, Concept) else self\n"
|
||||
)
|
||||
|
||||
assert res.status
|
||||
|
||||
Reference in New Issue
Block a user