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
|
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
|
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
|
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
|
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
|
WHERE_CLAUSE_FAILED = "where clause failed" # failed to validate where clause during evaluation
|
||||||
CHICKEN_AND_EGG = "chicken and egg" # infinite recursion when declaring concept
|
CHICKEN_AND_EGG = "chicken and egg" # infinite recursion when declaring concept
|
||||||
@@ -78,6 +79,7 @@ class BuiltinConcepts(Enum):
|
|||||||
NOT_INITIALIZED = "not initialized"
|
NOT_INITIALIZED = "not initialized"
|
||||||
NOT_FOUND = "not found" # when the wanted resource is not found
|
NOT_FOUND = "not found" # when the wanted resource is not found
|
||||||
FORMAT_INSTRUCTIONS = "format instructions" # to express how to print the concept
|
FORMAT_INSTRUCTIONS = "format instructions" # to express how to print the concept
|
||||||
|
NOT_IMPLEMENTED = "not implemented" # instead of raise an error
|
||||||
|
|
||||||
NODE = "node"
|
NODE = "node"
|
||||||
GENERIC_NODE = "generic node"
|
GENERIC_NODE = "generic node"
|
||||||
|
|||||||
@@ -314,11 +314,12 @@ def get_lexer_nodes(return_values, start, tokens):
|
|||||||
return lexer_nodes
|
return lexer_nodes
|
||||||
|
|
||||||
|
|
||||||
def ensure_evaluated(context, concept):
|
def ensure_evaluated(context, concept, eval_body=True):
|
||||||
"""
|
"""
|
||||||
Evaluate a concept is not already evaluated
|
Evaluate a concept is not already evaluated
|
||||||
:param context:
|
:param context:
|
||||||
:param concept:
|
:param concept:
|
||||||
|
:param eval_body:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if concept.metadata.is_evaluated:
|
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
|
# do not try to evaluate concept that are not fully initialized
|
||||||
for var in concept.metadata.variables:
|
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
|
return concept
|
||||||
|
|
||||||
with context.push(BuiltinConcepts.EVALUATE_CONCEPT, concept, desc=f"Evaluating concept {concept}") as sub_context:
|
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)
|
sub_context.local_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
|
||||||
evaluated = context.sheerka.evaluate_concept(sub_context, concept)
|
evaluated = context.sheerka.evaluate_concept(sub_context, concept)
|
||||||
sub_context.add_values(return_values=evaluated)
|
sub_context.add_values(return_values=evaluated)
|
||||||
|
|||||||
+5
-1
@@ -12,7 +12,7 @@ from core.tokenizer import Tokenizer, TokenKind
|
|||||||
PROPERTIES_FOR_DIGEST = ("name", "key",
|
PROPERTIES_FOR_DIGEST = ("name", "key",
|
||||||
"definition", "definition_type",
|
"definition", "definition_type",
|
||||||
"is_builtin", "is_unique",
|
"is_builtin", "is_unique",
|
||||||
"where", "pre", "post", "body",
|
"where", "pre", "post", "body", "ret",
|
||||||
"desc", "props", "variables")
|
"desc", "props", "variables")
|
||||||
PROPERTIES_TO_SERIALIZE = PROPERTIES_FOR_DIGEST + tuple(["id"])
|
PROPERTIES_TO_SERIALIZE = PROPERTIES_FOR_DIGEST + tuple(["id"])
|
||||||
PROPERTIES_FOR_NEW = ("where", "pre", "post", "body", "desc")
|
PROPERTIES_FOR_NEW = ("where", "pre", "post", "body", "desc")
|
||||||
@@ -40,6 +40,7 @@ class ConceptParts(Enum):
|
|||||||
PRE = "pre"
|
PRE = "pre"
|
||||||
POST = "post"
|
POST = "post"
|
||||||
BODY = "body"
|
BODY = "body"
|
||||||
|
RET = "ret"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_parts():
|
def get_parts():
|
||||||
@@ -56,6 +57,7 @@ class ConceptMetadata:
|
|||||||
where: str # condition to recognize variables in name
|
where: str # condition to recognize variables in name
|
||||||
pre: str # list of pre conditions before calling the main function
|
pre: str # list of pre conditions before calling the main function
|
||||||
post: str # list of post conditions after 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: str # regex used to define the concept
|
||||||
definition_type: str # definition can be done with something else than regex
|
definition_type: str # definition can be done with something else than regex
|
||||||
desc: str # possible description for the concept
|
desc: str # possible description for the concept
|
||||||
@@ -82,6 +84,7 @@ class Concept:
|
|||||||
where=None,
|
where=None,
|
||||||
pre=None,
|
pre=None,
|
||||||
post=None,
|
post=None,
|
||||||
|
ret=None,
|
||||||
definition=None,
|
definition=None,
|
||||||
definition_type=None,
|
definition_type=None,
|
||||||
desc=None,
|
desc=None,
|
||||||
@@ -98,6 +101,7 @@ class Concept:
|
|||||||
where,
|
where,
|
||||||
pre,
|
pre,
|
||||||
post,
|
post,
|
||||||
|
ret,
|
||||||
definition,
|
definition,
|
||||||
definition_type,
|
definition_type,
|
||||||
desc,
|
desc,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# ############################
|
# ############################
|
||||||
# from github: nealtodd/decorator.py
|
# from github gist: nealtodd/decorator.py (https://gist.github.com/nealtodd/2489618)
|
||||||
# ############################
|
# ############################
|
||||||
|
|
||||||
import pstats
|
import pstats
|
||||||
@@ -8,7 +8,7 @@ from cProfile import Profile
|
|||||||
|
|
||||||
def profile(sort_args=None, print_args=None):
|
def profile(sort_args=None, print_args=None):
|
||||||
sort_args = sort_args or ['cumulative']
|
sort_args = sort_args or ['cumulative']
|
||||||
print_args = print_args or [10]
|
print_args = print_args or [20]
|
||||||
profiler = Profile()
|
profiler = Profile()
|
||||||
|
|
||||||
def decorator(fn):
|
def decorator(fn):
|
||||||
|
|||||||
@@ -365,6 +365,7 @@ class Sheerka(Concept):
|
|||||||
self.sdp.reset()
|
self.sdp.reset()
|
||||||
self.locals = {}
|
self.locals = {}
|
||||||
|
|
||||||
|
# @profile()
|
||||||
def evaluate_user_input(self, text: str, user_name="kodjo"):
|
def evaluate_user_input(self, text: str, user_name="kodjo"):
|
||||||
"""
|
"""
|
||||||
Note to KSI: If you try to add execution context to this function,
|
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)
|
return self.new(BuiltinConcepts.UNKNOWN_PROPERTY, body=k, concept=concept)
|
||||||
|
|
||||||
# TODO : add the concept to the list of known concepts (self.instances)
|
# 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
|
return concept
|
||||||
|
|
||||||
def ret(self, who: str, status: bool, value, message=None, parents=None):
|
def ret(self, who: str, status: bool, value, message=None, parents=None):
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class SheerkaDump(BaseService):
|
|||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
self.sheerka.bind_service_method(self.dump_desc, "desc")
|
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):
|
def dump_desc(self, *concept_names, eval=False):
|
||||||
first = True
|
first = True
|
||||||
@@ -49,15 +49,22 @@ class SheerkaDump(BaseService):
|
|||||||
self.sheerka.log.info("")
|
self.sheerka.log.info("")
|
||||||
self.sheerka.log.info(f"name : {c.name}")
|
self.sheerka.log.info(f"name : {c.name}")
|
||||||
self.sheerka.log.info(f"key : {c.key}")
|
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"body : {c.metadata.body}")
|
||||||
self.sheerka.log.info(f"where : {c.metadata.where}")
|
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:
|
if eval:
|
||||||
self.sheerka.log.info(f"value : {value}")
|
self.sheerka.log.info(f"value : {value}")
|
||||||
|
if c.values:
|
||||||
for v in c.values:
|
for v in c.values:
|
||||||
self.sheerka.log.info(f"{v}: {c.get_value(v)}")
|
self.sheerka.log.info(f"{v}: {c.get_value(v)}")
|
||||||
else:
|
else:
|
||||||
self.sheerka.log.info("No property")
|
self.sheerka.log.info("No variable")
|
||||||
|
|
||||||
self.sheerka.log.info(f"digest : {c.get_origin()}")
|
self.sheerka.log.info(f"digest : {c.get_origin()}")
|
||||||
|
|
||||||
@@ -67,30 +74,7 @@ class SheerkaDump(BaseService):
|
|||||||
|
|
||||||
first = False
|
first = False
|
||||||
|
|
||||||
# def dump_history(self, page=20, start=0):
|
def dump_sdp(self):
|
||||||
# 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):
|
|
||||||
snapshot = self.sheerka.sdp.get_snapshot(SheerkaDataProvider.HeadFile)
|
snapshot = self.sheerka.sdp.get_snapshot(SheerkaDataProvider.HeadFile)
|
||||||
state = self.sheerka.sdp.load_state(snapshot)
|
state = self.sheerka.sdp.load_state(snapshot)
|
||||||
self.sheerka.log.info(get_pp().pformat(state.data))
|
self.sheerka.log.info(get_pp().pformat(state.data))
|
||||||
|
|||||||
@@ -145,10 +145,12 @@ class SheerkaEvaluateConcept(BaseService):
|
|||||||
if self.sheerka.has_id(concept.id):
|
if self.sheerka.has_id(concept.id):
|
||||||
self.sheerka.get_by_id(concept.id).compiled = concept.compiled
|
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):
|
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
|
value = prop_name.name if isinstance(current_prop, ConceptParts) else prop_name
|
||||||
return prefix + "." + value
|
return prefix + "." + value
|
||||||
|
|
||||||
@@ -178,6 +180,9 @@ class SheerkaEvaluateConcept(BaseService):
|
|||||||
if force_evaluation:
|
if force_evaluation:
|
||||||
sub_context.local_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
|
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
|
# when it's a concept, evaluate it
|
||||||
if isinstance(to_resolve, Concept) and \
|
if isinstance(to_resolve, Concept) and \
|
||||||
not context.sheerka.isinstance(to_resolve, BuiltinConcepts.RETURN_VALUE):
|
not context.sheerka.isinstance(to_resolve, BuiltinConcepts.RETURN_VALUE):
|
||||||
@@ -206,7 +211,7 @@ class SheerkaEvaluateConcept(BaseService):
|
|||||||
concept=current_concept,
|
concept=current_concept,
|
||||||
property_name=current_prop)
|
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"""
|
"""When dealing with a list, there are two possibilities"""
|
||||||
# It may be a list of ReturnValueConcept to execute (always the case for metadata)
|
# 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)
|
# or a list of single values (may be the case for properties)
|
||||||
@@ -215,7 +220,12 @@ class SheerkaEvaluateConcept(BaseService):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
if self.sheerka.isinstance(list_to_resolve[0], BuiltinConcepts.RETURN_VALUE):
|
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 = []
|
res = []
|
||||||
for to_resolve in list_to_resolve:
|
for to_resolve in list_to_resolve:
|
||||||
@@ -226,7 +236,7 @@ class SheerkaEvaluateConcept(BaseService):
|
|||||||
concept=current_concept,
|
concept=current_concept,
|
||||||
property_name=current_prop)
|
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):
|
if self.sheerka.isinstance(r, BuiltinConcepts.CONCEPT_EVAL_ERROR):
|
||||||
return r
|
return r
|
||||||
res.append(r)
|
res.append(r)
|
||||||
@@ -258,10 +268,10 @@ class SheerkaEvaluateConcept(BaseService):
|
|||||||
|
|
||||||
if isinstance(prop_ast, list):
|
if isinstance(prop_ast, list):
|
||||||
# Do not send the current concept for the properties
|
# 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:
|
else:
|
||||||
# Do not send the current concept for the properties
|
# 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):
|
if isinstance(resolved, Concept) and not context.sheerka.is_success(resolved):
|
||||||
resolved.set_value("concept", concept) # since current concept was not sent
|
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:
|
if part_key in concept.compiled and concept.compiled[part_key] is not None:
|
||||||
metadata_ast = concept.compiled[part_key]
|
metadata_ast = concept.compiled[part_key]
|
||||||
# if part_key is PRE, POST or WHERE, the concepts need to be evaluated
|
# if part_key is PRE, POST or WHERE, the concept need to be evaluated
|
||||||
# otherwise the predicates cannot be resolved
|
# 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
|
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):
|
if isinstance(resolved, Concept) and not context.sheerka.is_success(resolved):
|
||||||
return resolved
|
return resolved
|
||||||
else:
|
else:
|
||||||
@@ -312,9 +332,9 @@ class SheerkaEvaluateConcept(BaseService):
|
|||||||
|
|
||||||
def choose_metadata_to_eval(self, context, concept):
|
def choose_metadata_to_eval(self, context, concept):
|
||||||
if context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED):
|
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:
|
if context.in_context(BuiltinConcepts.EVAL_WHERE_REQUESTED) or concept.metadata.need_validation:
|
||||||
needed = self.needed_metadata(concept, ConceptParts.WHERE)
|
needed = self.needed_metadata(concept, ConceptParts.WHERE)
|
||||||
for e in needed:
|
for e in needed:
|
||||||
|
|||||||
@@ -326,13 +326,13 @@ class SheerkaExecute(BaseService):
|
|||||||
result = evaluator.eval(sub_context, item)
|
result = evaluator.eval(sub_context, item)
|
||||||
if result is None:
|
if result is None:
|
||||||
# match() was successful but nothing was done in eval
|
# 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})
|
debug_result.append({"input": item, "return_value": None})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if id(result) == id(item):
|
if id(result) == id(item):
|
||||||
# eval was successful, but we don't want to alter the processing flow
|
# 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
|
continue
|
||||||
|
|
||||||
# otherwise, item will be removed and replaced by result
|
# otherwise, item will be removed and replaced by result
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ class Keywords(Enum):
|
|||||||
PRE = "pre"
|
PRE = "pre"
|
||||||
POST = "post"
|
POST = "post"
|
||||||
ISA = "isa"
|
ISA = "isa"
|
||||||
|
RET = "ret"
|
||||||
|
|
||||||
|
|
||||||
class Tokenizer:
|
class Tokenizer:
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class AddConceptEvaluator(OneReturnValueEvaluator):
|
|||||||
concept.metadata.definition_type = def_concept_node.definition_type
|
concept.metadata.definition_type = def_concept_node.definition_type
|
||||||
name_to_use = self.get_name_to_use(def_concept_node)
|
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)
|
part_ret_val = getattr(def_concept_node, prop)
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ class PythonEvaluator(OneReturnValueEvaluator):
|
|||||||
concepts_entries = None
|
concepts_entries = None
|
||||||
evaluated = BuiltinConcepts.NOT_INITIALIZED
|
evaluated = BuiltinConcepts.NOT_INITIALIZED
|
||||||
errors = []
|
errors = []
|
||||||
|
expect_success = BuiltinConcepts.EVAL_SUCCESS_REQUESTED in context.local_hints
|
||||||
for globals_ in all_possible_globals:
|
for globals_ in all_possible_globals:
|
||||||
try:
|
try:
|
||||||
# eval
|
# eval
|
||||||
@@ -92,6 +93,7 @@ class PythonEvaluator(OneReturnValueEvaluator):
|
|||||||
context.log("Evaluating using 'exec'.", self.name)
|
context.log("Evaluating using 'exec'.", self.name)
|
||||||
evaluated = self.exec_with_return(node.ast_, globals_, sheerka.locals)
|
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
|
break # in this first version, we stop once a success is found
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
if concepts_entries is None:
|
if concepts_entries is None:
|
||||||
@@ -246,7 +248,7 @@ class PythonEvaluator(OneReturnValueEvaluator):
|
|||||||
# make the product the rest as cartesian product
|
# make the product the rest as cartesian product
|
||||||
res = [fixed_values]
|
res = [fixed_values]
|
||||||
for k, v in concepts_with_body.items():
|
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
|
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()
|
pre: ReturnValueConcept = NotInitializedNode()
|
||||||
post: ReturnValueConcept = NotInitializedNode()
|
post: ReturnValueConcept = NotInitializedNode()
|
||||||
body: ReturnValueConcept = NotInitializedNode()
|
body: ReturnValueConcept = NotInitializedNode()
|
||||||
|
ret: ReturnValueConcept = NotInitializedNode()
|
||||||
definition: ReturnValueConcept = NotInitializedNode()
|
definition: ReturnValueConcept = NotInitializedNode()
|
||||||
definition_type: str = None
|
definition_type: str = None
|
||||||
|
|
||||||
@@ -245,6 +246,7 @@ class DefaultParser(BaseParser):
|
|||||||
concept_found.pre = asts_found_by_parts[Keywords.PRE]
|
concept_found.pre = asts_found_by_parts[Keywords.PRE]
|
||||||
concept_found.post = asts_found_by_parts[Keywords.POST]
|
concept_found.post = asts_found_by_parts[Keywords.POST]
|
||||||
concept_found.body = asts_found_by_parts[Keywords.AS]
|
concept_found.body = asts_found_by_parts[Keywords.AS]
|
||||||
|
concept_found.ret = asts_found_by_parts[Keywords.RET]
|
||||||
|
|
||||||
return concept_found
|
return concept_found
|
||||||
|
|
||||||
@@ -279,7 +281,13 @@ class DefaultParser(BaseParser):
|
|||||||
|
|
||||||
def regroup_tokens_by_parts(self, keywords_tokens):
|
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, when trying to recognize the parts
|
||||||
tokens_found_by_parts = {
|
tokens_found_by_parts = {
|
||||||
@@ -289,6 +297,7 @@ class DefaultParser(BaseParser):
|
|||||||
Keywords.WHERE: None,
|
Keywords.WHERE: None,
|
||||||
Keywords.PRE: None,
|
Keywords.PRE: None,
|
||||||
Keywords.POST: None,
|
Keywords.POST: None,
|
||||||
|
Keywords.RET: None,
|
||||||
}
|
}
|
||||||
current_part = Keywords.CONCEPT
|
current_part = Keywords.CONCEPT
|
||||||
token = self.parser_input.token
|
token = self.parser_input.token
|
||||||
@@ -386,6 +395,7 @@ class DefaultParser(BaseParser):
|
|||||||
Keywords.WHERE: NotInitializedNode(),
|
Keywords.WHERE: NotInitializedNode(),
|
||||||
Keywords.PRE: NotInitializedNode(),
|
Keywords.PRE: NotInitializedNode(),
|
||||||
Keywords.POST: NotInitializedNode(),
|
Keywords.POST: NotInitializedNode(),
|
||||||
|
Keywords.RET: NotInitializedNode()
|
||||||
}
|
}
|
||||||
|
|
||||||
for keyword in tokens_found_by_parts:
|
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
|
has_unrecognized = True # never trust source code not. I may be an invalid source code
|
||||||
|
|
||||||
else: # cannot happen as of today :-)
|
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
|
# concept with UnrecognizedToken in their properties is considered as fatal error
|
||||||
if self.has_error:
|
if self.has_error:
|
||||||
|
|||||||
@@ -130,8 +130,15 @@ class SheerkaPickler:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def exist(self, obj):
|
def exist(self, obj):
|
||||||
for k, v in self.ids.items():
|
try:
|
||||||
if k == id(obj):
|
v = self.ids[id(obj)]
|
||||||
return True, v
|
return True, v
|
||||||
|
except KeyError:
|
||||||
return False, None
|
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",
|
where="definition of the where",
|
||||||
pre="definition of the pre",
|
pre="definition of the pre",
|
||||||
post="definition of the post",
|
post="definition of the post",
|
||||||
|
ret="concept to return",
|
||||||
definition="bnf definition",
|
definition="bnf definition",
|
||||||
definition_type="def type",
|
definition_type="def type",
|
||||||
desc="this this the desc",
|
desc="this this the desc",
|
||||||
@@ -88,6 +89,7 @@ def test_i_can_serialize():
|
|||||||
'name': 'concept_name',
|
'name': 'concept_name',
|
||||||
'post': 'definition of the post',
|
'post': 'definition of the post',
|
||||||
'pre': 'definition of the pre',
|
'pre': 'definition of the pre',
|
||||||
|
'ret': "concept to return",
|
||||||
'props': {},
|
'props': {},
|
||||||
'variables': [('a', "10"), ('b', None)],
|
'variables': [('a', "10"), ('b', None)],
|
||||||
'where': 'definition of the where'
|
'where': 'definition of the where'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import ast
|
import ast
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
import pytest
|
||||||
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
|
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
|
||||||
from core.concept import VARIABLE_PREFIX, Concept, DEFINITION_TYPE_BNF, DEFINITION_TYPE_DEF
|
from core.concept import VARIABLE_PREFIX, Concept, DEFINITION_TYPE_BNF, DEFINITION_TYPE_DEF
|
||||||
from core.tokenizer import Tokenizer
|
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))))
|
def_concept = DefConceptNode([], name=NameNode(list(Tokenizer(name))))
|
||||||
|
|
||||||
if body:
|
if body:
|
||||||
@@ -63,6 +64,8 @@ class TestAddConceptEvaluator(TestUsingMemoryBasedSheerka):
|
|||||||
def_concept.pre = self.get_concept_part(pre)
|
def_concept.pre = self.get_concept_part(pre)
|
||||||
if post:
|
if post:
|
||||||
def_concept.post = self.get_concept_part(post)
|
def_concept.post = self.get_concept_part(post)
|
||||||
|
if ret:
|
||||||
|
def_concept.ret = self.get_concept_part(ret)
|
||||||
if bnf_def:
|
if bnf_def:
|
||||||
def_concept.definition = bnf_def
|
def_concept.definition = bnf_def
|
||||||
def_concept.definition_type = DEFINITION_TYPE_BNF
|
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"))),
|
bnf_def=self.get_return_value("hello a", Sequence(StrMatch("hello"), StrMatch("a"))),
|
||||||
where="isinstance(a, str )",
|
where="isinstance(a, str )",
|
||||||
pre="a is not None",
|
pre="a is not None",
|
||||||
body="print('hello' + a)")
|
body="print('hello' + a)",
|
||||||
|
ret="a")
|
||||||
|
|
||||||
evaluated = AddConceptEvaluator().eval(context, def_concept_return_value)
|
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.key == "hello __var__0"
|
||||||
assert created_concept.metadata.where == "isinstance(a, str )"
|
assert created_concept.metadata.where == "isinstance(a, str )"
|
||||||
assert created_concept.metadata.pre == "a is not None"
|
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.body == "print('hello' + a)"
|
||||||
|
assert created_concept.metadata.ret == "a"
|
||||||
assert created_concept.metadata.definition == "hello a"
|
assert created_concept.metadata.definition == "hello a"
|
||||||
assert created_concept.metadata.definition_type == "bnf"
|
assert created_concept.metadata.definition_type == "bnf"
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ class TestPythonEvaluator(TestUsingMemoryBasedSheerka):
|
|||||||
evaluated = PythonEvaluator().eval(context, parsed)
|
evaluated = PythonEvaluator().eval(context, parsed)
|
||||||
|
|
||||||
assert evaluated.status
|
assert evaluated.status
|
||||||
assert evaluated.value == 2
|
assert evaluated.value == CB("foo", 2)
|
||||||
|
|
||||||
def test_i_can_eval_concept_token(self):
|
def test_i_can_eval_concept_token(self):
|
||||||
context = self.get_context()
|
context = self.get_context()
|
||||||
@@ -126,13 +126,35 @@ class TestPythonEvaluator(TestUsingMemoryBasedSheerka):
|
|||||||
assert evaluated.status
|
assert evaluated.status
|
||||||
assert evaluated.value == "foo"
|
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):
|
def test_i_can_call_function_with_complex_concepts(self):
|
||||||
sheerka, context, plus, mult = self.init_concepts(
|
sheerka, context, plus, mult = self.init_concepts(
|
||||||
self.from_def_concept("plus", "a plus b", ["a", "b"]),
|
self.from_def_concept("plus", "a plus b", ["a", "b"]),
|
||||||
self.from_def_concept("mult", "a mult 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()
|
python_evaluator = PythonEvaluator()
|
||||||
|
|
||||||
evaluated = python_evaluator.eval(context, parsed)
|
evaluated = python_evaluator.eval(context, parsed)
|
||||||
@@ -164,8 +186,8 @@ class TestPythonEvaluator(TestUsingMemoryBasedSheerka):
|
|||||||
|
|
||||||
all_globals = python_evaluator.get_all_possible_globals(context, my_globals)
|
all_globals = python_evaluator.get_all_possible_globals(context, my_globals)
|
||||||
assert len(all_globals) == 2
|
assert len(all_globals) == 2
|
||||||
assert all_globals[0]["foo"] == 'foo'
|
assert all_globals[0]["foo"] == CB(foo, "foo")
|
||||||
assert all_globals[1]["foo"] == CB(foo, "foo") # body is evaluated
|
assert all_globals[1]["foo"] == 'foo' # body is evaluated
|
||||||
|
|
||||||
def test_i_can_detect_one_error(self):
|
def test_i_can_detect_one_error(self):
|
||||||
sheerka, context, foo = self.init_concepts("foo")
|
sheerka, context, foo = self.init_concepts("foo")
|
||||||
@@ -194,11 +216,11 @@ class TestPythonEvaluator(TestUsingMemoryBasedSheerka):
|
|||||||
error0 = evaluated.body.body[0]
|
error0 = evaluated.body.body[0]
|
||||||
assert isinstance(error0, PythonEvalError)
|
assert isinstance(error0, PythonEvalError)
|
||||||
assert isinstance(error0.error, TypeError)
|
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'"
|
||||||
assert error0.concepts == {'foo': 'string'}
|
assert error0.concepts == {'foo': CB(foo, 'string')}
|
||||||
|
|
||||||
error1 = evaluated.body.body[1]
|
error1 = evaluated.body.body[1]
|
||||||
assert isinstance(error1, PythonEvalError)
|
assert isinstance(error1, PythonEvalError)
|
||||||
assert isinstance(error1.error, TypeError)
|
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'
|
||||||
assert error1.concepts == {'foo': CB(foo, 'string')}
|
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]
|
error0 = res[0].body.body.body[0]
|
||||||
assert isinstance(error0, PythonEvalError)
|
assert isinstance(error0, PythonEvalError)
|
||||||
assert isinstance(error0.error, TypeError)
|
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]
|
error1 = res[0].body.body.body[1]
|
||||||
assert isinstance(error1, PythonEvalError)
|
assert isinstance(error1, PythonEvalError)
|
||||||
assert isinstance(error1.error, TypeError)
|
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):
|
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
|
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))))
|
def_concept = DefConceptNode([], name=NameNode(list(Tokenizer(name))))
|
||||||
|
|
||||||
if body:
|
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)
|
def_concept.pre = get_concept_part(pre)
|
||||||
if post:
|
if post:
|
||||||
def_concept.post = get_concept_part(post)
|
def_concept.post = get_concept_part(post)
|
||||||
|
if ret:
|
||||||
|
def_concept.ret = get_concept_part(ret)
|
||||||
if bnf_def:
|
if bnf_def:
|
||||||
def_concept.definition = ReturnValueConcept(
|
def_concept.definition = ReturnValueConcept(
|
||||||
"parsers.Bnf",
|
"parsers.Bnf",
|
||||||
@@ -75,6 +77,9 @@ def get_concept_part(part):
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PN:
|
class PN:
|
||||||
|
"""
|
||||||
|
Python Node
|
||||||
|
"""
|
||||||
source: str # parser result source
|
source: str # parser result source
|
||||||
mode: str # compilation mode
|
mode: str # compilation mode
|
||||||
|
|
||||||
@@ -110,21 +115,23 @@ class TestDefaultParser(TestUsingMemoryBasedSheerka):
|
|||||||
assert node == expected
|
assert node == expected
|
||||||
|
|
||||||
def test_i_can_parse_complex_def_concept_statement(self):
|
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
|
where a,b
|
||||||
pre isinstance(a, int) and isinstance(b, float)
|
pre isinstance(b, int)
|
||||||
post isinstance(res, int)
|
post isinstance(res, a)
|
||||||
as res = a + b
|
as res = a * b
|
||||||
|
ret a if isinstance(a, Concept) else self
|
||||||
"""
|
"""
|
||||||
sheerka, context, parser = self.init_parser()
|
sheerka, context, parser = self.init_parser()
|
||||||
res = parser.parse(context, ParserInput(text))
|
res = parser.parse(context, ParserInput(text))
|
||||||
return_value = res.value
|
return_value = res.value
|
||||||
expected_concept = get_def_concept(
|
expected_concept = get_def_concept(
|
||||||
name="a plus b",
|
name="a mult b",
|
||||||
where="a,b\n",
|
where="a,b\n",
|
||||||
pre="isinstance(a, int) and isinstance(b, float)\n",
|
pre="isinstance(b, int)\n",
|
||||||
post="isinstance(res, int)\n",
|
post="isinstance(res, a)\n",
|
||||||
body=PN("res = a + b\n ", "exec")
|
body=PN("res = a * b\n", "exec"),
|
||||||
|
ret="a if isinstance(a, Concept) else self\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert res.status
|
assert res.status
|
||||||
|
|||||||
Reference in New Issue
Block a user