Added RET keyword

This commit is contained in:
2020-07-07 11:34:40 +02:00
parent 56e1cb4587
commit c4399d631c
21 changed files with 245 additions and 87 deletions
+2
View File
@@ -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"
+7 -2
View File
@@ -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
View File
@@ -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,
+2 -2
View File
@@ -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):
+2 -1
View File
@@ -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):
+11 -27
View File
@@ -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:
+2 -2
View File
@@ -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
+1
View File
@@ -128,6 +128,7 @@ class Keywords(Enum):
PRE = "pre"
POST = "post"
ISA = "isa"
RET = "ret"
class Tokenizer:
+1 -1
View File
@@ -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)
+3 -1
View File
@@ -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
+40
View File
@@ -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
+11 -1
View File
@@ -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:
+1 -1
View File
@@ -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:
+10 -3
View File
@@ -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
+2
View File
@@ -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'
+9 -4
View File
@@ -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"
+30 -8
View File
@@ -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'}
+46
View File
@@ -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)
+2 -2
View File
@@ -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):
"""
+17 -10
View File
@@ -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