import ast 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.sheerka.services.SheerkaExecute import ParserInput from core.tokenizer import Tokenizer from evaluators.DefConceptEvaluator import DefConceptEvaluator from parsers.BaseParser import BaseParser from parsers.BnfDefinitionParser import BnfDefinitionParser from parsers.BnfNodeParser import Sequence, StrMatch, ZeroOrMore, ConceptExpression from parsers.DefConceptParser import DefConceptNode, NameNode, DefConceptParser from parsers.PythonParser import PythonNode, PythonParser from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka class TestDefConceptEvaluator(TestUsingMemoryBasedSheerka): @staticmethod def get_concept_part(part): if isinstance(part, str): node = PythonNode(part, ast.parse(part, mode="exec")) return ReturnValueConcept( who="parsers.Default", status=True, value=ParserResultConcept( source=part, parser=PythonParser(), value=node)) if isinstance(part, PythonNode): return ReturnValueConcept( who="parsers.Default", status=True, value=ParserResultConcept( source=part.source, parser=PythonParser(), value=part)) if isinstance(part, ReturnValueConcept): return part @staticmethod def get_return_value(source, parsing_expression): return ReturnValueConcept( who="Parsers:RegexParser", status=True, value=ParserResultConcept( source=source, parser=BnfDefinitionParser(), value=parsing_expression ) ) 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: def_concept.body = self.get_concept_part(body) if where: def_concept.where = self.get_concept_part(where) if pre: 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 if definition: def_concept.definition = NameNode(list(Tokenizer(definition))) def_concept.definition_type = DEFINITION_TYPE_DEF return ReturnValueConcept(BaseParser.PREFIX + "some_name", True, ParserResultConcept(value=def_concept)) @staticmethod def get_def_concept_node_from_name_only(name): return DefConceptNode([], name=NameNode(list(Tokenizer(name)))) @pytest.mark.parametrize("ret_val, expected", [ (ReturnValueConcept(BaseParser.PREFIX + "some_name", True, ParserResultConcept(value=DefConceptNode([]))), True), (ReturnValueConcept(BaseParser.PREFIX + "some_name", False, ParserResultConcept(value=DefConceptNode([]))), False), (ReturnValueConcept(BaseParser.PREFIX + "some_name", True, "not a ParserResultConcept"), False), (ReturnValueConcept(BaseParser.PREFIX + "some_name", True, ParserResultConcept()), False), ]) def test_i_can_match(self, ret_val, expected): context = self.get_context() assert DefConceptEvaluator().matches(context, ret_val) == expected def test_that_the_source_is_correctly_set_for_bnf_concepts(self): context = self.get_context() def_concept_return_value = self.get_def_concept( name="hello a", 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)", ret="a") evaluated = DefConceptEvaluator().eval(context, def_concept_return_value) assert evaluated.status assert context.sheerka.isinstance(evaluated.body, BuiltinConcepts.NEW_CONCEPT) created_concept = evaluated.body.body assert created_concept.get_metadata().name == "hello a" assert created_concept.get_metadata().key == "hello __var__0" assert created_concept.get_metadata().where == "isinstance(a, str )" assert created_concept.get_metadata().pre == "a is not None" assert created_concept.get_metadata().post is None # test that NotInitialized is mapped into None assert created_concept.get_metadata().body == "print('hello' + a)" assert created_concept.get_metadata().ret == "a" assert created_concept.get_metadata().definition == "hello a" assert created_concept.get_metadata().definition_type == "bnf" def test_that_the_source_is_correctly_set_for_concepts_with_simple_definition(self): context = self.get_context() def_concept_return_value = self.get_def_concept( name="greetings", definition="hello a", where="isinstance(a, str )", pre="a is not None", body="print('hello' + a)") evaluated = DefConceptEvaluator().eval(context, def_concept_return_value) assert evaluated.status assert context.sheerka.isinstance(evaluated.body, BuiltinConcepts.NEW_CONCEPT) created_concept = evaluated.body.body assert created_concept.get_metadata().name == "greetings" assert created_concept.get_metadata().key == "hello __var__0" assert created_concept.get_metadata().where == "isinstance(a, str )" assert created_concept.get_metadata().pre == "a is not None" assert created_concept.get_metadata().post is None assert created_concept.get_metadata().body == "print('hello' + a)" assert created_concept.get_metadata().definition == "hello a" assert created_concept.get_metadata().definition_type == "def" def test_i_can_add_concept_with_the_correct_variables_when_referencing_other_concepts(self): context = self.get_context() def_concept_return_value = self.get_def_concept( name="x plus y", where=self.pretval(Concept("u is a v").def_var("u").def_var("v"), source="x is a number"), body=self.pretval(Concept("add a b").def_var("a").def_var("b"), source="add x y"), ) evaluated = DefConceptEvaluator().eval(context, def_concept_return_value) assert evaluated.status assert context.sheerka.isinstance(evaluated.body, BuiltinConcepts.NEW_CONCEPT) created_concept = evaluated.body.body assert created_concept.get_metadata().variables == [("x", None), ("y", None)] def test_other_concepts_are_not_variables(self): sheerka, context, *concepts = self.init_test().with_concepts("little", "size", create_new=True).unpack() def_concept_node = self.get_def_concept_node_from_name_only("little x") name_to_use = DefConceptEvaluator.get_name_to_use(def_concept_node) concept_part = self.get_concept_part("set_attr(x, size, little)") assert DefConceptEvaluator.get_variables(context, concept_part, name_to_use) == {"x"} def test_that_the_new_concept_is_correctly_saved_in_db(self): context = self.get_context() def_concept_return_value = self.get_def_concept( name="hello a", 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)") # sanity. Make sure that the concept does not already exist from_db = context.sheerka.get_by_key("hello " + VARIABLE_PREFIX + "0") assert context.sheerka.isinstance(from_db, BuiltinConcepts.UNKNOWN_CONCEPT) DefConceptEvaluator().eval(context, def_concept_return_value) context.sheerka.concepts_cache = {} # reset cache from_db = context.sheerka.get_by_key("hello " + VARIABLE_PREFIX + "0") assert from_db.get_metadata().key == f"hello {VARIABLE_PREFIX}0" assert from_db.get_metadata().name == "hello a" assert from_db.get_metadata().where == "isinstance(a, str )" assert from_db.get_metadata().pre == "a is not None" assert from_db.get_metadata().post is None assert from_db.get_metadata().body == "print('hello' + a)" assert from_db.get_metadata().definition == "hello a" assert from_db.get_metadata().definition_type == "bnf" assert len(from_db.get_metadata().variables) == 1 assert from_db.get_metadata().variables[0] == ("a", None) assert "a" in from_db.values() assert from_db.get_compiled() == {} # ast is not saved in db @pytest.mark.parametrize("expression, name, expected", [ ("isinstance(a, str)", "a b", {"a"}), ("a.location = b", "a is in b", {"a", "b"}), ("a.location = b", "'a' is in b", {"b"}), ("date.today()", "what is the date", set()), ("a.location", "where is a", {"a"}) ]) def test_i_can_get_variables_from_python_node_when_long_name(self, expression, name, expected): ret_val = self.get_concept_part(expression) context = self.get_context() assert DefConceptEvaluator.get_variables(context, ret_val, name.split()) == expected def test_i_can_get_variables_when_keywords(self): sheerka, context = self.init_concepts() def_concept = self.get_def_concept("condition pre").value.value name_to_use = DefConceptEvaluator.get_name_to_use(def_concept) concept_part = self.get_concept_part("pre") assert DefConceptEvaluator.get_variables(context, concept_part, name_to_use) == {"pre"} def test_i_cannot_get_variables_from_python_node_when_name_has_only_one_token(self): ret_val = self.get_concept_part("isinstance(a, str)") context = self.get_context() assert DefConceptEvaluator.get_variables(context, ret_val, ["a"]) == set() def test_i_can_get_variables_from_definition(self): parsing_expression = Sequence(ConceptExpression('mult'), ZeroOrMore(Sequence(StrMatch("+"), ConceptExpression("add")))) ret_val = self.get_return_value("mult (('+'|'-') add)?", parsing_expression) assert DefConceptEvaluator.get_variables(self.get_context(), ret_val, []) == {"add", "mult"} def test_concept_that_references_itself_is_correctly_created(self): context = self.get_context() def_concept_as_return_value = self.get_def_concept("foo", body="foo") ret_val = DefConceptEvaluator().eval(context, def_concept_as_return_value) assert ret_val.status new_concept = ret_val.body.body assert new_concept.name == 'foo' assert new_concept.get_metadata().body == 'foo' assert new_concept.values() == {} assert new_concept.get_metadata().variables == [] def test_i_can_get_variable_when_rule_is_defined(self): sheerka, context = self.init_test().unpack() def_concept_parser = DefConceptParser() parsed_ret_val = def_concept_parser.parse(context, ParserInput("def concept rule x as r:|x:")) assert parsed_ret_val.status # sanity check evaluated = DefConceptEvaluator().eval(context, parsed_ret_val) assert evaluated.status assert evaluated.body.body.key == "rule __var__0" assert evaluated.body.body.get_metadata().variables == [("x", None)] def test_i_can_recognize_variable_when_keyword_argument(self): sheerka, context = self.init_test().unpack() def_concept_parser = DefConceptParser() parsed_ret_val = def_concept_parser.parse(context, ParserInput("def concept foo1 x as func(param=x)")) assert parsed_ret_val.status # sanity check evaluated = DefConceptEvaluator().eval(context, parsed_ret_val) assert evaluated.status assert evaluated.body.body.key == "foo1 __var__0" assert evaluated.body.body.get_metadata().variables == [("x", None)] parsed_ret_val = def_concept_parser.parse(context, ParserInput("def concept foo2 x as func(x=value)")) assert parsed_ret_val.status # sanity check evaluated = DefConceptEvaluator().eval(context, parsed_ret_val) assert evaluated.status assert evaluated.body.body.key == "foo2 __var__0" assert evaluated.body.body.get_metadata().variables == [("x", None)]