Added concept 'isa' other_concept functionality
This commit is contained in:
@@ -45,6 +45,7 @@ class BuiltinConcepts(Enum):
|
|||||||
ENUMERATION = "enum" # represents a list or a set
|
ENUMERATION = "enum" # represents a list or a set
|
||||||
LIST = "list" # represents a list
|
LIST = "list" # represents a list
|
||||||
CANNOT_RESOLVE_VALUE_ERROR = "value cannot be resolved" # don't know how to find concept value
|
CANNOT_RESOLVE_VALUE_ERROR = "value cannot be resolved" # don't know how to find concept value
|
||||||
|
CONCEPT_ALREADY_IN_SET = "concept already in set"
|
||||||
|
|
||||||
NODE = "node"
|
NODE = "node"
|
||||||
GENERIC_NODE = "generic node"
|
GENERIC_NODE = "generic node"
|
||||||
@@ -68,6 +69,7 @@ BuiltinErrors = [str(e) for e in {
|
|||||||
BuiltinConcepts.CONCEPT_ALREADY_DEFINED,
|
BuiltinConcepts.CONCEPT_ALREADY_DEFINED,
|
||||||
BuiltinConcepts.CONCEPT_EVAL_ERROR,
|
BuiltinConcepts.CONCEPT_EVAL_ERROR,
|
||||||
BuiltinConcepts.CANNOT_RESOLVE_VALUE_ERROR,
|
BuiltinConcepts.CANNOT_RESOLVE_VALUE_ERROR,
|
||||||
|
BuiltinConcepts.CONCEPT_ALREADY_IN_SET,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -320,3 +322,24 @@ class ListConcept(Concept):
|
|||||||
|
|
||||||
def __contains__(self, item):
|
def __contains__(self, item):
|
||||||
return item in self.body
|
return item in self.body
|
||||||
|
|
||||||
|
|
||||||
|
class ConceptAlreadyInSet(Concept):
|
||||||
|
def __init__(self, concept=None, concept_set=None):
|
||||||
|
super().__init__(BuiltinConcepts.CONCEPT_ALREADY_IN_SET,
|
||||||
|
True,
|
||||||
|
False,
|
||||||
|
BuiltinConcepts.CONCEPT_ALREADY_IN_SET,
|
||||||
|
concept)
|
||||||
|
self.set_prop("concept_set", concept_set)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"ConceptAlreadyInSet(concept={self.concept}, concept_set={self.concept_set})"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def concept(self):
|
||||||
|
return self.body
|
||||||
|
|
||||||
|
@property
|
||||||
|
def concept_set(self):
|
||||||
|
return self.props["concept_set"].value
|
||||||
|
|||||||
+35
-1
@@ -15,6 +15,7 @@ CONCEPT_EVALUATION_STEPS = [
|
|||||||
BuiltinConcepts.BEFORE_EVALUATION,
|
BuiltinConcepts.BEFORE_EVALUATION,
|
||||||
BuiltinConcepts.EVALUATION,
|
BuiltinConcepts.EVALUATION,
|
||||||
BuiltinConcepts.AFTER_EVALUATION]
|
BuiltinConcepts.AFTER_EVALUATION]
|
||||||
|
|
||||||
CONCEPT_LEXER_PARSER_CLASS = "parsers.ConceptLexerParser.ConceptLexerParser"
|
CONCEPT_LEXER_PARSER_CLASS = "parsers.ConceptLexerParser.ConceptLexerParser"
|
||||||
DEBUG_TAB_SIZE = 4
|
DEBUG_TAB_SIZE = 4
|
||||||
|
|
||||||
@@ -353,14 +354,17 @@ class Sheerka(Concept):
|
|||||||
obj.metadata.id = self.sdp.get_next_key(self.BUILTIN_CONCEPTS_KEYS if is_builtin else self.USER_CONCEPTS_KEYS)
|
obj.metadata.id = self.sdp.get_next_key(self.BUILTIN_CONCEPTS_KEYS if is_builtin else self.USER_CONCEPTS_KEYS)
|
||||||
self.log.debug(f"Setting id '{obj.metadata.id}' to concept '{obj.metadata.name}'.")
|
self.log.debug(f"Setting id '{obj.metadata.id}' to concept '{obj.metadata.name}'.")
|
||||||
|
|
||||||
def create_new_concept(self, context, concept: Concept):
|
def create_new_concept(self, context, concept: Concept, logger=None):
|
||||||
"""
|
"""
|
||||||
Adds a new concept to the system
|
Adds a new concept to the system
|
||||||
:param context:
|
:param context:
|
||||||
:param concept: DefConceptNode
|
:param concept: DefConceptNode
|
||||||
|
:param logger
|
||||||
:return: digest of the new concept
|
:return: digest of the new concept
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
logger = logger or self.log
|
||||||
|
|
||||||
concept.init_key()
|
concept.init_key()
|
||||||
concepts_definitions = None
|
concepts_definitions = None
|
||||||
init_ret_value = None
|
init_ret_value = None
|
||||||
@@ -393,6 +397,7 @@ class Sheerka(Concept):
|
|||||||
if concepts_definitions is not None:
|
if concepts_definitions is not None:
|
||||||
self.sdp.set(context.event_digest, self.CONCEPTS_DEFINITIONS_ENTRY, concepts_definitions, use_ref=True)
|
self.sdp.set(context.event_digest, self.CONCEPTS_DEFINITIONS_ENTRY, concepts_definitions, use_ref=True)
|
||||||
except SheerkaDataProviderDuplicateKeyError as error:
|
except SheerkaDataProviderDuplicateKeyError as error:
|
||||||
|
context.log_error(logger, "Failed to create a new concept.", who=self.create_new_concept.__name__)
|
||||||
return self.ret(self.create_new_concept.__name__, False, ErrorConcept(error), error.args[0])
|
return self.ret(self.create_new_concept.__name__, False, ErrorConcept(error), error.args[0])
|
||||||
|
|
||||||
# Updates the caches
|
# Updates the caches
|
||||||
@@ -404,6 +409,35 @@ class Sheerka(Concept):
|
|||||||
ret = self.ret(self.create_new_concept.__name__, True, self.new(BuiltinConcepts.NEW_CONCEPT, body=concept))
|
ret = self.ret(self.create_new_concept.__name__, True, self.new(BuiltinConcepts.NEW_CONCEPT, body=concept))
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def add_concept_to_set(self, context, concept, concept_set, logger=None):
|
||||||
|
"""
|
||||||
|
Add an entry in sdp to tell that concept isa concept_set
|
||||||
|
:param context:
|
||||||
|
:param concept:
|
||||||
|
:param concept_set:
|
||||||
|
:param logger:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
logger = logger or self.log
|
||||||
|
|
||||||
|
context.log(logger, f"Adding concept {concept} to set {concept_set}", who=self.add_concept_to_set.__name__)
|
||||||
|
|
||||||
|
assert concept.id
|
||||||
|
assert concept_set.id
|
||||||
|
|
||||||
|
try:
|
||||||
|
ret = self.sdp.add_unique(context.event_digest, "All_" + str(concept_set.id), concept.id)
|
||||||
|
if ret == (None, None): # concept already in set
|
||||||
|
return self.ret(
|
||||||
|
self.add_concept_to_set.__name__,
|
||||||
|
False,
|
||||||
|
self.new(BuiltinConcepts.CONCEPT_ALREADY_IN_SET, body=concept, concept_set=concept_set))
|
||||||
|
else:
|
||||||
|
return self.ret(self.add_concept_to_set.__name__, True, self.new(BuiltinConcepts.SUCCESS))
|
||||||
|
except Exception as error:
|
||||||
|
context.log_error(logger, "Failed to add to set.", who=self.add_concept_to_set.__name__)
|
||||||
|
return self.ret(self.create_new_concept.__name__, False, ErrorConcept(error), error.args[0])
|
||||||
|
|
||||||
def initialize_concept_asts(self, context, concept: Concept, logger=None):
|
def initialize_concept_asts(self, context, concept: Concept, logger=None):
|
||||||
"""
|
"""
|
||||||
Updates the codes of the newly created concept
|
Updates the codes of the newly created concept
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ class Keywords(Enum):
|
|||||||
WHERE = "where"
|
WHERE = "where"
|
||||||
PRE = "pre"
|
PRE = "pre"
|
||||||
POST = "post"
|
POST = "post"
|
||||||
|
ISA = "isa"
|
||||||
|
|
||||||
|
|
||||||
class Tokenizer:
|
class Tokenizer:
|
||||||
|
|||||||
+58
-2
@@ -81,8 +81,8 @@ So, you could call the concept by
|
|||||||
|
|
||||||
They will produce the strings "hello kodjo" or "hello my friend"
|
They will produce the strings "hello kodjo" or "hello my friend"
|
||||||
|
|
||||||
About versioning
|
About versionning of the information
|
||||||
""""""""""""""""
|
"""""""""""""""""""""""""""""""""""""
|
||||||
As I said previously, I mimic how git_ versions its objects.
|
As I said previously, I mimic how git_ versions its objects.
|
||||||
|
|
||||||
::
|
::
|
||||||
@@ -598,3 +598,59 @@ For those who doesn't know that BNF stands for, please have a look at the bnf_
|
|||||||
wikipedia page.
|
wikipedia page.
|
||||||
|
|
||||||
I guess that I will need a complete chapter to explain how you retrieve what was parsed
|
I guess that I will need a complete chapter to explain how you retrieve what was parsed
|
||||||
|
|
||||||
|
2019-12-21
|
||||||
|
**********
|
||||||
|
|
||||||
|
Implementing Inheritance
|
||||||
|
""""""""""""""""""""""""
|
||||||
|
|
||||||
|
Except that it is not inheritance, at least the way it is seen in modern programing languages.
|
||||||
|
|
||||||
|
I think that I should first express what I am trying to do. I guess that it will help me
|
||||||
|
have a better understanding myself.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
def concept one as 1
|
||||||
|
def concept two as 2
|
||||||
|
one is a number
|
||||||
|
two is a number
|
||||||
|
|
||||||
|
When I enter :code:`one`, the result should be :code:`1`
|
||||||
|
|
||||||
|
But I should be able to express other concepts by using
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
def concept a plus b where a is a number and b is a number as a + b
|
||||||
|
|
||||||
|
Just by reading what I have just written, we can see that 'is a' has two separate meanings.
|
||||||
|
In the first usage, it's an affirmation, in the latter one, it's a question.
|
||||||
|
|
||||||
|
Should we consider them as the same concept, with two usages, or as two separate concepts,
|
||||||
|
which are somehow linked ?
|
||||||
|
|
||||||
|
As of now, there is only one usage to all concepts, which is the property 'BODY', but I have
|
||||||
|
prepared the property 'PRE' which can be used for that.
|
||||||
|
|
||||||
|
I am a little bit making a digression. The original subject was on how I can express that a
|
||||||
|
concept is an element of another concept. We may focus on the implementation later.
|
||||||
|
|
||||||
|
So saying that 'one' is a 'number' means that there is a set called 'number'
|
||||||
|
in which 'one' belong.
|
||||||
|
|
||||||
|
The simple implementation will be to create an entry 'all_number' in sdp and to add 'one' in it.
|
||||||
|
The two issue that I foresee are:
|
||||||
|
|
||||||
|
* What about infinite sets ? (my set 'number' can never be completed if I put the item one by one)
|
||||||
|
* What if the same name refers to different set (I don't have any example in mind, but I guess that synonyms of sets do exist)
|
||||||
|
|
||||||
|
|
||||||
|
For the two questions, I will first try the simple implementations and see there I go from there. ie :
|
||||||
|
|
||||||
|
* on the top of the entry all_numbers which lists the known numbers, you can define concepts :code:`is a number`
|
||||||
|
that can be also used to detect the the concept is part of the set
|
||||||
|
* the entry in sdp will not be all_number, but all_id_of_number. I will use the concept id instead of its name
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ class AddConceptEvaluator(OneReturnValueEvaluator):
|
|||||||
sheerka.is_success(def_concept_node.definition):
|
sheerka.is_success(def_concept_node.definition):
|
||||||
concept.bnf = def_concept_node.definition.value.value
|
concept.bnf = def_concept_node.definition.value.value
|
||||||
|
|
||||||
ret = sheerka.create_new_concept(context, concept)
|
ret = sheerka.create_new_concept(context, concept, self.verbose_log)
|
||||||
if not ret.status:
|
if not ret.status:
|
||||||
error_cause = sheerka.value(ret.body)
|
error_cause = sheerka.value(ret.body)
|
||||||
context.log(self.log, f"Failed to add concept '{concept.name}'. Reason: {error_cause}", self.name)
|
context.log(self.log, f"Failed to add concept '{concept.name}'. Reason: {error_cause}", self.name)
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import core.builtin_helpers
|
||||||
|
from core.builtin_concepts import ParserResultConcept, BuiltinConcepts
|
||||||
|
from evaluators.BaseEvaluator import OneReturnValueEvaluator
|
||||||
|
from parsers.DefaultParser import IsaConceptNode
|
||||||
|
|
||||||
|
ALL_STEPS = [
|
||||||
|
BuiltinConcepts.BEFORE_PARSING,
|
||||||
|
BuiltinConcepts.PARSING,
|
||||||
|
BuiltinConcepts.EVALUATION,
|
||||||
|
BuiltinConcepts.AFTER_EVALUATION
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class AddConceptInSetEvaluator(OneReturnValueEvaluator):
|
||||||
|
"""
|
||||||
|
Tells that a concept is a part of a set
|
||||||
|
"""
|
||||||
|
NAME = "AddConceptInSet"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(self.NAME, 50)
|
||||||
|
|
||||||
|
def matches(self, context, return_value):
|
||||||
|
return return_value.status and \
|
||||||
|
isinstance(return_value.value, ParserResultConcept) and \
|
||||||
|
isinstance(return_value.value.value, IsaConceptNode)
|
||||||
|
|
||||||
|
def eval(self, context, return_value):
|
||||||
|
|
||||||
|
def _resolve(name_node):
|
||||||
|
ret_val = sheerka.ret(
|
||||||
|
self.name,
|
||||||
|
True,
|
||||||
|
sheerka.new(BuiltinConcepts.USER_INPUT, body=name_node.tokens, user_name="N/A"))
|
||||||
|
sub_context = context.push(desc=f"Recognizing '{name_node}'")
|
||||||
|
r = sheerka.execute(sub_context, ret_val, ALL_STEPS, self.verbose_log)
|
||||||
|
return core.builtin_helpers.expect_one(context, r)
|
||||||
|
|
||||||
|
isa_node = return_value.value.value
|
||||||
|
sheerka = context.sheerka
|
||||||
|
context.log(self.log, f"Adding a concept {isa_node.concept} to set {isa_node.set}", self.name)
|
||||||
|
|
||||||
|
# Try to recognize the concept
|
||||||
|
res = _resolve(isa_node.concept)
|
||||||
|
if not res.status:
|
||||||
|
return sheerka.ret(
|
||||||
|
self.name,
|
||||||
|
False,
|
||||||
|
sheerka.new(BuiltinConcepts.UNKNOWN_CONCEPT, body=str(isa_node.concept)),
|
||||||
|
parents=[return_value])
|
||||||
|
concept = res.value
|
||||||
|
|
||||||
|
res = _resolve(isa_node.set)
|
||||||
|
if not res.status:
|
||||||
|
return sheerka.ret(
|
||||||
|
self.name,
|
||||||
|
False,
|
||||||
|
sheerka.new(BuiltinConcepts.UNKNOWN_CONCEPT, body=str(isa_node.set)),
|
||||||
|
parents=[return_value])
|
||||||
|
concept_set = res.value
|
||||||
|
|
||||||
|
res = sheerka.add_concept_to_set(context, concept, concept_set, self.verbose_log)
|
||||||
|
if not res.status:
|
||||||
|
context.log(self.log, f"Failed. Reason: {sheerka.value(res.body)}.", self.name)
|
||||||
|
else:
|
||||||
|
context.log(self.log, f"Concept added.", self.name)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
@@ -96,6 +96,12 @@ class DefConceptNode(DefaultParserNode):
|
|||||||
return asts
|
return asts
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass()
|
||||||
|
class IsaConceptNode(DefaultParserNode):
|
||||||
|
concept: NameNode = NotInitializedNode()
|
||||||
|
set: NameNode = NotInitializedNode()
|
||||||
|
|
||||||
|
|
||||||
class DefaultParser(BaseParser):
|
class DefaultParser(BaseParser):
|
||||||
"""
|
"""
|
||||||
Parse sheerka specific grammar (like def concept)
|
Parse sheerka specific grammar (like def concept)
|
||||||
@@ -220,7 +226,7 @@ class DefaultParser(BaseParser):
|
|||||||
self.context.log(self.verbose_log, "Keyword DEF found.", self.name)
|
self.context.log(self.verbose_log, "Keyword DEF found.", self.name)
|
||||||
return self.parse_def_concept(token)
|
return self.parse_def_concept(token)
|
||||||
else:
|
else:
|
||||||
return self.add_error(CannotHandleErrorNode([], self.text))
|
return self.parse_isa_concept()
|
||||||
|
|
||||||
def parse_def_concept(self, def_token):
|
def parse_def_concept(self, def_token):
|
||||||
"""
|
"""
|
||||||
@@ -257,6 +263,35 @@ class DefaultParser(BaseParser):
|
|||||||
|
|
||||||
return concept_found
|
return concept_found
|
||||||
|
|
||||||
|
def parse_isa_concept(self):
|
||||||
|
concept_name = self.parse_concept_name()
|
||||||
|
if isinstance(concept_name, DefaultParserErrorNode):
|
||||||
|
return concept_name
|
||||||
|
|
||||||
|
keyword = []
|
||||||
|
token = self.get_token()
|
||||||
|
if token.value != Keywords.ISA:
|
||||||
|
return self.add_error(CannotHandleErrorNode([token], ""))
|
||||||
|
keyword.append(token)
|
||||||
|
self.next_token()
|
||||||
|
|
||||||
|
set_name = self.parse_concept_name()
|
||||||
|
return IsaConceptNode(keyword, concept_name, set_name)
|
||||||
|
|
||||||
|
def parse_concept_name(self):
|
||||||
|
tokens = []
|
||||||
|
token = self.get_token()
|
||||||
|
|
||||||
|
while not (token.type == TokenKind.EOF or token.type == TokenKind.KEYWORD):
|
||||||
|
tokens.append(token)
|
||||||
|
self.next_token()
|
||||||
|
token = self.get_token()
|
||||||
|
|
||||||
|
if len(tokens) == 0:
|
||||||
|
return self.add_error(UnexpectedTokenErrorNode([token], "Unexpected token", []))
|
||||||
|
else:
|
||||||
|
return NameNode(tokens)
|
||||||
|
|
||||||
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]
|
||||||
|
|||||||
@@ -407,12 +407,15 @@ class SheerkaDataProvider:
|
|||||||
state.date = datetime.now()
|
state.date = datetime.now()
|
||||||
if entry not in state.data:
|
if entry not in state.data:
|
||||||
state.data[entry] = {obj}
|
state.data[entry] = {obj}
|
||||||
|
already_exist = False
|
||||||
else:
|
else:
|
||||||
state.data[entry].add(obj)
|
already_exist = obj in state.data[entry]
|
||||||
|
if not already_exist:
|
||||||
|
state.data[entry].add(obj)
|
||||||
|
|
||||||
new_snapshot = self.save_state(state)
|
new_snapshot = self.save_state(state)
|
||||||
self.set_snapshot(new_snapshot)
|
self.set_snapshot(new_snapshot)
|
||||||
return entry, None
|
return (None if already_exist else entry), None
|
||||||
|
|
||||||
def set(self, event_digest, entry, obj, use_ref=False):
|
def set(self, event_digest, entry, obj, use_ref=False):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
|
||||||
|
from core.concept import Concept
|
||||||
|
from core.sheerka import Sheerka, ExecutionContext
|
||||||
|
from core.tokenizer import Tokenizer
|
||||||
|
from evaluators.AddConceptInSetEvaluator import AddConceptInSetEvaluator
|
||||||
|
from parsers.DefaultParser import IsaConceptNode, NameNode
|
||||||
|
|
||||||
|
|
||||||
|
def get_context():
|
||||||
|
sheerka = Sheerka(skip_builtins_in_db=True)
|
||||||
|
sheerka.initialize("mem://")
|
||||||
|
return ExecutionContext("test", "xxx", sheerka)
|
||||||
|
|
||||||
|
|
||||||
|
def get_ret_val(concept_name, concept_set_name):
|
||||||
|
n1 = NameNode(list(Tokenizer(concept_name)))
|
||||||
|
n2 = NameNode(list(Tokenizer(concept_set_name)))
|
||||||
|
|
||||||
|
return ReturnValueConcept("some_name", True, ParserResultConcept(value=IsaConceptNode([], n1, n2)))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("ret_val, expected", [
|
||||||
|
(ReturnValueConcept("some_name", True, ParserResultConcept(value=IsaConceptNode([]))), True),
|
||||||
|
(ReturnValueConcept("some_name", False, ParserResultConcept(value=IsaConceptNode([]))), False),
|
||||||
|
(ReturnValueConcept("some_name", True, "not a ParserResultConcept"), False),
|
||||||
|
(ReturnValueConcept("some_name", True, ParserResultConcept()), False),
|
||||||
|
])
|
||||||
|
def test_i_can_match(ret_val, expected):
|
||||||
|
context = get_context()
|
||||||
|
assert AddConceptInSetEvaluator().matches(context, ret_val) == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_i_cannot_add_if_the_concept_does_not_exists():
|
||||||
|
context = get_context()
|
||||||
|
|
||||||
|
ret_val = get_ret_val("foo", "bar")
|
||||||
|
res = AddConceptInSetEvaluator().eval(context, ret_val)
|
||||||
|
|
||||||
|
assert not res.status
|
||||||
|
assert context.sheerka.isinstance(res.value, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||||
|
assert res.value.body == "foo"
|
||||||
|
|
||||||
|
|
||||||
|
def test_i_cannot_add_if_the_set_does_not_exists():
|
||||||
|
context = get_context()
|
||||||
|
foo = Concept("foo")
|
||||||
|
context.sheerka.set_id_if_needed(foo, False)
|
||||||
|
context.sheerka.add_in_cache(foo)
|
||||||
|
|
||||||
|
ret_val = get_ret_val("foo", "bar")
|
||||||
|
res = AddConceptInSetEvaluator().eval(context, ret_val)
|
||||||
|
|
||||||
|
assert not res.status
|
||||||
|
assert context.sheerka.isinstance(res.value, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||||
|
assert res.value.body == "bar"
|
||||||
|
|
||||||
|
|
||||||
|
def test_i_can_add_concept_to_a_set_of_concept():
|
||||||
|
context = get_context()
|
||||||
|
foo = Concept("foo")
|
||||||
|
context.sheerka.set_id_if_needed(foo, False)
|
||||||
|
context.sheerka.add_in_cache(foo)
|
||||||
|
|
||||||
|
bar = Concept("bar")
|
||||||
|
context.sheerka.set_id_if_needed(bar, False)
|
||||||
|
context.sheerka.add_in_cache(bar)
|
||||||
|
|
||||||
|
ret_val = get_ret_val("foo", "bar")
|
||||||
|
res = AddConceptInSetEvaluator().eval(context, ret_val)
|
||||||
|
|
||||||
|
assert res.status
|
||||||
|
assert context.sheerka.isinstance(res.value, BuiltinConcepts.SUCCESS)
|
||||||
|
|
||||||
|
|
||||||
|
def test_i_cannot_add_the_same_concept_twice():
|
||||||
|
context = get_context()
|
||||||
|
foo = Concept("foo")
|
||||||
|
context.sheerka.set_id_if_needed(foo, False)
|
||||||
|
context.sheerka.add_in_cache(foo)
|
||||||
|
|
||||||
|
bar = Concept("bar")
|
||||||
|
context.sheerka.set_id_if_needed(bar, False)
|
||||||
|
context.sheerka.add_in_cache(bar)
|
||||||
|
|
||||||
|
ret_val = get_ret_val("foo", "bar")
|
||||||
|
AddConceptInSetEvaluator().eval(context, ret_val)
|
||||||
|
res = AddConceptInSetEvaluator().eval(context, ret_val)
|
||||||
|
|
||||||
|
assert not res.status
|
||||||
|
assert context.sheerka.isinstance(res.value, BuiltinConcepts.CONCEPT_ALREADY_IN_SET)
|
||||||
|
assert res.value.concept == foo
|
||||||
|
assert res.value.concept_set == bar
|
||||||
+53
-24
@@ -6,7 +6,7 @@ from core.sheerka import Sheerka, ExecutionContext
|
|||||||
from parsers.ConceptLexerParser import OrderedChoice, StrMatch, ConceptMatch
|
from parsers.ConceptLexerParser import OrderedChoice, StrMatch, ConceptMatch
|
||||||
from parsers.PythonParser import PythonParser, PythonNode
|
from parsers.PythonParser import PythonParser, PythonNode
|
||||||
from core.tokenizer import Keywords, Tokenizer
|
from core.tokenizer import Keywords, Tokenizer
|
||||||
from parsers.DefaultParser import DefaultParser, NameNode, SyntaxErrorNode, CannotHandleErrorNode
|
from parsers.DefaultParser import DefaultParser, NameNode, SyntaxErrorNode, CannotHandleErrorNode, IsaConceptNode
|
||||||
from parsers.DefaultParser import UnexpectedTokenErrorNode, DefConceptNode
|
from parsers.DefaultParser import UnexpectedTokenErrorNode, DefConceptNode
|
||||||
from parsers.BnfParser import BnfParser
|
from parsers.BnfParser import BnfParser
|
||||||
|
|
||||||
@@ -55,24 +55,24 @@ from parsers.BnfParser import BnfParser
|
|||||||
# return left_as_string == right_as_string
|
# return left_as_string == right_as_string
|
||||||
#
|
#
|
||||||
|
|
||||||
def get_concept(name, where=None, pre=None, post=None, body=None, definition=None):
|
def get_def_concept(name, where=None, pre=None, post=None, body=None, definition=None):
|
||||||
concept = DefConceptNode([], name=NameNode(list(Tokenizer(name))))
|
def_concept = DefConceptNode([], name=NameNode(list(Tokenizer(name))))
|
||||||
|
|
||||||
if body:
|
if body:
|
||||||
concept.body = get_concept_part(body)
|
def_concept.body = get_concept_part(body)
|
||||||
if where:
|
if where:
|
||||||
concept.where = get_concept_part(where)
|
def_concept.where = get_concept_part(where)
|
||||||
if pre:
|
if pre:
|
||||||
concept.pre = get_concept_part(pre)
|
def_concept.pre = get_concept_part(pre)
|
||||||
if post:
|
if post:
|
||||||
concept.post = get_concept_part(post)
|
def_concept.post = get_concept_part(post)
|
||||||
if definition:
|
if definition:
|
||||||
concept.definition = ReturnValueConcept(
|
def_concept.definition = ReturnValueConcept(
|
||||||
"parsers.Bnf",
|
"parsers.Bnf",
|
||||||
True,
|
True,
|
||||||
definition)
|
definition)
|
||||||
|
|
||||||
return concept
|
return def_concept
|
||||||
|
|
||||||
|
|
||||||
def get_context():
|
def get_context():
|
||||||
@@ -145,16 +145,16 @@ def get_concept_part(part):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("text, expected", [
|
@pytest.mark.parametrize("text, expected", [
|
||||||
("def concept hello", get_concept(name="hello")),
|
("def concept hello", get_def_concept(name="hello")),
|
||||||
("def concept hello ", get_concept(name="hello")),
|
("def concept hello ", get_def_concept(name="hello")),
|
||||||
("def concept a + b", get_concept(name="a + b")),
|
("def concept a + b", get_def_concept(name="a + b")),
|
||||||
("def concept a+b", get_concept(name="a + b")),
|
("def concept a+b", get_def_concept(name="a + b")),
|
||||||
("def concept 'a+b'+c", get_concept(name="'a+b' + c")),
|
("def concept 'a+b'+c", get_def_concept(name="'a+b' + c")),
|
||||||
("def concept 'as if'", get_concept(name="'as if'")),
|
("def concept 'as if'", get_def_concept(name="'as if'")),
|
||||||
("def concept 'as' if", get_concept(name="'as if'")),
|
("def concept 'as' if", get_def_concept(name="'as if'")),
|
||||||
("def concept hello as 'hello'", get_concept(name="hello", body="'hello'")),
|
("def concept hello as 'hello'", get_def_concept(name="hello", body="'hello'")),
|
||||||
("def concept hello as 1", get_concept(name="hello", body="1")),
|
("def concept hello as 1", get_def_concept(name="hello", body="1")),
|
||||||
("def concept hello as 1 + 1", get_concept(name="hello", body="1 + 1")),
|
("def concept hello as 1 + 1", get_def_concept(name="hello", body="1 + 1")),
|
||||||
])
|
])
|
||||||
def test_i_can_parse_def_concept(text, expected):
|
def test_i_can_parse_def_concept(text, expected):
|
||||||
parser = DefaultParser()
|
parser = DefaultParser()
|
||||||
@@ -178,7 +178,7 @@ as res = a + b
|
|||||||
parser = DefaultParser()
|
parser = DefaultParser()
|
||||||
res = parser.parse(get_context(), text)
|
res = parser.parse(get_context(), text)
|
||||||
return_value = res.value
|
return_value = res.value
|
||||||
expected_concept = get_concept(
|
expected_concept = get_def_concept(
|
||||||
name="a plus b",
|
name="a plus b",
|
||||||
where="a,b",
|
where="a,b",
|
||||||
pre="isinstance(a, int) and isinstance(b, float)",
|
pre="isinstance(a, int) and isinstance(b, float)",
|
||||||
@@ -199,7 +199,7 @@ def func(x):
|
|||||||
func(a)
|
func(a)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
expected_concept = get_concept(
|
expected_concept = get_def_concept(
|
||||||
name="add one to a ",
|
name="add one to a ",
|
||||||
body=PythonNode(
|
body=PythonNode(
|
||||||
"def func(x):\n return x+1\nfunc(a)",
|
"def func(x):\n return x+1\nfunc(a)",
|
||||||
@@ -223,7 +223,7 @@ def concept add one to a as:
|
|||||||
func(a)
|
func(a)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
expected_concept = get_concept(
|
expected_concept = get_def_concept(
|
||||||
name="add one to a ",
|
name="add one to a ",
|
||||||
body=PythonNode(
|
body=PythonNode(
|
||||||
"def func(x):\n return x+1\nfunc(a)",
|
"def func(x):\n return x+1\nfunc(a)",
|
||||||
@@ -292,7 +292,7 @@ def test_name_is_mandatory():
|
|||||||
def test_concept_keyword_is_mandatory_but_the_concept_is_recognized():
|
def test_concept_keyword_is_mandatory_but_the_concept_is_recognized():
|
||||||
text = "def hello as a where b pre c post d"
|
text = "def hello as a where b pre c post d"
|
||||||
|
|
||||||
expected_concept = get_concept(name="hello", body="a", where="b", pre="c", post="d")
|
expected_concept = get_def_concept(name="hello", body="a", where="b", pre="c", post="d")
|
||||||
parser = DefaultParser()
|
parser = DefaultParser()
|
||||||
res = parser.parse(get_context(), text)
|
res = parser.parse(get_context(), text)
|
||||||
return_value = res.value
|
return_value = res.value
|
||||||
@@ -342,7 +342,7 @@ def test_i_can_parse_def_concept_from_regex():
|
|||||||
node = res.value.value
|
node = res.value.value
|
||||||
definition = OrderedChoice(ConceptMatch("a_concept"), StrMatch("a_string"))
|
definition = OrderedChoice(ConceptMatch("a_concept"), StrMatch("a_string"))
|
||||||
parser_result = ParserResultConcept(BnfParser(), "a_concept | 'a_string'", definition, definition)
|
parser_result = ParserResultConcept(BnfParser(), "a_concept | 'a_string'", definition, definition)
|
||||||
expected = get_concept(name="name", body="__definition[0]", definition=parser_result)
|
expected = get_def_concept(name="name", body="__definition[0]", definition=parser_result)
|
||||||
|
|
||||||
assert res.status
|
assert res.status
|
||||||
assert res.who == parser.name
|
assert res.who == parser.name
|
||||||
@@ -370,3 +370,32 @@ def test_i_can_detect_not_for_me():
|
|||||||
assert not res.status
|
assert not res.status
|
||||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.NOT_FOR_ME)
|
assert context.sheerka.isinstance(res.value, BuiltinConcepts.NOT_FOR_ME)
|
||||||
assert isinstance(res.value.body[0], CannotHandleErrorNode)
|
assert isinstance(res.value.body[0], CannotHandleErrorNode)
|
||||||
|
|
||||||
|
|
||||||
|
def test_i_can_parse_is_a():
|
||||||
|
parser = DefaultParser()
|
||||||
|
text = "the name of my 'concept' isa the name of the set"
|
||||||
|
res = parser.parse(get_context(), text)
|
||||||
|
expected = IsaConceptNode([],
|
||||||
|
concept=NameNode(list(Tokenizer("the name of my 'concept'"))),
|
||||||
|
set=NameNode(list(Tokenizer("the name of the set"))))
|
||||||
|
|
||||||
|
assert res.status
|
||||||
|
assert res.who == parser.name
|
||||||
|
assert res.value.source == text
|
||||||
|
assert isinstance(res.value, ParserResultConcept)
|
||||||
|
assert res.value.value == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("text", [
|
||||||
|
"concept",
|
||||||
|
"isa number",
|
||||||
|
"name isa",
|
||||||
|
])
|
||||||
|
def test_i_cannot_parse_invalid_entries(text):
|
||||||
|
parser = DefaultParser()
|
||||||
|
res = parser.parse(get_context(), text)
|
||||||
|
|
||||||
|
assert not res.status
|
||||||
|
assert isinstance(res.body, ParserResultConcept)
|
||||||
|
assert isinstance(res.body.body[0], UnexpectedTokenErrorNode)
|
||||||
|
|||||||
+69
-1
@@ -3,7 +3,7 @@ import os
|
|||||||
from os import path
|
from os import path
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept, UserInputConcept
|
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept, UserInputConcept, ConceptAlreadyInSet
|
||||||
from core.concept import Concept, PROPERTIES_TO_SERIALIZE, Property
|
from core.concept import Concept, PROPERTIES_TO_SERIALIZE, Property
|
||||||
from core.sheerka import Sheerka, ExecutionContext
|
from core.sheerka import Sheerka, ExecutionContext
|
||||||
from evaluators.MutipleSameSuccessEvaluator import MultipleSameSuccessEvaluator
|
from evaluators.MutipleSameSuccessEvaluator import MultipleSameSuccessEvaluator
|
||||||
@@ -600,3 +600,71 @@ def test_builtin_error_concept_are_errors():
|
|||||||
# only test a random one, it will be the same for the others
|
# only test a random one, it will be the same for the others
|
||||||
sheerka = get_sheerka()
|
sheerka = get_sheerka()
|
||||||
assert not sheerka.is_success(sheerka.new(BuiltinConcepts.TOO_MANY_SUCCESS))
|
assert not sheerka.is_success(sheerka.new(BuiltinConcepts.TOO_MANY_SUCCESS))
|
||||||
|
|
||||||
|
|
||||||
|
def test_i_can_add_concept_to_set():
|
||||||
|
sheerka = get_sheerka(False, False)
|
||||||
|
|
||||||
|
foo = Concept("foo")
|
||||||
|
sheerka.set_id_if_needed(foo, False)
|
||||||
|
|
||||||
|
all_foos = Concept("all_foos")
|
||||||
|
sheerka.set_id_if_needed(all_foos, False)
|
||||||
|
|
||||||
|
context = get_context(sheerka)
|
||||||
|
res = sheerka.add_concept_to_set(context, foo, all_foos)
|
||||||
|
|
||||||
|
assert res.status
|
||||||
|
assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS)
|
||||||
|
|
||||||
|
all_entries = get_sheerka(False, False).sdp.get("All_" + all_foos.id, None, False)
|
||||||
|
assert len(all_entries) == 1
|
||||||
|
assert foo.id in all_entries
|
||||||
|
|
||||||
|
|
||||||
|
def test_i_can_add_several_concepts_to_set():
|
||||||
|
sheerka = get_sheerka(False, False)
|
||||||
|
|
||||||
|
foo1 = Concept("foo1")
|
||||||
|
sheerka.set_id_if_needed(foo1, False)
|
||||||
|
|
||||||
|
foo2 = Concept("foo1")
|
||||||
|
sheerka.set_id_if_needed(foo2, False)
|
||||||
|
|
||||||
|
all_foos = Concept("all_foos")
|
||||||
|
sheerka.set_id_if_needed(all_foos, False)
|
||||||
|
|
||||||
|
context = get_context(sheerka)
|
||||||
|
sheerka.add_concept_to_set(context, foo1, all_foos)
|
||||||
|
res = sheerka.add_concept_to_set(context, foo2, all_foos)
|
||||||
|
|
||||||
|
assert res.status
|
||||||
|
assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS)
|
||||||
|
|
||||||
|
all_entries = get_sheerka(False, False).sdp.get("All_" + all_foos.id, None, False)
|
||||||
|
assert len(all_entries) == 2
|
||||||
|
assert foo1.id in all_entries
|
||||||
|
assert foo2.id in all_entries
|
||||||
|
|
||||||
|
|
||||||
|
def test_i_cannot_add_the_same_concept_twice_in_a_set():
|
||||||
|
sheerka = get_sheerka()
|
||||||
|
|
||||||
|
foo = Concept("foo")
|
||||||
|
sheerka.set_id_if_needed(foo, False)
|
||||||
|
|
||||||
|
all_foos = Concept("all_foos")
|
||||||
|
sheerka.set_id_if_needed(all_foos, False)
|
||||||
|
|
||||||
|
context = get_context(sheerka)
|
||||||
|
sheerka.add_concept_to_set(context, foo, all_foos)
|
||||||
|
res = sheerka.add_concept_to_set(context, foo, all_foos)
|
||||||
|
|
||||||
|
assert not res.status
|
||||||
|
assert res.body == ConceptAlreadyInSet(foo, all_foos)
|
||||||
|
|
||||||
|
all_entries = sheerka.sdp.get("All_" + all_foos.id, None, False)
|
||||||
|
assert len(all_entries) == 1
|
||||||
|
assert foo.id in all_entries
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -836,15 +836,20 @@ def test_i_can_add_a_dictionary_as_a_reference(root):
|
|||||||
])
|
])
|
||||||
def test_i_can_add_unique(root):
|
def test_i_can_add_unique(root):
|
||||||
sdp = SheerkaDataProvider(root)
|
sdp = SheerkaDataProvider(root)
|
||||||
sdp.add_unique(evt_digest, "entry", ObjNoKey(1, "foo"))
|
entry, key = sdp.add_unique(evt_digest, "entry", ObjNoKey(1, "foo"))
|
||||||
sdp.add_unique(evt_digest, "entry", ObjNoKey(1, "foo"))
|
assert (entry, key) == ("entry", None)
|
||||||
sdp.add_unique(evt_digest, "entry", ObjNoKey(2, "bar"))
|
|
||||||
|
entry, key = sdp.add_unique(evt_digest, "entry", ObjNoKey(1, "foo"))
|
||||||
|
assert (entry, key) == (None, None)
|
||||||
|
|
||||||
entry, key = sdp.add_unique(evt_digest, "entry", ObjNoKey(2, "bar"))
|
entry, key = sdp.add_unique(evt_digest, "entry", ObjNoKey(2, "bar"))
|
||||||
|
assert (entry, key) == ("entry", None)
|
||||||
|
|
||||||
|
entry, key = sdp.add_unique(evt_digest, "entry", ObjNoKey(2, "bar"))
|
||||||
|
assert (entry, key) == (None, None)
|
||||||
|
|
||||||
state = sdp.load_state(sdp.get_snapshot())
|
state = sdp.load_state(sdp.get_snapshot())
|
||||||
assert state.data == {"entry": {ObjNoKey(1, "foo"), ObjNoKey(2, "bar")}}
|
assert state.data == {"entry": {ObjNoKey(1, "foo"), ObjNoKey(2, "bar")}}
|
||||||
assert entry == "entry"
|
|
||||||
assert key is None
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("root", [
|
@pytest.mark.parametrize("root", [
|
||||||
|
|||||||
@@ -30,6 +30,32 @@ def init_test():
|
|||||||
os.chdir(current_pwd)
|
os.chdir(current_pwd)
|
||||||
|
|
||||||
|
|
||||||
|
def get_sheerka(use_dict=True, skip_builtins_in_db=True):
|
||||||
|
root = "mem://" if use_dict else root_folder
|
||||||
|
sheerka = Sheerka(skip_builtins_in_db=skip_builtins_in_db)
|
||||||
|
sheerka.initialize(root)
|
||||||
|
|
||||||
|
return sheerka
|
||||||
|
|
||||||
|
|
||||||
|
def get_context(sheerka):
|
||||||
|
return ExecutionContext("test", "xxx", sheerka)
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_concept():
|
||||||
|
concept = Concept(
|
||||||
|
name="a + b",
|
||||||
|
where="isinstance(a, int) and isinstance(b, int)",
|
||||||
|
pre="isinstance(a, int) and isinstance(b, int)",
|
||||||
|
post="isinstance(res, int)",
|
||||||
|
body="def func(x,y):\n return x+y\nfunc(a,b)",
|
||||||
|
desc="specific description")
|
||||||
|
concept.set_prop("a", "value1")
|
||||||
|
concept.set_prop("b", "value2")
|
||||||
|
|
||||||
|
return concept
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("text, expected", [
|
@pytest.mark.parametrize("text, expected", [
|
||||||
("1 + 1", 2),
|
("1 + 1", 2),
|
||||||
("sheerka.test()", 'I have access to Sheerka !')
|
("sheerka.test()", 'I have access to Sheerka !')
|
||||||
@@ -382,27 +408,11 @@ def test_i_can_eval_bnf_definitions_from_separate_instances():
|
|||||||
assert sheerka.isinstance(res[0].value, concept_b)
|
assert sheerka.isinstance(res[0].value, concept_b)
|
||||||
|
|
||||||
|
|
||||||
def get_sheerka(use_dict=True, skip_builtins_in_db=True):
|
def test_i_can_say_that_a_concept_isa_another_concept():
|
||||||
root = "mem://" if use_dict else root_folder
|
sheerka = get_sheerka()
|
||||||
sheerka = Sheerka(skip_builtins_in_db=skip_builtins_in_db)
|
sheerka.evaluate_user_input("def concept foo")
|
||||||
sheerka.initialize(root)
|
sheerka.evaluate_user_input("def concept bar")
|
||||||
|
|
||||||
return sheerka
|
res = sheerka.evaluate_user_input("foo isa bar")
|
||||||
|
assert res[0].status
|
||||||
|
assert sheerka.isinstance(res[0].body, BuiltinConcepts.SUCCESS)
|
||||||
def get_context(sheerka):
|
|
||||||
return ExecutionContext("test", "xxx", sheerka)
|
|
||||||
|
|
||||||
|
|
||||||
def get_default_concept():
|
|
||||||
concept = Concept(
|
|
||||||
name="a + b",
|
|
||||||
where="isinstance(a, int) and isinstance(b, int)",
|
|
||||||
pre="isinstance(a, int) and isinstance(b, int)",
|
|
||||||
post="isinstance(res, int)",
|
|
||||||
body="def func(x,y):\n return x+y\nfunc(a,b)",
|
|
||||||
desc="specific description")
|
|
||||||
concept.set_prop("a", "value1")
|
|
||||||
concept.set_prop("b", "value2")
|
|
||||||
|
|
||||||
return concept
|
|
||||||
|
|||||||
Reference in New Issue
Block a user