Added concept 'isa' other_concept functionality

This commit is contained in:
2019-12-21 22:02:07 +01:00
parent 2474b08150
commit a683d4cd42
13 changed files with 489 additions and 61 deletions
+23
View File
@@ -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
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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)
+70
View File
@@ -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
+36 -1
View File
@@ -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]
+5 -2
View File
@@ -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):
""" """
+94
View File
@@ -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
View File
@@ -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
View File
@@ -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
+10 -5
View File
@@ -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", [
+33 -23
View File
@@ -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