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
|
||||
LIST = "list" # represents a list
|
||||
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"
|
||||
GENERIC_NODE = "generic node"
|
||||
@@ -68,6 +69,7 @@ BuiltinErrors = [str(e) for e in {
|
||||
BuiltinConcepts.CONCEPT_ALREADY_DEFINED,
|
||||
BuiltinConcepts.CONCEPT_EVAL_ERROR,
|
||||
BuiltinConcepts.CANNOT_RESOLVE_VALUE_ERROR,
|
||||
BuiltinConcepts.CONCEPT_ALREADY_IN_SET,
|
||||
}]
|
||||
|
||||
"""
|
||||
@@ -320,3 +322,24 @@ class ListConcept(Concept):
|
||||
|
||||
def __contains__(self, item):
|
||||
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.EVALUATION,
|
||||
BuiltinConcepts.AFTER_EVALUATION]
|
||||
|
||||
CONCEPT_LEXER_PARSER_CLASS = "parsers.ConceptLexerParser.ConceptLexerParser"
|
||||
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)
|
||||
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
|
||||
:param context:
|
||||
:param concept: DefConceptNode
|
||||
:param logger
|
||||
:return: digest of the new concept
|
||||
"""
|
||||
|
||||
logger = logger or self.log
|
||||
|
||||
concept.init_key()
|
||||
concepts_definitions = None
|
||||
init_ret_value = None
|
||||
@@ -393,6 +397,7 @@ class Sheerka(Concept):
|
||||
if concepts_definitions is not None:
|
||||
self.sdp.set(context.event_digest, self.CONCEPTS_DEFINITIONS_ENTRY, concepts_definitions, use_ref=True)
|
||||
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])
|
||||
|
||||
# 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))
|
||||
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):
|
||||
"""
|
||||
Updates the codes of the newly created concept
|
||||
|
||||
@@ -86,6 +86,7 @@ class Keywords(Enum):
|
||||
WHERE = "where"
|
||||
PRE = "pre"
|
||||
POST = "post"
|
||||
ISA = "isa"
|
||||
|
||||
|
||||
class Tokenizer:
|
||||
|
||||
+59
-3
@@ -81,8 +81,8 @@ So, you could call the concept by
|
||||
|
||||
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.
|
||||
|
||||
::
|
||||
@@ -597,4 +597,60 @@ Like in regular expressions, you will also find
|
||||
For those who doesn't know that BNF stands for, please have a look at the bnf_
|
||||
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):
|
||||
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:
|
||||
error_cause = sheerka.value(ret.body)
|
||||
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
|
||||
|
||||
|
||||
@dataclass()
|
||||
class IsaConceptNode(DefaultParserNode):
|
||||
concept: NameNode = NotInitializedNode()
|
||||
set: NameNode = NotInitializedNode()
|
||||
|
||||
|
||||
class DefaultParser(BaseParser):
|
||||
"""
|
||||
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)
|
||||
return self.parse_def_concept(token)
|
||||
else:
|
||||
return self.add_error(CannotHandleErrorNode([], self.text))
|
||||
return self.parse_isa_concept()
|
||||
|
||||
def parse_def_concept(self, def_token):
|
||||
"""
|
||||
@@ -257,6 +263,35 @@ class DefaultParser(BaseParser):
|
||||
|
||||
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_concept_parts = [Keywords.CONCEPT, Keywords.FROM, Keywords.AS, Keywords.WHERE, Keywords.PRE, Keywords.POST]
|
||||
|
||||
@@ -407,12 +407,15 @@ class SheerkaDataProvider:
|
||||
state.date = datetime.now()
|
||||
if entry not in state.data:
|
||||
state.data[entry] = {obj}
|
||||
already_exist = False
|
||||
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)
|
||||
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):
|
||||
"""
|
||||
|
||||
@@ -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.PythonParser import PythonParser, PythonNode
|
||||
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.BnfParser import BnfParser
|
||||
|
||||
@@ -55,24 +55,24 @@ from parsers.BnfParser import BnfParser
|
||||
# return left_as_string == right_as_string
|
||||
#
|
||||
|
||||
def get_concept(name, where=None, pre=None, post=None, body=None, definition=None):
|
||||
concept = DefConceptNode([], name=NameNode(list(Tokenizer(name))))
|
||||
def get_def_concept(name, where=None, pre=None, post=None, body=None, definition=None):
|
||||
def_concept = DefConceptNode([], name=NameNode(list(Tokenizer(name))))
|
||||
|
||||
if body:
|
||||
concept.body = get_concept_part(body)
|
||||
def_concept.body = get_concept_part(body)
|
||||
if where:
|
||||
concept.where = get_concept_part(where)
|
||||
def_concept.where = get_concept_part(where)
|
||||
if pre:
|
||||
concept.pre = get_concept_part(pre)
|
||||
def_concept.pre = get_concept_part(pre)
|
||||
if post:
|
||||
concept.post = get_concept_part(post)
|
||||
def_concept.post = get_concept_part(post)
|
||||
if definition:
|
||||
concept.definition = ReturnValueConcept(
|
||||
def_concept.definition = ReturnValueConcept(
|
||||
"parsers.Bnf",
|
||||
True,
|
||||
definition)
|
||||
|
||||
return concept
|
||||
return def_concept
|
||||
|
||||
|
||||
def get_context():
|
||||
@@ -145,16 +145,16 @@ def get_concept_part(part):
|
||||
|
||||
|
||||
@pytest.mark.parametrize("text, expected", [
|
||||
("def concept hello", get_concept(name="hello")),
|
||||
("def concept hello ", get_concept(name="hello")),
|
||||
("def concept a + b", get_concept(name="a + b")),
|
||||
("def concept a+b", get_concept(name="a + b")),
|
||||
("def concept 'a+b'+c", get_concept(name="'a+b' + c")),
|
||||
("def concept 'as if'", get_concept(name="'as if'")),
|
||||
("def concept 'as' if", get_concept(name="'as if'")),
|
||||
("def concept hello as 'hello'", get_concept(name="hello", body="'hello'")),
|
||||
("def concept hello as 1", get_concept(name="hello", body="1")),
|
||||
("def concept hello as 1 + 1", get_concept(name="hello", body="1 + 1")),
|
||||
("def concept hello", get_def_concept(name="hello")),
|
||||
("def concept hello ", get_def_concept(name="hello")),
|
||||
("def concept a + b", get_def_concept(name="a + b")),
|
||||
("def concept a+b", get_def_concept(name="a + b")),
|
||||
("def concept 'a+b'+c", get_def_concept(name="'a+b' + c")),
|
||||
("def concept 'as if'", get_def_concept(name="'as if'")),
|
||||
("def concept 'as' if", get_def_concept(name="'as if'")),
|
||||
("def concept hello as 'hello'", get_def_concept(name="hello", body="'hello'")),
|
||||
("def concept hello as 1", get_def_concept(name="hello", body="1")),
|
||||
("def concept hello as 1 + 1", get_def_concept(name="hello", body="1 + 1")),
|
||||
])
|
||||
def test_i_can_parse_def_concept(text, expected):
|
||||
parser = DefaultParser()
|
||||
@@ -178,7 +178,7 @@ as res = a + b
|
||||
parser = DefaultParser()
|
||||
res = parser.parse(get_context(), text)
|
||||
return_value = res.value
|
||||
expected_concept = get_concept(
|
||||
expected_concept = get_def_concept(
|
||||
name="a plus b",
|
||||
where="a,b",
|
||||
pre="isinstance(a, int) and isinstance(b, float)",
|
||||
@@ -199,7 +199,7 @@ def func(x):
|
||||
func(a)
|
||||
"""
|
||||
|
||||
expected_concept = get_concept(
|
||||
expected_concept = get_def_concept(
|
||||
name="add one to a ",
|
||||
body=PythonNode(
|
||||
"def func(x):\n return x+1\nfunc(a)",
|
||||
@@ -223,7 +223,7 @@ def concept add one to a as:
|
||||
func(a)
|
||||
"""
|
||||
|
||||
expected_concept = get_concept(
|
||||
expected_concept = get_def_concept(
|
||||
name="add one to a ",
|
||||
body=PythonNode(
|
||||
"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():
|
||||
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()
|
||||
res = parser.parse(get_context(), text)
|
||||
return_value = res.value
|
||||
@@ -342,7 +342,7 @@ def test_i_can_parse_def_concept_from_regex():
|
||||
node = res.value.value
|
||||
definition = OrderedChoice(ConceptMatch("a_concept"), StrMatch("a_string"))
|
||||
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.who == parser.name
|
||||
@@ -370,3 +370,32 @@ def test_i_can_detect_not_for_me():
|
||||
assert not res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.NOT_FOR_ME)
|
||||
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
|
||||
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.sheerka import Sheerka, ExecutionContext
|
||||
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
|
||||
sheerka = get_sheerka()
|
||||
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):
|
||||
sdp = SheerkaDataProvider(root)
|
||||
sdp.add_unique(evt_digest, "entry", ObjNoKey(1, "foo"))
|
||||
sdp.add_unique(evt_digest, "entry", ObjNoKey(1, "foo"))
|
||||
sdp.add_unique(evt_digest, "entry", ObjNoKey(2, "bar"))
|
||||
entry, key = sdp.add_unique(evt_digest, "entry", ObjNoKey(1, "foo"))
|
||||
assert (entry, key) == ("entry", None)
|
||||
|
||||
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"))
|
||||
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())
|
||||
assert state.data == {"entry": {ObjNoKey(1, "foo"), ObjNoKey(2, "bar")}}
|
||||
assert entry == "entry"
|
||||
assert key is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("root", [
|
||||
|
||||
@@ -30,6 +30,32 @@ def init_test():
|
||||
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", [
|
||||
("1 + 1", 2),
|
||||
("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)
|
||||
|
||||
|
||||
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)
|
||||
def test_i_can_say_that_a_concept_isa_another_concept():
|
||||
sheerka = get_sheerka()
|
||||
sheerka.evaluate_user_input("def concept foo")
|
||||
sheerka.evaluate_user_input("def concept bar")
|
||||
|
||||
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
|
||||
res = sheerka.evaluate_user_input("foo isa bar")
|
||||
assert res[0].status
|
||||
assert sheerka.isinstance(res[0].body, BuiltinConcepts.SUCCESS)
|
||||
|
||||
Reference in New Issue
Block a user