Fixed variable recognition when it is a concept

This commit is contained in:
2020-11-20 17:24:52 +01:00
parent 315f8ea09b
commit 0e945fe0fd
8 changed files with 79 additions and 36 deletions
+3
View File
@@ -691,6 +691,9 @@ class Sheerka(Concept):
return None return None
return new_instances(concept) if return_new else concept return new_instances(concept) if return_new else concept
def fast_get_by_name(self, name):
return self.cache_manager.get(self.CONCEPTS_BY_NAME_ENTRY, name)
def has_id(self, concept_id): def has_id(self, concept_id):
""" """
Returns True if a concept with this id exists in cache Returns True if a concept with this id exists in cache
@@ -21,6 +21,7 @@ class SheerkaCreateNewConcept(BaseService):
def initialize(self): def initialize(self):
self.sheerka.bind_service_method(self.create_new_concept, True) self.sheerka.bind_service_method(self.create_new_concept, True)
self.sheerka.bind_service_method(self.not_is_variable, False, visible=False)
def create_new_concept(self, context, concept: Concept): def create_new_concept(self, context, concept: Concept):
""" """
@@ -108,3 +109,11 @@ class SheerkaCreateNewConcept(BaseService):
refs.add(concept.id) refs.add(concept.id)
return refs return refs
def not_is_variable(self, name):
"""
Given a name tells if it refers to a variable name
:param name:
:return:
"""
return not self.sheerka.cache_manager.get(self.sheerka.CONCEPTS_BY_NAME_ENTRY, name)
+7 -9
View File
@@ -8,7 +8,7 @@ from evaluators.BaseEvaluator import OneReturnValueEvaluator
from parsers.BaseParser import NotInitializedNode from parsers.BaseParser import NotInitializedNode
from parsers.BnfNodeParser import ParsingExpression, ParsingExpressionVisitor from parsers.BnfNodeParser import ParsingExpression, ParsingExpressionVisitor
from parsers.DefConceptParser import DefConceptNode, NameNode from parsers.DefConceptParser import DefConceptNode, NameNode
from parsers.PythonParser import PythonNode from parsers.PythonParser import PythonNode, get_python_node
class ConceptOrRuleNameVisitor(ParsingExpressionVisitor): class ConceptOrRuleNameVisitor(ParsingExpressionVisitor):
@@ -129,15 +129,13 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
This function can only be a draft, as there may be tons of different situations This function can only be a draft, as there may be tons of different situations
I guess that it can only be complete when will we have access to Sheerka memory I guess that it can only be complete when will we have access to Sheerka memory
""" """
# #
# Case of NameNode # Case of NameNode
# #
if isinstance(ret_value, NameNode): if isinstance(ret_value, NameNode):
names = [str(t.value) for t in ret_value.tokens if t.type in ( names = [str(t.value) for t in ret_value.tokens if t.type in (
TokenKind.IDENTIFIER, TokenKind.STRING, TokenKind.KEYWORD)] TokenKind.IDENTIFIER, TokenKind.STRING, TokenKind.KEYWORD)]
variables = filter(lambda x: x in concept_name, names) return set(filter(lambda x: x in concept_name and context.sheerka.not_is_variable(x), names))
return set(variables)
# #
# case of BNF # case of BNF
@@ -150,13 +148,13 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
# #
# Case of python code # Case of python code
# #
if isinstance(ret_value.value, ParserResultConcept) and isinstance(ret_value.value.value, PythonNode): if (python_node := get_python_node(ret_value.value.value)) is not None:
if len(concept_name) > 1: if len(concept_name) > 1:
python_node = ret_value.value.value
visitor = UnreferencedVariablesVisitor(context) visitor = UnreferencedVariablesVisitor(context)
names = visitor.get_names(python_node.ast_) names = visitor.get_names(python_node.ast_)
variables = filter(lambda x: x in concept_name, names) return set(filter(lambda x: x in concept_name and context.sheerka.not_is_variable(x), names))
return set(variables) else:
return set()
# #
# Concept # Concept
@@ -172,4 +170,4 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
variables.add(identifier) variables.add(identifier)
return variables return variables
return [] return set()
+2 -3
View File
@@ -12,7 +12,7 @@ from core.rule import Rule
from core.sheerka.ExecutionContext import ExecutionContext from core.sheerka.ExecutionContext import ExecutionContext
from core.tokenizer import Token, TokenKind from core.tokenizer import Token, TokenKind
from evaluators.BaseEvaluator import OneReturnValueEvaluator from evaluators.BaseEvaluator import OneReturnValueEvaluator
from parsers.PythonParser import PythonNode from parsers.PythonParser import PythonNode, get_python_node
TO_DISABLED = ["breakpoint", "callable", "compile", "delattr", "eval", "exec", "exit", "input", "locals", "open", TO_DISABLED = ["breakpoint", "callable", "compile", "delattr", "eval", "exec", "exit", "input", "locals", "open",
"print", "quit", "setattr"] "print", "quit", "setattr"]
@@ -88,8 +88,7 @@ class PythonEvaluator(OneReturnValueEvaluator):
def eval(self, context, return_value): def eval(self, context, return_value):
sheerka = context.sheerka sheerka = context.sheerka
node = return_value.value.value if isinstance(return_value.value.value, PythonNode) else \ node = get_python_node(return_value.value.value)
return_value.value.value.python_node
debugger = context.get_debugger(PythonEvaluator.NAME, "eval") debugger = context.get_debugger(PythonEvaluator.NAME, "eval")
debugger.debug_entering(node=node) debugger.debug_entering(node=node)
+7 -1
View File
@@ -18,6 +18,12 @@ class ChickenAndEggError(Exception):
concepts: Set[str] concepts: Set[str]
@dataclass
class NoFirstTokenError(ErrorNode):
concept: Concept
key: str
@dataclass() @dataclass()
class LexerNode(Node): class LexerNode(Node):
start: int # starting index in the tokens list start: int # starting index in the tokens list
@@ -891,7 +897,7 @@ class BaseNodeParser(BaseParser):
if keywords is None: if keywords is None:
# no first token found for a concept ? # no first token found for a concept ?
return sheerka.ret(sheerka.name, False, concept) return sheerka.ret(sheerka.name, False, NoFirstTokenError(concept, concept.key))
for keyword in keywords: for keyword in keywords:
res.setdefault(keyword, []).append(concept.id) res.setdefault(keyword, []).append(concept.id)
+8
View File
@@ -11,6 +11,14 @@ from parsers.BaseParser import BaseParser, Node, ErrorNode
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def get_python_node(obj):
if isinstance(obj, PythonNode):
return obj
if hasattr(obj, "python_node"):
return obj.python_node
return None
@dataclass() @dataclass()
class PythonErrorNode(ErrorNode): class PythonErrorNode(ErrorNode):
source: str source: str
+35 -22
View File
@@ -6,8 +6,8 @@ from core.concept import VARIABLE_PREFIX, Concept, DEFINITION_TYPE_BNF, DEFINITI
from core.tokenizer import Tokenizer from core.tokenizer import Tokenizer
from evaluators.DefConceptEvaluator import DefConceptEvaluator from evaluators.DefConceptEvaluator import DefConceptEvaluator
from parsers.BaseParser import BaseParser from parsers.BaseParser import BaseParser
from parsers.BnfNodeParser import Sequence, StrMatch, ZeroOrMore, ConceptExpression
from parsers.BnfDefinitionParser import BnfDefinitionParser from parsers.BnfDefinitionParser import BnfDefinitionParser
from parsers.BnfNodeParser import Sequence, StrMatch, ZeroOrMore, ConceptExpression
from parsers.DefConceptParser import DefConceptNode, NameNode from parsers.DefConceptParser import DefConceptNode, NameNode
from parsers.PythonParser import PythonNode, PythonParser from parsers.PythonParser import PythonNode, PythonParser
@@ -76,6 +76,10 @@ class TestDefConceptEvaluator(TestUsingMemoryBasedSheerka):
return ReturnValueConcept(BaseParser.PREFIX + "some_name", True, ParserResultConcept(value=def_concept)) 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", [ @pytest.mark.parametrize("ret_val, expected", [
(ReturnValueConcept(BaseParser.PREFIX + "some_name", True, ParserResultConcept(value=DefConceptNode([]))), (ReturnValueConcept(BaseParser.PREFIX + "some_name", True, ParserResultConcept(value=DefConceptNode([]))),
True), True),
@@ -88,7 +92,7 @@ class TestDefConceptEvaluator(TestUsingMemoryBasedSheerka):
context = self.get_context() context = self.get_context()
assert DefConceptEvaluator().matches(context, ret_val) == expected assert DefConceptEvaluator().matches(context, ret_val) == expected
def test_that_the_source_is_correctly_set_for_bnf_concept(self): def test_that_the_source_is_correctly_set_for_bnf_concepts(self):
context = self.get_context() context = self.get_context()
def_concept_return_value = self.get_def_concept( def_concept_return_value = self.get_def_concept(
name="hello a", name="hello a",
@@ -114,22 +118,7 @@ class TestDefConceptEvaluator(TestUsingMemoryBasedSheerka):
assert created_concept.get_metadata().definition == "hello a" assert created_concept.get_metadata().definition == "hello a"
assert created_concept.get_metadata().definition_type == "bnf" assert created_concept.get_metadata().definition_type == "bnf"
def test_i_can_add_concept_with_the_correct_variables_when_referencing_other_concepts(self): 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="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_that_the_source_is_correctly_set_for_concept_with_simple_definition(self):
context = self.get_context() context = self.get_context()
def_concept_return_value = self.get_def_concept( def_concept_return_value = self.get_def_concept(
name="greetings", name="greetings",
@@ -153,6 +142,30 @@ class TestDefConceptEvaluator(TestUsingMemoryBasedSheerka):
assert created_concept.get_metadata().definition == "hello a" assert created_concept.get_metadata().definition == "hello a"
assert created_concept.get_metadata().definition_type == "def" 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_concepts("little", "size", create_new=True)
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): def test_that_the_new_concept_is_correctly_saved_in_db(self):
context = self.get_context() context = self.get_context()
def_concept_return_value = self.get_def_concept( def_concept_return_value = self.get_def_concept(
@@ -195,7 +208,7 @@ class TestDefConceptEvaluator(TestUsingMemoryBasedSheerka):
ret_val = self.get_concept_part(expression) ret_val = self.get_concept_part(expression)
context = self.get_context() context = self.get_context()
assert DefConceptEvaluator.get_variables(context.sheerka, ret_val, name.split()) == expected assert DefConceptEvaluator.get_variables(context, ret_val, name.split()) == expected
def test_i_can_get_variables_when_keywords(self): def test_i_can_get_variables_when_keywords(self):
sheerka, context = self.init_concepts() sheerka, context = self.init_concepts()
@@ -204,20 +217,20 @@ class TestDefConceptEvaluator(TestUsingMemoryBasedSheerka):
name_to_use = DefConceptEvaluator.get_name_to_use(def_concept) name_to_use = DefConceptEvaluator.get_name_to_use(def_concept)
concept_part = self.get_concept_part("pre") concept_part = self.get_concept_part("pre")
assert DefConceptEvaluator.get_variables(context.sheerka, concept_part, name_to_use) == {"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): 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)") ret_val = self.get_concept_part("isinstance(a, str)")
context = self.get_context() context = self.get_context()
assert DefConceptEvaluator.get_variables(context.sheerka, ret_val, ["a"]) == [] assert DefConceptEvaluator.get_variables(context, ret_val, ["a"]) == set()
def test_i_can_get_variables_from_definition(self): def test_i_can_get_variables_from_definition(self):
parsing_expression = Sequence(ConceptExpression('mult'), parsing_expression = Sequence(ConceptExpression('mult'),
ZeroOrMore(Sequence(StrMatch("+"), ConceptExpression("add")))) ZeroOrMore(Sequence(StrMatch("+"), ConceptExpression("add"))))
ret_val = self.get_return_value("mult (('+'|'-') add)?", parsing_expression) ret_val = self.get_return_value("mult (('+'|'-') add)?", parsing_expression)
assert DefConceptEvaluator.get_variables(self.get_sheerka(), ret_val, []) == {"add", "mult"} assert DefConceptEvaluator.get_variables(self.get_context(), ret_val, []) == {"add", "mult"}
def test_concept_that_references_itself_is_correctly_created(self): def test_concept_that_references_itself_is_correctly_created(self):
context = self.get_context() context = self.get_context()
+8 -1
View File
@@ -1,6 +1,6 @@
import pytest import pytest
from core.concept import Concept from core.concept import Concept
from parsers.BaseNodeParser import BaseNodeParser from parsers.BaseNodeParser import BaseNodeParser, NoFirstTokenError
from parsers.BnfNodeParser import StrMatch, Sequence, OrderedChoice, Optional, ZeroOrMore, OneOrMore, ConceptExpression from parsers.BnfNodeParser import StrMatch, Sequence, OrderedChoice, Optional, ZeroOrMore, OneOrMore, ConceptExpression
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
@@ -115,6 +115,13 @@ class TestBaseNodeParser(TestUsingMemoryBasedSheerka):
"qux": ["1005"], "qux": ["1005"],
} }
def test_i_cannot_get_concept_by_first_keyword_when_no_first_keyword(self):
sheerka, context, foo = self.init_concepts(Concept("x y", body="x y").def_var("x").def_var("y"))
res = BaseNodeParser.get_concepts_by_first_token(context, [foo])
assert not res.status
assert res.body == NoFirstTokenError(foo, foo.key)
def test_i_can_resolve_concepts_by_first_keyword(self): def test_i_can_resolve_concepts_by_first_keyword(self):
sheerka, context, *updated = self.init_concepts( sheerka, context, *updated = self.init_concepts(
"one", "one",