diff --git a/src/core/concept.py b/src/core/concept.py index 08798a2..8a03776 100644 --- a/src/core/concept.py +++ b/src/core/concept.py @@ -248,7 +248,7 @@ class Concept: if token.value in variables: key += VARIABLE_PREFIX + str(variables.index(token.value)) else: - #value = token.value[1:-1] if token.type == TokenKind.STRING else token.value + # value = token.value[1:-1] if token.type == TokenKind.STRING else token.value key += token.value first = False @@ -490,6 +490,16 @@ class InfiniteRecursionResolved: return self.value +def ensure_concept(*concepts): + if hasattr(concepts, "__iter__"): + for concept in concepts: + if not isinstance(concept, Concept): + raise TypeError(f"'{concept}' must be a concept") + else: + if not isinstance(concepts, Concept): + raise TypeError(f"'{concepts}' must be a concept") + + # ################################ # # Class created for tests purpose diff --git a/src/core/sheerka/Sheerka.py b/src/core/sheerka/Sheerka.py index 6d85127..b1242c8 100644 --- a/src/core/sheerka/Sheerka.py +++ b/src/core/sheerka/Sheerka.py @@ -13,6 +13,7 @@ from core.builtin_concepts import BuiltinConcepts, ErrorConcept, ReturnValueConc from core.concept import Concept, ConceptParts, PROPERTIES_FOR_NEW from core.sheerka.ExecutionContext import ExecutionContext from core.sheerka_logger import console_handler +from core.tokenizer import Token, TokenKind from printer.SheerkaPrinter import SheerkaPrinter from sdp.sheerkaDataProvider import SheerkaDataProvider, Event @@ -489,6 +490,37 @@ class Sheerka(Concept): metadata = [(index_name, key), ("id", concept_id)] if concept_id else (index_name, key) return self._get_unknown(metadata) + def resolve(self, concept): + if concept is None: + return concept + + # if the entry is a concept token, use its values. + if isinstance(concept, Token): + if concept.type != TokenKind.CONCEPT: + return None + concept = concept.value + + # if the entry is a tuple + # concept[0] is the name + # concept[1] is the id + if isinstance(concept, tuple): + if concept[1]: + if self.is_known(found := self.get_by_id(concept[1])): + return found + elif concept[0]: + return found if self.is_known(found := self.get_by_name(concept[0])) else None + else: + return None + + # otherwise search in db + if isinstance(concept, str): + if self.is_known(found := self.get_by_id(concept)): + return found + if self.is_known(found := self.get_by_name(concept)): + return found + + return None + def has_id(self, concept_id): """ Returns True if a concept with this id exists in cache diff --git a/src/core/sheerka/services/SheerkaComparisonManager.py b/src/core/sheerka/services/SheerkaComparisonManager.py index 23afe60..a46cc27 100644 --- a/src/core/sheerka/services/SheerkaComparisonManager.py +++ b/src/core/sheerka/services/SheerkaComparisonManager.py @@ -3,6 +3,7 @@ from dataclasses import dataclass from cache.Cache import Cache from cache.ListCache import ListCache from core.builtin_concepts import BuiltinConcepts +from core.concept import ensure_concept from core.sheerka.services.sheerka_service import ServiceObj, BaseService @@ -105,6 +106,7 @@ class SheerkaComparisonManager(BaseService): :return: """ context.log(f"Setting concept {concept_a} is greater than {concept_b}", who=self.NAME) + ensure_concept(concept_a, concept_b) event_digest = context.event.get_digest() comparison_obj = ComparisonObj(event_digest, prop_name, concept_a.id, concept_b.id, ">", comparison_context) @@ -121,6 +123,7 @@ class SheerkaComparisonManager(BaseService): :return: """ context.log(f"Setting concept {concept_a} is less than {concept_b}", who=self.NAME) + ensure_concept(concept_a, concept_b) event_digest = context.event.get_digest() comparison_obj = ComparisonObj(event_digest, prop_name, concept_a.id, concept_b.id, "<", comparison_context) diff --git a/src/core/sheerka/services/SheerkaCreateNewConcept.py b/src/core/sheerka/services/SheerkaCreateNewConcept.py index d841d8b..26988dd 100644 --- a/src/core/sheerka/services/SheerkaCreateNewConcept.py +++ b/src/core/sheerka/services/SheerkaCreateNewConcept.py @@ -1,6 +1,6 @@ import core.utils from core.builtin_concepts import BuiltinConcepts, ErrorConcept -from core.concept import Concept +from core.concept import Concept, DEFINITION_TYPE_DEF, ensure_concept from core.sheerka.services.sheerka_service import BaseService from sdp.sheerkaDataProvider import SheerkaDataProviderDuplicateKeyError @@ -30,6 +30,8 @@ class SheerkaCreateNewConcept(BaseService): :return: digest of the new concept """ + ensure_concept(concept) + sheerka = self.sheerka concept.init_key() @@ -49,24 +51,29 @@ class SheerkaCreateNewConcept(BaseService): # set id before saving in db sheerka.set_id_if_needed(concept, False) - # update the dictionary of concepts by first key + # compute new concepts_by_first_keyword init_ret_value = self.bnp.get_concepts_by_first_keyword(context, [concept], True) if not init_ret_value.status: return sheerka.ret(self.NAME, False, ErrorConcept(init_ret_value.value)) concepts_by_first_keyword = init_ret_value.body - # update resolved dictionary + # computes resolved concepts_by_first_keyword init_ret_value = self.bnp.resolve_concepts_by_first_keyword(context, concepts_by_first_keyword) if not init_ret_value.status: return sheerka.ret(self.NAME, False, ErrorConcept(init_ret_value.value)) resolved_concepts_by_first_keyword = init_ret_value.body - concept.freeze_definition_hash() + # if everything is fine + concept.freeze_definition_hash() cache_manager.add_concept(concept) cache_manager.put(sheerka.CONCEPTS_BY_FIRST_KEYWORD_ENTRY, False, concepts_by_first_keyword) cache_manager.put(sheerka.RESOLVED_CONCEPTS_BY_FIRST_KEYWORD_ENTRY, False, resolved_concepts_by_first_keyword) + if concept.metadata.definition_type == DEFINITION_TYPE_DEF and concept.metadata.definition != concept.name: + # allow search by definition when definition relevant + cache_manager.put(self.sheerka.CONCEPTS_BY_NAME_ENTRY, concept.metadata.definition, concept) + if concept.bnf and init_bnf_ret_value is not None and init_bnf_ret_value.status: sheerka.cache_manager.clear(sheerka.CONCEPTS_GRAMMARS_ENTRY) diff --git a/src/core/sheerka/services/SheerkaModifyConcept.py b/src/core/sheerka/services/SheerkaModifyConcept.py index 1ce10c5..9635c9a 100644 --- a/src/core/sheerka/services/SheerkaModifyConcept.py +++ b/src/core/sheerka/services/SheerkaModifyConcept.py @@ -41,6 +41,7 @@ class SheerkaModifyConcept(BaseService): # TODO : update concept by first keyword # TODO : update resolved by first keyword # TODO : update concepts grammars + # TODO : update when definition_type = DEFINITION_TYPE_DEF ret = self.sheerka.ret(self.NAME, True, self.sheerka.new(BuiltinConcepts.NEW_CONCEPT, body=concept)) return ret diff --git a/src/core/sheerka/services/SheerkaSetsManager.py b/src/core/sheerka/services/SheerkaSetsManager.py index 332bf16..17faa49 100644 --- a/src/core/sheerka/services/SheerkaSetsManager.py +++ b/src/core/sheerka/services/SheerkaSetsManager.py @@ -2,7 +2,7 @@ import core.builtin_helpers from cache.SetCache import SetCache from core.ast.nodes import python_to_concept from core.builtin_concepts import BuiltinConcepts -from core.concept import Concept, ConceptParts +from core.concept import Concept, ConceptParts, ensure_concept from core.sheerka.services.sheerka_service import BaseService GROUP_PREFIX = 'All_' @@ -36,6 +36,7 @@ class SheerkaSetsManager(BaseService): """ context.log(f"Setting concept {concept} is a {concept_set}", who=self.NAME) + ensure_concept(concept, concept_set) if BuiltinConcepts.ISA in concept.metadata.props and concept_set in concept.metadata.props[BuiltinConcepts.ISA]: return self.sheerka.ret( @@ -61,9 +62,7 @@ class SheerkaSetsManager(BaseService): """ context.log(f"Adding concept {concept} to set {concept_set}", who=self.NAME) - - assert concept.id - assert concept_set.id + ensure_concept(concept, concept_set) set_elements = self.sheerka.cache_manager.get(self.CONCEPTS_GROUPS_ENTRY, concept_set.id) if set_elements and concept.id in set_elements: @@ -79,6 +78,7 @@ class SheerkaSetsManager(BaseService): """Adding multiple concepts at the same time""" context.log(f"Adding concepts {concepts} to set {concept_set}", who=self.NAME) + ensure_concept(concept_set) already_in_set = [] for concept in concepts: res = self.add_concept_to_set(context, concept, concept_set) @@ -103,6 +103,8 @@ class SheerkaSetsManager(BaseService): :return: """ + ensure_concept(concept) + def _get_set_elements(sub_concept): if not self.isaset(context, sub_concept): return self.sheerka.new(BuiltinConcepts.NOT_A_SET, body=concept) @@ -151,8 +153,9 @@ class SheerkaSetsManager(BaseService): if isinstance(a, BuiltinConcepts): # common KSI error ;-) raise SyntaxError("Remember that the first parameter of isinstance MUST be a concept") - if not (isinstance(a, Concept) and isinstance(b, Concept)): - return False + ensure_concept(a, b) + # if not (isinstance(a, Concept) and isinstance(b, Concept)): + # return False # TODO, first check the 'isa' property of a if not (a.id and b.id): @@ -163,6 +166,7 @@ class SheerkaSetsManager(BaseService): def isa(self, a, b): + ensure_concept(a, b) if BuiltinConcepts.ISA not in a.metadata.props: return False diff --git a/src/core/utils.py b/src/core/utils.py index a718228..bde66d9 100644 --- a/src/core/utils.py +++ b/src/core/utils.py @@ -363,7 +363,7 @@ def unstr_concept(concept_repr): return key if key != "" else None, id if id != "" else None -def encode_concept(t, use_concept=False): +def encode_concept(t): """ Given a tuple of concept id, concept id Create a valid Python identifier that can be parsed back @@ -371,15 +371,13 @@ def encode_concept(t, use_concept=False): >>> assert encode_concept(("key", "id")) == "__C__KEY_key__ID_id__C__" >>> assert encode_concept((None, "id")) == "__C__KEY_00None00__ID_id__C__" >>> assert encode_concept(("key", None)) == "__C__KEY_key__ID_00None00__C__" - >>> assert encode_concept(("key", "id"), True) == "__C__USE_CONCEPT__KEY_key__ID_id__C__" :param t: - :param use_concept: :return: """ key, id_ = (t[0], t[1]) if isinstance(t, tuple) else (t.key, t.id) - prefix = "__C__USE_CONCEPT" if use_concept else "__C" + prefix = "__C" sanitized_key = "".join(c if c.isalnum() else "0" for c in key) if key else "00None00" return prefix + f"__KEY_{sanitized_key}__ID_{id_ or '00None00'}__C__" @@ -393,15 +391,14 @@ def decode_concept(text): :param text: :return: """ - use_concept = text.startswith("__C__USE_CONCEPT") m = decode_regex.search(text) lookup = {"00None00": None} if m: key = lookup.get(m.group(1), m.group(1)) id_ = lookup.get(m.group(2), m.group(2)) - return key, id_, use_concept + return key, id_ - return None, None, None + return None, None def tokens_index(tokens, sub_tokens, skip=0): diff --git a/src/evaluators/PythonEvaluator.py b/src/evaluators/PythonEvaluator.py index de0466a..c615e2a 100644 --- a/src/evaluators/PythonEvaluator.py +++ b/src/evaluators/PythonEvaluator.py @@ -57,8 +57,8 @@ class PythonEvaluator(OneReturnValueEvaluator): # Do not evaluate if the ast refers to a concept (leave it to ConceptEvaluator) if isinstance(node.ast_, ast.Expression) and isinstance(node.ast_.body, ast.Name): - c = context.sheerka.get_by_key(node.ast_.body.id) - if not context.sheerka.isinstance(c, BuiltinConcepts.UNKNOWN_CONCEPT): + c = context.sheerka.resolve(node.ast_.body.id) + if c is not None: context.log("It's a simple concept. Not for me.", self.name) not_for_me = context.sheerka.new(BuiltinConcepts.NOT_FOR_ME, body=node) return sheerka.ret(self.name, False, not_for_me, parents=[return_value]) @@ -91,54 +91,11 @@ class PythonEvaluator(OneReturnValueEvaluator): "BuiltinConcepts": core.builtin_concepts.BuiltinConcepts, } + # has to tbe the first, to allow override method_from_sheerka = self.update_globals_with_sheerka_methods(my_locals, context) - if context.obj: - context.log(f"Concept '{context.obj}' is in context. Adding it and its properties to locals.", self.name) - - for prop_name in context.obj.variables(): - prop_value = context.obj.get_value(prop_name) - if isinstance(prop_value, Concept): - my_locals[prop_name] = context.sheerka.objvalue(prop_value) - else: - my_locals[prop_name] = prop_value - - my_locals["self"] = context.obj.body - - node_concept = core.ast.nodes.python_to_concept(node.ast_) - unreferenced_names_visitor = UnreferencedNamesVisitor(context.sheerka) - unreferenced_names_visitor.visit(node_concept) - - for name in unreferenced_names_visitor.names: - context.log(f"Resolving '{name}'.", self.name) - - if name in node.concepts: - context.log(f"Using value from node.", self.name) - concept = node.concepts[name] - return_concept = False - - else: - c_key, c_id, return_concept = self.resolve_name(name) - - if c_key in my_locals: - context.log(f"Using value from property.", self.name) - continue - - context.log(f"Instantiating new concept with {c_key=}, {c_id=}.", self.name) - new = context.sheerka.new - concept = new((None, c_id)) if c_id else new(c_key) - if context.sheerka.isinstance(concept, BuiltinConcepts.UNKNOWN_CONCEPT): - context.log(f"({c_key=}, {c_id=}) is not a concept. Skipping.", self.name) - continue - - context.log(f"Evaluating '{concept}'", self.name) - with context.push(self.name, desc=f"Evaluating '{concept}'", obj=concept) as sub_context: - sub_context.local_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED) - evaluated = context.sheerka.evaluate_concept(sub_context, concept) - sub_context.add_values(return_values=evaluated) - - if evaluated.key == concept.key: - my_locals[name] = evaluated if return_concept else context.sheerka.objvalue(evaluated) + self.update_globals_with_context(my_locals, context) + self.update_globals_with_node(my_locals, context, node) if self.locals: # when exta values are given. Add them my_locals.update(self.locals) @@ -163,6 +120,77 @@ class PythonEvaluator(OneReturnValueEvaluator): return methods_from_sheerka # to allow access using prefix "sheerka." + def update_globals_with_context(self, my_locals, context): + if context.obj: + context.log(f"Concept '{context.obj}' is in context. Adding it and its properties to locals.", self.name) + + for prop_name in context.obj.variables(): + prop_value = context.obj.get_value(prop_name) + if isinstance(prop_value, Concept): + my_locals[prop_name] = context.sheerka.objvalue(prop_value) + else: + my_locals[prop_name] = prop_value + + my_locals["self"] = context.obj.body + + def update_globals_with_node(self, my_locals, context, node): + node_concept = core.ast.nodes.python_to_concept(node.ast_) + unreferenced_names_visitor = UnreferencedNamesVisitor(context.sheerka) + unreferenced_names_visitor.visit(node_concept) + + for name in unreferenced_names_visitor.names: + context.log(f"Resolving '{name}'.", self.name) + + if name in node.concepts: + context.log(f"Using value from node.", self.name) + concept = self.resolve_concept(context, node.concepts[name]) + + elif name in my_locals: + context.log(f"Using value from property.", self.name) + continue + + else: + context.log(f"Instantiating new concept with {name}.", self.name) + concept = self.resolve_concept(context, name) + + if concept is None: + context.log(f"Concept '{name}' is not found or cannot be instantiated. Skipping.", self.name) + continue + + if concept.metadata.is_evaluated: + context.log(f"Concept {name} is already evaluated.", self.name) + + else: + context.log(f"Evaluating '{concept}'", self.name) + with context.push(self.name, desc=f"Evaluating '{concept}'", obj=concept) as sub_context: + sub_context.local_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED) + evaluated = context.sheerka.evaluate_concept(sub_context, concept) + sub_context.add_values(return_values=evaluated) + + if evaluated.key != concept.key: + context.log(f"Error while evaluating '{name}'. Skipping.", self.name) + continue + concept = evaluated + + my_locals[name] = context.sheerka.objvalue(concept) + + @staticmethod + def resolve_concept(context, concept_hint): + if isinstance(concept_hint, Concept): + return concept_hint + + concept = context.sheerka.resolve(concept_hint) + if concept is None: + return None + new_instance = context.sheerka.new_from_template(concept, concept.key) + if isinstance(concept_hint, tuple): + # It's means that it comes from PythonParser which have found a concept token (c:xxx:) + # So a concept was explicitly required, not its value + # We mark the concept as already evaluated, so it's body will not be evaluated + new_instance.metadata.is_evaluated = True + + return new_instance + @staticmethod def resolve_name(to_resolve): """ diff --git a/src/parsers/BaseParser.py b/src/parsers/BaseParser.py index 0d42df3..74db60b 100644 --- a/src/parsers/BaseParser.py +++ b/src/parsers/BaseParser.py @@ -141,15 +141,22 @@ class BaseParser: body=self.error_sink if self.has_error else tree, try_parsed=try_parse) - def get_input_as_text(self, parser_input, custom_switcher=None): + def get_input_as_text(self, parser_input, custom_switcher=None, tracker=None): + """ + Recreate back the source code from parser_input + :param parser_input: list of Tokens + :param custom_switcher: map of [TokenKind, overridden values] + :param tracker: keep track of the value overridden by custom_switcher + :return: + """ if isinstance(parser_input, list): - return self.get_text_from_tokens(parser_input, custom_switcher) + return self.get_text_from_tokens(parser_input, custom_switcher, tracker) if isinstance(parser_input, ParserResultConcept): parser_input = parser_input.source if "c:" in parser_input: - return self.get_text_from_tokens(list(Tokenizer(parser_input)), custom_switcher) + return self.get_text_from_tokens(list(Tokenizer(parser_input)), custom_switcher, tracker) return parser_input @@ -194,7 +201,14 @@ class BaseParser: return lst @staticmethod - def get_text_from_tokens(tokens, custom_switcher=None): + def get_text_from_tokens(tokens, custom_switcher=None, tracker=None): + """ + Create the source code, from the list of token + :param tokens: list of tokens + :param custom_switcher: to override the behaviour (the return value) of some token + :param tracker: keep track of the original token value when custom switched + :return: + """ if tokens is None: return "" res = "" @@ -213,6 +227,8 @@ class BaseParser: for token in tokens: value = switcher.get(token.type, lambda t: t.value)(token) res += value + if tracker is not None and token.type in custom_switcher: + tracker[value] = token.value return res @staticmethod diff --git a/src/parsers/PythonParser.py b/src/parsers/PythonParser.py index 2a9b1f2..ffa717d 100644 --- a/src/parsers/PythonParser.py +++ b/src/parsers/PythonParser.py @@ -72,11 +72,12 @@ class PythonParser(BaseParser): tree = None python_switcher = { - TokenKind.CONCEPT: lambda t: core.utils.encode_concept(t.value, True) + TokenKind.CONCEPT: lambda t: core.utils.encode_concept(t.value) } try: - source = self.get_input_as_text(parser_input, python_switcher) + tracker = {} + source = self.get_input_as_text(parser_input, python_switcher, tracker) source = source.strip() parser_input = parser_input if isinstance(parser_input, str) else source @@ -108,7 +109,7 @@ class PythonParser(BaseParser): BuiltinConcepts.PARSER_RESULT, parser=self, source=parser_input, - body=PythonNode(parser_input, tree), + body=PythonNode(parser_input, tree, tracker), try_parsed=None)) self.log_result(context, parser_input, ret) diff --git a/tests/BaseTest.py b/tests/BaseTest.py index 9d0d849..52fbe39 100644 --- a/tests/BaseTest.py +++ b/tests/BaseTest.py @@ -122,3 +122,18 @@ class BaseTest: concept.bnf = expression or StrMatch(name) concept.metadata.definition_type = DEFINITION_TYPE_BNF return concept + + @staticmethod + def def_concept(name, definition, variables=None, **kwargs): + concept = Concept(name=name, definition=definition, definition_type=DEFINITION_TYPE_DEF) + if variables: + for v in variables: + concept.def_var(v) + + if kwargs: + for k, v in kwargs.items(): + if k in ("body", "pre", "post", "where"): + setattr(concept.metadata, k, v) + else: + concept.metadata.variables[k] = v + return concept diff --git a/tests/core/test_SheerkaCreateNewConcept.py b/tests/core/test_SheerkaCreateNewConcept.py index 76ac9ae..c78dad8 100644 --- a/tests/core/test_SheerkaCreateNewConcept.py +++ b/tests/core/test_SheerkaCreateNewConcept.py @@ -139,6 +139,23 @@ class TestSheerkaCreateNewConcept(TestUsingMemoryBasedSheerka): assert res.status + def test_i_can_get_by_name_when_created_with_def_definition(self): + sheerka = self.get_sheerka(cache_only=False) + context = self.get_context(sheerka) + concept = self.def_concept("plus", "a plus b", ["a", "b"]) + + res = sheerka.create_new_concept(context, concept) + + assert res.status + assert sheerka.get_by_name(concept.name) == concept + assert sheerka.get_by_name(concept.metadata.definition) == concept + + concept = Concept(name="foo", definition="foo", definition_type=DEFINITION_TYPE_DEF) + res = sheerka.create_new_concept(context, concept) + + assert res.status + assert sheerka.get_by_name(concept.name) == concept # it's not a list, ie the entry is not duplicated + class TestSheerkaCreateNewConceptFileBased(TestUsingFileBasedSheerka): def test_i_can_add_several_concepts(self): diff --git a/tests/core/test_sheerka.py b/tests/core/test_sheerka.py index 8134bbc..dda8f18 100644 --- a/tests/core/test_sheerka.py +++ b/tests/core/test_sheerka.py @@ -4,6 +4,7 @@ import pytest from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept, UserInputConcept from core.concept import Concept, PROPERTIES_TO_SERIALIZE, ConceptParts from core.sheerka.Sheerka import Sheerka, BASE_NODE_PARSER_CLASS +from core.tokenizer import Token, TokenKind, Tokenizer from tests.TestUsingFileBasedSheerka import TestUsingFileBasedSheerka from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka @@ -274,6 +275,55 @@ class TestSheerkaUsingMemoryBasedSheerka(TestUsingMemoryBasedSheerka): sheerka = self.get_sheerka() assert not sheerka.is_success(sheerka.new(BuiltinConcepts.TOO_MANY_SUCCESS)) + @pytest.mark.parametrize("concept, expected", [ + (None, None), + ("foo", ["foo", "foo2"]), + ("bar", "bar"), + ("1001", "foo"), # by id take precedence over by name + ("plus", "plus"), + ("a mult b", "mult"), + ("unknown", None), + + # by tuple + ((None, None), None), + (("foo", None), ["foo", "foo2"]), + (("foo", "1002"), "foo2"), + ((None, "1001"), "foo"), + (("plus", None), "plus"), + (("1001", None), "1001"), + (("unknown", None), None), + ((None, "unknown"), None), + # + # by token + (Token(TokenKind.CONCEPT, (None, None), 0, 0, 0), None), + (Token(TokenKind.CONCEPT, ("foo", None), 0, 0, 0), ["foo", "foo2"]), + + ]) + def test_i_can_resolve_concept(self, concept, expected): + sheerka, context, *concepts = self.init_concepts( + "foo", + Concept("foo", body="another one"), + "bar", + self.def_concept("plus", "a plus b", ["a", "b"]), + Concept("a mult b").def_var("a").def_var("b"), + Concept("1001"), + ) + + cmap = {k: concepts[i] for i, k in enumerate(["foo", "foo2", "bar", "plus", "mult", "1001"])} + cmap[None] = None + + if isinstance(expected, list): + assert sheerka.resolve(concept) == [cmap[e] for e in expected] + else: + assert sheerka.resolve(concept) == cmap[expected] + + def test_i_can_resolve_when_searching_by_definition(self): + sheerka, context, plus = self.init_concepts( + self.def_concept("plus", "a plus b", ["a", "b"]), + create_new=True + ) + + assert sheerka.resolve("a plus b") == plus class TestSheerkaUsingFileBasedSheerka(TestUsingFileBasedSheerka): diff --git a/tests/core/test_utils.py b/tests/core/test_utils.py index 7dd2896..abf9016 100644 --- a/tests/core/test_utils.py +++ b/tests/core/test_utils.py @@ -198,7 +198,6 @@ def test_encode_concept_key_id(): assert core.utils.encode_concept(("key", "id")) == "__C__KEY_key__ID_id__C__" assert core.utils.encode_concept((None, "id")) == "__C__KEY_00None00__ID_id__C__" assert core.utils.encode_concept(("key", None)) == "__C__KEY_key__ID_00None00__C__" - assert core.utils.encode_concept(("key", "id"), True) == "__C__USE_CONCEPT__KEY_key__ID_id__C__" assert core.utils.encode_concept(("k + y", "id")) == "__C__KEY_k000y__ID_id__C__" concept = Concept("foo").init_key() @@ -209,7 +208,6 @@ def test_encode_concept_key_id(): def test_decode_concept_key_id(): - assert core.utils.decode_concept("__C__KEY_key__ID_id__C__") == ("key", "id", False) - assert core.utils.decode_concept("__C__KEY_00None00__ID_id__C__") == (None, "id", False) - assert core.utils.decode_concept("__C__KEY_key__ID_00None00__C__") == ("key", None, False) - assert core.utils.decode_concept("__C__USE_CONCEPT__KEY_key__ID_id__C__") == ("key", "id", True) + assert core.utils.decode_concept("__C__KEY_key__ID_id__C__") == ("key", "id") + assert core.utils.decode_concept("__C__KEY_00None00__ID_id__C__") == (None, "id") + assert core.utils.decode_concept("__C__KEY_key__ID_00None00__C__") == ("key", None) diff --git a/tests/evaluators/test_PythonEvaluator.py b/tests/evaluators/test_PythonEvaluator.py index 26324dc..b1f33fe 100644 --- a/tests/evaluators/test_PythonEvaluator.py +++ b/tests/evaluators/test_PythonEvaluator.py @@ -1,14 +1,14 @@ import pytest from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts -from core.concept import Concept +from core.concept import Concept, DEFINITION_TYPE_DEF from evaluators.PythonEvaluator import PythonEvaluator from parsers.PythonParser import PythonNode, PythonParser from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka -def get_context_name(context): - return context.name +def get_concept_name(concept): + return concept.name class TestPythonEvaluator(TestUsingMemoryBasedSheerka): @@ -118,23 +118,38 @@ class TestPythonEvaluator(TestUsingMemoryBasedSheerka): context = self.get_context() context.sheerka.add_in_cache(Concept("foo", body="2")) - parsed = PythonParser().parse(context, "get_context_name(c:foo:)") + parsed = PythonParser().parse(context, "get_concept_name(c:foo:)") python_evaluator = PythonEvaluator() - python_evaluator.locals["get_context_name"] = get_context_name + python_evaluator.locals["get_concept_name"] = get_concept_name evaluated = python_evaluator.eval(context, parsed) assert evaluated.status assert evaluated.value == "foo" # sanity, does not work otherwise - parsed = PythonParser().parse(context, "get_context_name(foo)") + parsed = PythonParser().parse(context, "get_concept_name(foo)") python_evaluator = PythonEvaluator() - python_evaluator.locals["get_context_name"] = get_context_name + python_evaluator.locals["get_concept_name"] = get_concept_name evaluated = python_evaluator.eval(context, parsed) assert not evaluated.status assert evaluated.body.body.args[0] == "'int' object has no attribute 'name'" + def test_i_can_call_function_with_complex_concepts(self): + sheerka, context, plus, mult = self.init_concepts( + self.def_concept("plus", "a plus b", ["a", "b"]), + self.def_concept("mult", "a mult b", ["a", "b"]), + ) + + parsed = PythonParser().parse(context, "is_greater_than(BuiltinConcepts.PRECEDENCE, mult, plus)") + python_evaluator = PythonEvaluator() + + evaluated = python_evaluator.eval(context, parsed) + + assert evaluated.status + assert sheerka.get_concepts_weights(BuiltinConcepts.PRECEDENCE) == {'1001': 1, '1002': 2} + + # @pytest.mark.parametrize("text, concept_key, concept_id, use_concept", [ # ("__C__key__C__", "key", None, False), # ("__C__key__id__C__", "key", "id", False), diff --git a/tests/parsers/test_PythonParser.py b/tests/parsers/test_PythonParser.py index e205d0b..6afaa5a 100644 --- a/tests/parsers/test_PythonParser.py +++ b/tests/parsers/test_PythonParser.py @@ -1,9 +1,10 @@ import ast + +import core.utils import pytest from core.builtin_concepts import ParserResultConcept, NotForMeConcept from core.tokenizer import Tokenizer, LexerError from parsers.PythonParser import PythonNode, PythonParser, PythonErrorNode -import core.utils from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka @@ -72,12 +73,16 @@ class TestPythonParser(TestUsingMemoryBasedSheerka): assert res.value.get_value("reason")[0].text == error_text def test_i_can_parse_a_concept(self): - text = "c:name|key: + 1" + text = "c:name|id: + 1" parser = PythonParser() res = parser.parse(self.get_context(), text) + encoded = core.utils.encode_concept(("name", "id")) assert res assert res.value.value == PythonNode( - "c:name|key: + 1", - ast.parse(core.utils.encode_concept(("name", "key"), True) + "+1", mode="eval")) + "c:name|id: + 1", + ast.parse(encoded + "+1", mode="eval")) + assert res.value.value.concepts == { + encoded: ("name", "id") + }