Fixed infinite recursion when parsing complex BNF node
This commit is contained in:
+11
-1
@@ -13,7 +13,7 @@ class BaseTest:
|
||||
pass
|
||||
|
||||
def get_context(self, sheerka=None, eval_body=False, eval_where=False):
|
||||
context = ExecutionContext("test", Event(), sheerka or self.get_sheerka(), BuiltinConcepts.NOP, None)
|
||||
context = ExecutionContext("test", Event(), sheerka or self.get_sheerka(), BuiltinConcepts.TESTING, None)
|
||||
if eval_body:
|
||||
context.local_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
|
||||
if eval_where:
|
||||
@@ -145,3 +145,13 @@ class BaseTest:
|
||||
else:
|
||||
concept.metadata.variables[k] = v
|
||||
return concept
|
||||
|
||||
def init_scenario(self, init_expressions):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
for expression in init_expressions:
|
||||
res = sheerka.evaluate_user_input(expression)
|
||||
assert len(res) == 1, f"Failed to execute '{expression}'"
|
||||
assert res[0].status, f"Error while executing '{expression}'"
|
||||
|
||||
return sheerka
|
||||
|
||||
@@ -103,3 +103,21 @@ def test_global_hits_are_global_even_when_empty():
|
||||
|
||||
assert a.global_hints == {"global hint 2"}
|
||||
assert b.global_hints == {"global hint 2"}
|
||||
|
||||
|
||||
def test_i_can_search():
|
||||
a = ExecutionContext("foo", Event("event_1"), "fake_sheerka", BuiltinConcepts.TESTING, "a")
|
||||
ab = a.push(BuiltinConcepts.TESTING, "ab", obj="obj_ab")
|
||||
ac = a.push(BuiltinConcepts.TESTING, "ac", obj="obj_ac")
|
||||
abb = ab.push(BuiltinConcepts.TESTING, "abb", obj="skip")
|
||||
abbb = abb.push(BuiltinConcepts.TESTING, "abbb", obj="obj_abbb")
|
||||
|
||||
assert list(abbb.search()) == [abb, ab, a]
|
||||
assert list(abbb.search(start_with_self=True)) == [abbb, abb, ab, a]
|
||||
assert list(abbb.search(lambda ec: ec.obj != "skip")) == [ab, a]
|
||||
assert list(abbb.search(lambda ec: ec.obj != "skip", lambda ec: ec.action_context)) == ["ab", "a"]
|
||||
assert list(abbb.search(stop=lambda ec: ec.obj == "skip")) == []
|
||||
assert list(abbb.search(
|
||||
stop=lambda ec: ec.obj == "skip",
|
||||
start_with_self=True,
|
||||
get_obj=lambda ec: ec.obj)) == ["obj_abbb"]
|
||||
|
||||
@@ -10,7 +10,7 @@ class TestSheerkaGreaterThanManager(TestUsingMemoryBasedSheerka):
|
||||
sheerka, context, one, two = self.init_concepts("one", "two", cache_only=False)
|
||||
service = sheerka.services[SheerkaComparisonManager.NAME]
|
||||
|
||||
res = service.is_greater_than(context, "prop_name", two, one)
|
||||
res = service.set_is_greater_than(context, "prop_name", two, one)
|
||||
assert res.status
|
||||
assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS)
|
||||
|
||||
@@ -29,7 +29,7 @@ class TestSheerkaGreaterThanManager(TestUsingMemoryBasedSheerka):
|
||||
sheerka, context, one, two = self.init_concepts("one", "two", cache_only=False)
|
||||
service = sheerka.services[SheerkaComparisonManager.NAME]
|
||||
|
||||
res = service.is_less_than(context, "prop_name", one, two)
|
||||
res = service.set_is_less_than(context, "prop_name", one, two)
|
||||
assert res.status
|
||||
assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS)
|
||||
|
||||
@@ -48,8 +48,8 @@ class TestSheerkaGreaterThanManager(TestUsingMemoryBasedSheerka):
|
||||
sheerka, context, one, two, three, four = self.init_concepts("one", "two", "three", "four", cache_only=False)
|
||||
service = sheerka.services[SheerkaComparisonManager.NAME]
|
||||
|
||||
service.is_greater_than(context, "prop_name", two, one)
|
||||
service.is_greater_than(context, "prop_name", three, two)
|
||||
service.set_is_greater_than(context, "prop_name", two, one)
|
||||
service.set_is_greater_than(context, "prop_name", three, two)
|
||||
|
||||
in_cache = sheerka.cache_manager.get(SheerkaComparisonManager.COMPARISON_ENTRY, "prop_name|#")
|
||||
assert in_cache == [
|
||||
@@ -67,7 +67,7 @@ class TestSheerkaGreaterThanManager(TestUsingMemoryBasedSheerka):
|
||||
|
||||
sheerka.cache_manager.clear(SheerkaComparisonManager.COMPARISON_ENTRY) # reset the cache
|
||||
|
||||
service.is_greater_than(context, "prop_name", four, three)
|
||||
service.set_is_greater_than(context, "prop_name", four, three)
|
||||
in_cache = sheerka.cache_manager.get(SheerkaComparisonManager.COMPARISON_ENTRY, "prop_name|#")
|
||||
assert in_cache == [
|
||||
ComparisonObj(context.event.get_digest(), "prop_name", two.id, one.id, ">", "#"),
|
||||
@@ -92,10 +92,10 @@ class TestSheerkaGreaterThanManager(TestUsingMemoryBasedSheerka):
|
||||
for entry in entries:
|
||||
if ">" in entry:
|
||||
a, b = [concepts_map[e.strip()] for e in entry.split(">")]
|
||||
service.is_greater_than(context, "prop_name", a, b)
|
||||
service.set_is_greater_than(context, "prop_name", a, b)
|
||||
else:
|
||||
a, b = [concepts_map[e.strip()] for e in entry.split("<")]
|
||||
service.is_less_than(context, "prop_name", a, b)
|
||||
service.set_is_less_than(context, "prop_name", a, b)
|
||||
|
||||
assert service.get_concepts_weights("prop_name") == expected
|
||||
|
||||
@@ -103,8 +103,8 @@ class TestSheerkaGreaterThanManager(TestUsingMemoryBasedSheerka):
|
||||
sheerka, context, one, two, three = self.init_concepts("one", "two", "three")
|
||||
service = sheerka.services[SheerkaComparisonManager.NAME]
|
||||
|
||||
service.is_greater_than(context, "prop_name", two, one)
|
||||
service.is_less_than(context, "prop_name", two, three)
|
||||
service.set_is_greater_than(context, "prop_name", two, one)
|
||||
service.set_is_less_than(context, "prop_name", two, three)
|
||||
|
||||
res = service.get_partition("prop_name")
|
||||
|
||||
@@ -118,8 +118,8 @@ class TestSheerkaGreaterThanManager(TestUsingMemoryBasedSheerka):
|
||||
sheerka, context, one, two = self.init_concepts("one", "two")
|
||||
service = sheerka.services[SheerkaComparisonManager.NAME]
|
||||
|
||||
service.is_greater_than(context, "prop_name", two, one)
|
||||
res = service.is_greater_than(context, "prop_name", one, two)
|
||||
service.set_is_greater_than(context, "prop_name", two, one)
|
||||
res = service.set_is_greater_than(context, "prop_name", one, two)
|
||||
|
||||
assert not res.status
|
||||
assert sheerka.isinstance(res.body, BuiltinConcepts.CHICKEN_AND_EGG)
|
||||
@@ -129,15 +129,15 @@ class TestSheerkaGreaterThanManager(TestUsingMemoryBasedSheerka):
|
||||
sheerka, context, one, two, three, four, five = self.init_concepts("one", "two", "three", "four", "five")
|
||||
service = sheerka.services[SheerkaComparisonManager.NAME]
|
||||
|
||||
service.is_greater_than(context, "prop_name", two, one)
|
||||
service.is_greater_than(context, "prop_name", five, four)
|
||||
service.is_greater_than(context, "prop_name", four, three)
|
||||
service.is_greater_than(context, "prop_name", five, two)
|
||||
service.set_is_greater_than(context, "prop_name", two, one)
|
||||
service.set_is_greater_than(context, "prop_name", five, four)
|
||||
service.set_is_greater_than(context, "prop_name", four, three)
|
||||
service.set_is_greater_than(context, "prop_name", five, two)
|
||||
|
||||
res = service.is_greater_than(context, "prop_name", two, one)
|
||||
res = service.set_is_greater_than(context, "prop_name", two, one)
|
||||
assert res.status
|
||||
|
||||
res = service.is_greater_than(context, "prop_name", one, five)
|
||||
res = service.set_is_greater_than(context, "prop_name", one, five)
|
||||
|
||||
assert not res.status
|
||||
assert sheerka.isinstance(res.body, BuiltinConcepts.CHICKEN_AND_EGG)
|
||||
@@ -147,13 +147,13 @@ class TestSheerkaGreaterThanManager(TestUsingMemoryBasedSheerka):
|
||||
sheerka, context, one, two = self.init_concepts("one", "two")
|
||||
service = sheerka.services[SheerkaComparisonManager.NAME]
|
||||
|
||||
service.is_greater_than(context, "prop_name", two, one)
|
||||
service.is_less_than(context, "prop_name", one, two)
|
||||
service.set_is_greater_than(context, "prop_name", two, one)
|
||||
service.set_is_less_than(context, "prop_name", one, two)
|
||||
|
||||
weighted = sheerka.cache_manager.get(SheerkaComparisonManager.RESOLVED_COMPARISON_ENTRY, "prop_name|#")
|
||||
assert weighted == {"1001": 1, "1002": 2}
|
||||
|
||||
def test_methods_are_correctly_bound(self):
|
||||
sheerka, context, one, two = self.init_concepts("one", "two")
|
||||
res = sheerka.is_greater_than(context, "prop_name", two, one)
|
||||
res = sheerka.set_is_greater_than(context, "prop_name", two, one)
|
||||
assert res.status
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from core.concept import Concept, ConceptParts
|
||||
from core.sheerka.Sheerka import Sheerka
|
||||
from core.sheerka.services.SheerkaVariableManager import SheerkaVariableManager
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
@@ -50,6 +49,26 @@ class TestSheerkaVariable(TestUsingMemoryBasedSheerka):
|
||||
sheerka.delete(context, "TestSheerkaVariable", "my_variable")
|
||||
assert sheerka.load("TestSheerkaVariable", "my_variable") is None
|
||||
|
||||
def test_i_can_set_and_get_a_value(self):
|
||||
sheerka = self.get_sheerka(cache_only=False)
|
||||
context = self.get_context(sheerka)
|
||||
context.event.user_id = "Test_user"
|
||||
|
||||
sheerka.set(context, "my_variable", "my value")
|
||||
res = sheerka.get(context, "my_variable")
|
||||
assert res == "my value"
|
||||
|
||||
# I can persist in db
|
||||
sheerka.cache_manager.commit(context)
|
||||
|
||||
assert sheerka.sdp.exists(SheerkaVariableManager.VARIABLES_ENTRY, "Test_user|my_variable")
|
||||
loaded = sheerka.sdp.get(SheerkaVariableManager.VARIABLES_ENTRY, "Test_user|my_variable")
|
||||
assert loaded.event_id == context.event.get_digest()
|
||||
assert loaded.key == "my_variable"
|
||||
assert loaded.value == "my value"
|
||||
assert loaded.who == "Test_user"
|
||||
assert loaded.parents is None
|
||||
|
||||
# def test_i_can_get_the_parent_when_modified(self):
|
||||
# sheerka = self.get_sheerka()
|
||||
# context = self.get_context(sheerka)
|
||||
|
||||
@@ -132,7 +132,7 @@ class TestPythonEvaluator(TestUsingMemoryBasedSheerka):
|
||||
self.from_def_concept("mult", "a mult b", ["a", "b"]),
|
||||
)
|
||||
|
||||
parsed = PythonParser().parse(context, ParserInput("is_greater_than(BuiltinConcepts.PRECEDENCE, mult, plus)"))
|
||||
parsed = PythonParser().parse(context, ParserInput("set_is_greater_than(BuiltinConcepts.PRECEDENCE, mult, plus)"))
|
||||
python_evaluator = PythonEvaluator()
|
||||
|
||||
evaluated = python_evaluator.eval(context, parsed)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import pytest
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import Concept, PROPERTIES_TO_SERIALIZE, simplec, CMV, CB, CC, CV
|
||||
from core.concept import Concept, PROPERTIES_TO_SERIALIZE, simplec, CMV
|
||||
from evaluators.MutipleSameSuccessEvaluator import MultipleSameSuccessEvaluator
|
||||
from evaluators.PythonEvaluator import PythonEvalError
|
||||
from parsers.BaseNodeParser import SyaAssociativity
|
||||
@@ -12,16 +12,6 @@ from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
class TestSheerkaNonRegMemory(TestUsingMemoryBasedSheerka):
|
||||
|
||||
def init_scenario(self, init_expressions):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
for expression in init_expressions:
|
||||
res = sheerka.evaluate_user_input(expression)
|
||||
assert len(res) == 1, f"Failed to execute '{expression}'"
|
||||
assert res[0].status, f"Error while executing '{expression}'"
|
||||
|
||||
return sheerka
|
||||
|
||||
@pytest.mark.parametrize("text, expected", [
|
||||
("1 + 1", 2),
|
||||
("sheerka.test()", 'I have access to Sheerka !')
|
||||
@@ -880,10 +870,10 @@ as:
|
||||
|
||||
sheerka = self.init_scenario(definitions)
|
||||
|
||||
res = sheerka.evaluate_user_input("is_greater_than('some_prop', two, one)")
|
||||
res = sheerka.evaluate_user_input("set_is_greater_than('some_prop', two, one)")
|
||||
assert res[0].status
|
||||
|
||||
res = sheerka.evaluate_user_input("is_less_than('some_prop', two, three)")
|
||||
res = sheerka.evaluate_user_input("set_is_less_than('some_prop', two, three)")
|
||||
assert res[0].status
|
||||
|
||||
res = sheerka.evaluate_user_input("get_concepts_weights('some_prop')")
|
||||
@@ -891,7 +881,7 @@ as:
|
||||
assert res[0].body == {'1001': 1, '1002': 2, '1003': 3}
|
||||
|
||||
# test i use a concept to define relation
|
||||
sheerka.evaluate_user_input("def concept a > b as is_greater_than('some_prop', a, b)")
|
||||
sheerka.evaluate_user_input("def concept a > b as set_is_greater_than('some_prop', a, b)")
|
||||
res = sheerka.evaluate_user_input("eval four > three")
|
||||
assert res[0].status
|
||||
|
||||
@@ -901,7 +891,7 @@ as:
|
||||
sheerka, context, one, two, plus = self.init_concepts(
|
||||
Concept("one", body="1"),
|
||||
Concept("two", body="2"),
|
||||
self.from_def_concept("<", "a < b", ["a", "b"], body="is_less_than('some_prop', a, b)")
|
||||
self.from_def_concept("<", "a < b", ["a", "b"], body="set_is_less_than('some_prop', a, b)")
|
||||
)
|
||||
|
||||
expression = "c:one: < c:two:"
|
||||
@@ -946,6 +936,33 @@ as:
|
||||
assert isinstance(error1.error, TypeError)
|
||||
assert error1.error.args[0] == "unsupported operand type(s) for +: 'Concept' and 'int'"
|
||||
|
||||
def test_i_can_evaluate_bnf_concept_defined_with_group_after_restart(self):
|
||||
"""
|
||||
BNF Concepts defined with group and being themselves part a s group get messed up after restart
|
||||
:return:
|
||||
"""
|
||||
init = [
|
||||
"def concept one as 1",
|
||||
"def concept two as 2",
|
||||
"def concept twenty as 20",
|
||||
"def concept number",
|
||||
"one isa number",
|
||||
"two isa number",
|
||||
"twenty isa number",
|
||||
"def concept twenties from bnf twenty number where number < 10 as twenty + number",
|
||||
"twenties isa number",
|
||||
]
|
||||
|
||||
sheerka = self.init_scenario(init)
|
||||
|
||||
# simulate that sheerka was stopped and restarted
|
||||
sheerka.cache_manager.clear(sheerka.CONCEPTS_GRAMMARS_ENTRY)
|
||||
sheerka.cache_manager.get(sheerka.CONCEPTS_BY_KEY_ENTRY, "twenties").compiled = {}
|
||||
|
||||
res = sheerka.evaluate_user_input("eval twenty one")
|
||||
assert res[0].status
|
||||
assert res[0].body == 21
|
||||
|
||||
|
||||
class TestSheerkaNonRegFile(TestUsingFileBasedSheerka):
|
||||
def test_i_can_def_several_concepts(self):
|
||||
@@ -1013,3 +1030,23 @@ class TestSheerkaNonRegFile(TestUsingFileBasedSheerka):
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(eval_body=True), res[0].value)
|
||||
assert evaluated.body == "one two three"
|
||||
assert evaluated.get_value("a") == sheerka.new(concept_a.key, body="one two").init_key()
|
||||
|
||||
def test_i_can_eval_sophisticated_bnf_concepts_after_restart(self):
|
||||
self.init_scenario([
|
||||
"def concept one as 1",
|
||||
"def concept number",
|
||||
"one isa number",
|
||||
"def concept twenty as 20",
|
||||
"twenty isa number",
|
||||
"def concept twenties from bnf twenty number where number < 10 as twenty + number",
|
||||
"twenties isa number",
|
||||
"def concept thirty as 30",
|
||||
"thirty isa number",
|
||||
"def concept thirties from bnf thirty number where number < 10 as thirty + number",
|
||||
"thirties isa number",
|
||||
])
|
||||
|
||||
sheerka = self.get_sheerka() # another instance
|
||||
|
||||
assert sheerka.evaluate_user_input("eval twenty one")[0].body == 21
|
||||
assert sheerka.evaluate_user_input("eval thirty one")[0].body == 31
|
||||
|
||||
@@ -71,7 +71,7 @@ def get_node(
|
||||
if sub_expr == "')'":
|
||||
return ")"
|
||||
|
||||
if isinstance(sub_expr, (scnode, utnode)):
|
||||
if isinstance(sub_expr, (scnode, utnode, DoNotResolve)):
|
||||
return sub_expr
|
||||
|
||||
if isinstance(sub_expr, cnode):
|
||||
|
||||
@@ -24,7 +24,7 @@ class TestBaseNodeParser(TestUsingMemoryBasedSheerka):
|
||||
|
||||
sheerka, context, *updated = self.init_concepts(concept)
|
||||
|
||||
res = BaseNodeParser.get_concepts_by_first_keyword(context, updated)
|
||||
res = BaseNodeParser.get_concepts_by_first_token(context, updated)
|
||||
|
||||
assert res.status
|
||||
assert res.body == expected
|
||||
@@ -54,7 +54,7 @@ class TestBaseNodeParser(TestUsingMemoryBasedSheerka):
|
||||
concept.bnf = bnf
|
||||
sheerka.set_id_if_needed(concept, False)
|
||||
|
||||
res = BaseNodeParser.get_concepts_by_first_keyword(context, [concept])
|
||||
res = BaseNodeParser.get_concepts_by_first_token(context, [concept])
|
||||
|
||||
assert res.status
|
||||
assert res.body == expected
|
||||
@@ -75,7 +75,7 @@ class TestBaseNodeParser(TestUsingMemoryBasedSheerka):
|
||||
foo.bnf = OrderedChoice(ConceptExpression("bar"), ConceptExpression("baz"), StrMatch("qux"))
|
||||
sheerka.set_id_if_needed(foo, False)
|
||||
|
||||
res = BaseNodeParser.get_concepts_by_first_keyword(context, [bar, baz, foo])
|
||||
res = BaseNodeParser.get_concepts_by_first_token(context, [bar, baz, foo])
|
||||
|
||||
assert res.status
|
||||
assert res.body == {
|
||||
@@ -102,7 +102,7 @@ class TestBaseNodeParser(TestUsingMemoryBasedSheerka):
|
||||
foo.bnf = OrderedChoice(ConceptExpression("one"), ConceptExpression("bar"), StrMatch("qux"))
|
||||
sheerka.set_id_if_needed(foo, False)
|
||||
|
||||
res = BaseNodeParser.get_concepts_by_first_keyword(context, [bar, foo], use_sheerka=True)
|
||||
res = BaseNodeParser.get_concepts_by_first_token(context, [bar, foo], use_sheerka=True)
|
||||
|
||||
assert res.status
|
||||
assert res.body == {
|
||||
@@ -149,7 +149,7 @@ class TestBaseNodeParser(TestUsingMemoryBasedSheerka):
|
||||
sheerka.set_isa(context, sheerka.new("one"), number)
|
||||
sheerka.set_isa(context, sheerka.new("two"), number)
|
||||
|
||||
cbfk = BaseNodeParser.get_concepts_by_first_keyword(context, [one, two, three, number, foo]).body
|
||||
cbfk = BaseNodeParser.get_concepts_by_first_token(context, [one, two, three, number, foo]).body
|
||||
|
||||
resolved_ret_val = BaseNodeParser.resolve_concepts_by_first_keyword(context, cbfk)
|
||||
|
||||
@@ -171,7 +171,7 @@ class TestBaseNodeParser(TestUsingMemoryBasedSheerka):
|
||||
ConceptExpression("foo"),
|
||||
ConceptExpression("bar")))
|
||||
|
||||
concepts_by_first_keywords = BaseNodeParser.get_concepts_by_first_keyword(
|
||||
concepts_by_first_keywords = BaseNodeParser.get_concepts_by_first_token(
|
||||
context, [good, foo, bar, baz]).body
|
||||
|
||||
resolved_ret_val = BaseNodeParser.resolve_concepts_by_first_keyword(context, concepts_by_first_keywords)
|
||||
@@ -187,7 +187,7 @@ class TestBaseNodeParser(TestUsingMemoryBasedSheerka):
|
||||
a = self.create_and_add_in_cache_concept(sheerka, "a", bnf=Sequence("one", "two"))
|
||||
b = self.create_and_add_in_cache_concept(sheerka, "b", bnf=Sequence(ConceptExpression("a"), "two"))
|
||||
|
||||
concepts_by_first_keywords = BaseNodeParser.get_concepts_by_first_keyword(
|
||||
concepts_by_first_keywords = BaseNodeParser.get_concepts_by_first_token(
|
||||
context, [a, b]).body
|
||||
|
||||
resolved_ret_val = BaseNodeParser.resolve_concepts_by_first_keyword(context, concepts_by_first_keywords)
|
||||
@@ -202,7 +202,7 @@ class TestBaseNodeParser(TestUsingMemoryBasedSheerka):
|
||||
# foo = self.get_concept(sheerka, "foo", ConceptExpression("bar"))
|
||||
# bar = self.get_concept(sheerka, "bar", ConceptExpression("foo"))
|
||||
#
|
||||
# concepts_by_first_keywords = BaseNodeParser.get_concepts_by_first_keyword(sheerka, [good, foo, bar]).body
|
||||
# concepts_by_first_keywords = BaseNodeParser.get_concepts_by_first_token(sheerka, [good, foo, bar]).body
|
||||
#
|
||||
# resolved_ret_val = BaseNodeParser.resolve_concepts_by_first_keyword(sheerka, concepts_by_first_keywords)
|
||||
# assert resolved_ret_val.status
|
||||
@@ -218,7 +218,7 @@ class TestBaseNodeParser(TestUsingMemoryBasedSheerka):
|
||||
# two = self.get_concept(sheerka, "two", ConceptExpression("three"))
|
||||
# three = self.get_concept(sheerka, "three", ConceptExpression("two"))
|
||||
#
|
||||
# concepts_by_first_keywords = BaseNodeParser.get_concepts_by_first_keyword(sheerka, [good, one, two, three]).body
|
||||
# concepts_by_first_keywords = BaseNodeParser.get_concepts_by_first_token(sheerka, [good, one, two, three]).body
|
||||
#
|
||||
# resolved_ret_val = BaseNodeParser.resolve_concepts_by_first_keyword(sheerka, concepts_by_first_keywords)
|
||||
# assert resolved_ret_val.status
|
||||
@@ -233,7 +233,7 @@ class TestBaseNodeParser(TestUsingMemoryBasedSheerka):
|
||||
# one = self.get_concept(sheerka, "one", ConceptExpression("two"))
|
||||
# two = self.get_concept(sheerka, "two", OrderedChoice(ConceptExpression("one"), ConceptExpression("two")))
|
||||
#
|
||||
# concepts_by_first_keywords = BaseNodeParser.get_concepts_by_first_keyword(sheerka, [good, one, two]).body
|
||||
# concepts_by_first_keywords = BaseNodeParser.get_concepts_by_first_token(sheerka, [good, one, two]).body
|
||||
#
|
||||
# resolved_ret_val = BaseNodeParser.resolve_concepts_by_first_keyword(sheerka, concepts_by_first_keywords)
|
||||
# assert resolved_ret_val.status
|
||||
@@ -248,7 +248,7 @@ class TestBaseNodeParser(TestUsingMemoryBasedSheerka):
|
||||
# one = self.get_concept(sheerka, "one", ConceptExpression("two"))
|
||||
# two = self.get_concept(sheerka, "two", Sequence(StrMatch("yes"), ConceptExpression("one")))
|
||||
#
|
||||
# concepts_by_first_keywords = BaseNodeParser.get_concepts_by_first_keyword(sheerka, [good, one, two]).body
|
||||
# concepts_by_first_keywords = BaseNodeParser.get_concepts_by_first_token(sheerka, [good, one, two]).body
|
||||
#
|
||||
# resolved_ret_val = BaseNodeParser.resolve_concepts_by_first_keyword(sheerka, concepts_by_first_keywords)
|
||||
# assert resolved_ret_val.status
|
||||
|
||||
@@ -1,25 +1,40 @@
|
||||
import pytest
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import Concept, ConceptParts, DoNotResolve
|
||||
from core.concept import Concept, ConceptParts, DoNotResolve, CC, DEFINITION_TYPE_BNF
|
||||
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||
from parsers.BaseNodeParser import CNC, UTN, CN
|
||||
from parsers.BnfNodeParser import BnfNodeParser, StrMatch, TerminalNode, NonTerminalNode, Sequence, OrderedChoice, \
|
||||
Optional, ZeroOrMore, OneOrMore, ConceptExpression
|
||||
from parsers.BnfParser import BnfParser
|
||||
|
||||
import tests.parsers.parsers_utils
|
||||
from tests.BaseTest import BaseTest
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
cmap = {
|
||||
"one": Concept("one"),
|
||||
"two": Concept("two"),
|
||||
"three": Concept("three"),
|
||||
"plus": Concept(name="a plus b").def_var("a").def_var("b"),
|
||||
"bnf one": Concept("bnf_one", definition="'one'"),
|
||||
'one and two': Concept("one and two", definition="one two"),
|
||||
'one or more three': Concept("one or more three", definition="three+"),
|
||||
'two or four': Concept("two or four", definition="two | 'four'"),
|
||||
"twenties": Concept("twenties", definition="'twenty' c:two or four:=unit"),
|
||||
"one or more plus": Concept("one or more plus", definition="c:a plus b:+"), # TODO
|
||||
"four": Concept("four"),
|
||||
"thirty": Concept("thirty", body=30),
|
||||
"forty": Concept("forty", body=40),
|
||||
"fifty": Concept("fifty", body=50),
|
||||
"number": Concept("number"),
|
||||
"foo": Concept("foo"),
|
||||
"bar": Concept("bar"),
|
||||
"baz": Concept("baz"),
|
||||
|
||||
"bnf baz": Concept("bnf baz", definition="'baz'"), # this one should be chosen
|
||||
|
||||
"plus": Concept("plus", definition="one 'plus' two").def_var("a").def_var("b"),
|
||||
|
||||
'foo then bar': Concept("foo then bar", definition="foo bar").def_var("foo").def_var("bar"),
|
||||
'foo or bar': Concept("foo or bar", definition="foo | bar").def_var("foo").def_var("bar"),
|
||||
'one or more foo': Concept("one or more foo", definition="foo+").def_var("foo"),
|
||||
|
||||
"t1": Concept("t1", definition="'twenty' (one|two)=unit").def_var("unit").def_var("one").def_var("two"),
|
||||
"three_four": Concept("three_four", definition="three | four").def_var("three").def_var("four"),
|
||||
"t2": Concept("t2", definition="'twenty' three_four=unit").def_var("unit").def_var("three").def_var("four"),
|
||||
|
||||
# testing keywords
|
||||
"def_only": Concept("def"),
|
||||
@@ -65,15 +80,57 @@ def compute_expected_array(my_concepts_map, expression, expected, exclude_body=F
|
||||
class TestBnfNodeParser(TestUsingMemoryBasedSheerka):
|
||||
sheerka = None
|
||||
|
||||
@staticmethod
|
||||
def update_bnf(context, concept):
|
||||
bnf_parser = BnfParser()
|
||||
res = bnf_parser.parse(context, concept.metadata.definition)
|
||||
if res.status:
|
||||
concept.bnf = res.value.value
|
||||
concept.metadata.definition_type = DEFINITION_TYPE_BNF
|
||||
else:
|
||||
raise Exception(res)
|
||||
return concept
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
t = TestBnfNodeParser()
|
||||
t = cls()
|
||||
TestBnfNodeParser.sheerka, context, _ = t.init_parser(
|
||||
cmap,
|
||||
singleton=False,
|
||||
create_new=True,
|
||||
init_from_sheerka=True)
|
||||
|
||||
# end of initialisation
|
||||
sheerka = TestBnfNodeParser.sheerka
|
||||
sheerka.set_isa(context, sheerka.new("one"), sheerka.new("number"))
|
||||
sheerka.set_isa(context, sheerka.new("two"), sheerka.new("number"))
|
||||
sheerka.set_isa(context, sheerka.new("three"), sheerka.new("number"))
|
||||
sheerka.set_isa(context, sheerka.new("four"), sheerka.new("number"))
|
||||
sheerka.set_isa(context, sheerka.new("thirty"), sheerka.new("number"))
|
||||
sheerka.set_isa(context, sheerka.new("forty"), sheerka.new("number"))
|
||||
sheerka.set_isa(context, sheerka.new("fifty"), sheerka.new("number"))
|
||||
|
||||
thirties = cls.update_bnf(context, Concept("thirties",
|
||||
definition="thirty number",
|
||||
where="number < 10",
|
||||
body="thirty + number").def_var("thirty").def_var("number"))
|
||||
cmap["thirties"] = sheerka.create_new_concept(context, thirties).body.body
|
||||
sheerka.set_isa(context, sheerka.new("thirties"), sheerka.new("number"))
|
||||
|
||||
forties = cls.update_bnf(context, Concept("forties",
|
||||
definition="forty number",
|
||||
where="number < 10",
|
||||
body="forty + number").def_var("forty").def_var("number"))
|
||||
cmap["forties"] = sheerka.create_new_concept(context, forties).body.body
|
||||
sheerka.set_isa(context, sheerka.new("forties"), sheerka.new("number"))
|
||||
|
||||
fifties = cls.update_bnf(context, Concept("fifties",
|
||||
definition="fifty number",
|
||||
where="number < 10",
|
||||
body="fifty + number").def_var("fifty").def_var("number"))
|
||||
cmap["fifties"] = sheerka.create_new_concept(context, fifties).body.body
|
||||
sheerka.set_isa(context, sheerka.new("fifties"), sheerka.new("number"))
|
||||
|
||||
def init_parser(self, my_concepts_map=None, init_from_sheerka=False, **kwargs):
|
||||
if my_concepts_map is not None:
|
||||
sheerka, context, *updated = self.init_concepts(*my_concepts_map.values(), **kwargs)
|
||||
@@ -174,6 +231,7 @@ class TestBnfNodeParser(TestUsingMemoryBasedSheerka):
|
||||
self.validate_get_concepts_sequences(my_map, text, expected)
|
||||
|
||||
def test_i_can_use_skip_whitespace_when_mixing_sequence_and_strmatch(self):
|
||||
# to match '--filter' in one word
|
||||
my_map = {
|
||||
"filter": self.bnf_concept("filter",
|
||||
Sequence(StrMatch("-", skip_whitespace=False),
|
||||
@@ -236,20 +294,50 @@ class TestBnfNodeParser(TestUsingMemoryBasedSheerka):
|
||||
|
||||
self.validate_get_concepts_sequences(my_map, text, expected)
|
||||
|
||||
@pytest.mark.parametrize("concept_three, expected", [
|
||||
(Concept("three"), []),
|
||||
(BaseTest.bnf_concept("three", StrMatch("three")), [UTN('twenty '), "three"])
|
||||
])
|
||||
def test_i_can_manage_sequence_with_wrong_order_choice(self, concept_three, expected):
|
||||
my_map = {
|
||||
"foo": self.bnf_concept("foo",
|
||||
Sequence(
|
||||
StrMatch("twenty"),
|
||||
OrderedChoice(StrMatch("one"), StrMatch("two")))),
|
||||
"three": concept_three}
|
||||
|
||||
text = "twenty three"
|
||||
self.validate_get_concepts_sequences(my_map, text, expected)
|
||||
|
||||
@pytest.mark.parametrize("text, expected", [
|
||||
("thirty one ok", [CNC("foo", source="thirty one ok")]),
|
||||
("twenty one ok", [CNC("foo", source="twenty one ok")]),
|
||||
("ok thirty one", [CNC("foo", source="ok thirty one")]),
|
||||
("ok twenty one", [CNC("foo", source="ok twenty one")]),
|
||||
("ok one", []),
|
||||
])
|
||||
def test_i_can_mix_sequence_and_ordered(self, text, expected):
|
||||
my_map = {
|
||||
"foo": self.bnf_concept("foo",
|
||||
Sequence(
|
||||
StrMatch("ok"),
|
||||
OrderedChoice(StrMatch("twenty"), StrMatch("thirty")),
|
||||
StrMatch("one"),
|
||||
StrMatch("ok"))
|
||||
StrMatch("one"))
|
||||
)}
|
||||
self.validate_get_concepts_sequences(my_map, text, expected)
|
||||
|
||||
@pytest.mark.parametrize("text, expected", [
|
||||
# ("twenty one", [CNC("foo", source="twenty one")]),
|
||||
# ("twenty three", []), # three does not exist
|
||||
("twenty four", []), # four exists but should not be seen
|
||||
])
|
||||
def test_i_can_mix_sequence_and_ordered_2(self, text, expected):
|
||||
my_map = {
|
||||
"foo": self.bnf_concept("foo",
|
||||
Sequence(
|
||||
StrMatch("twenty"),
|
||||
OrderedChoice(StrMatch("one"), StrMatch("two")))),
|
||||
"four": Concept("four")}
|
||||
self.validate_get_concepts_sequences(my_map, text, expected)
|
||||
|
||||
@pytest.mark.parametrize("text, expected", [
|
||||
("twenty thirty", [CNC("foo", source="twenty thirty")]),
|
||||
("one", [CNC("foo", source="one")]),
|
||||
@@ -531,6 +619,7 @@ class TestBnfNodeParser(TestUsingMemoryBasedSheerka):
|
||||
"bar": self.bnf_concept("bar", Sequence(
|
||||
ConceptExpression("foo"),
|
||||
OrderedChoice(StrMatch("one"), StrMatch("two")))),
|
||||
"three": Concept("three")
|
||||
}
|
||||
|
||||
text = "twenty two"
|
||||
@@ -553,6 +642,33 @@ class TestBnfNodeParser(TestUsingMemoryBasedSheerka):
|
||||
}
|
||||
assert concept_bar.compiled["foo"].compiled == {ConceptParts.BODY: DoNotResolve("thirty")}
|
||||
|
||||
text = "thirty three"
|
||||
expected = [[CN("foo", source="thirty"), CN("three")], []]
|
||||
self.validate_get_concepts_sequences(my_map, text, expected, multiple_result=True)
|
||||
|
||||
def test_i_can_mix_reference_to_other_concepts_2(self):
|
||||
# this time, we use concept expression
|
||||
my_map = {
|
||||
"twenty": self.bnf_concept("twenty", StrMatch("twenty")),
|
||||
"number": self.bnf_concept("number", OrderedChoice(StrMatch("one"), StrMatch("two"))),
|
||||
"twenties": self.bnf_concept("twenties",
|
||||
Sequence(ConceptExpression("twenty"), ConceptExpression("number"))),
|
||||
"three": Concept("three")
|
||||
}
|
||||
|
||||
text = "twenty two"
|
||||
|
||||
expected = [CNC("twenties",
|
||||
source="twenty two",
|
||||
twenty=CC("twenty", body=DoNotResolve("twenty")),
|
||||
number=CC("number", source="two", body=DoNotResolve("two"))
|
||||
)]
|
||||
self.validate_get_concepts_sequences(my_map, text, expected)
|
||||
|
||||
text = "twenty three"
|
||||
expected = [[CN("twenty"), CN("three")], []]
|
||||
self.validate_get_concepts_sequences(my_map, text, expected, multiple_result=True)
|
||||
|
||||
def test_i_can_mix_reference_to_other_concepts_when_body(self):
|
||||
my_map = {
|
||||
"foo": self.bnf_concept(Concept("foo", body="'foo'"),
|
||||
@@ -654,12 +770,12 @@ class TestBnfNodeParser(TestUsingMemoryBasedSheerka):
|
||||
'one': my_map["one"],
|
||||
ConceptParts.BODY: DoNotResolve(value='twenty one')}
|
||||
|
||||
@pytest.mark.parametrize("bar_expr", [
|
||||
ConceptExpression("foo"),
|
||||
OrderedChoice(ConceptExpression("foo"), StrMatch("one")),
|
||||
Sequence(StrMatch("one"), ConceptExpression("foo"), StrMatch("two"))
|
||||
@pytest.mark.parametrize("bar_expr, expected", [
|
||||
(ConceptExpression("foo"), {}),
|
||||
(OrderedChoice(ConceptExpression("foo"), StrMatch("one")), {'one': ['1002']}),
|
||||
(Sequence(StrMatch("one"), ConceptExpression("foo"), StrMatch("two")), {'one': ['1001', '1002']})
|
||||
])
|
||||
def test_i_can_detect_infinite_recursion(self, bar_expr):
|
||||
def test_i_can_detect_infinite_recursion(self, bar_expr, expected):
|
||||
my_map = {
|
||||
"foo": self.bnf_concept("foo", ConceptExpression("bar")),
|
||||
"bar": self.bnf_concept("bar", bar_expr),
|
||||
@@ -669,14 +785,64 @@ class TestBnfNodeParser(TestUsingMemoryBasedSheerka):
|
||||
parser.context = context
|
||||
parser.sheerka = sheerka
|
||||
|
||||
parsing_expression = parser.get_parsing_expression(my_map["foo"])
|
||||
# every obvious cyclic recursion are removed from concept_by_first_keyword dict
|
||||
parser.init_from_concepts(context, my_map.values())
|
||||
assert parser.concepts_by_first_keyword == expected
|
||||
|
||||
# get_parsing_expression() also returns CHICKEN_AND_EGG
|
||||
parsing_expression = parser.get_parsing_expression(context, my_map["foo"])
|
||||
assert sheerka.isinstance(parsing_expression, BuiltinConcepts.CHICKEN_AND_EGG)
|
||||
assert sheerka.isinstance(parser.concepts_grammars.get(my_map["foo"].id), BuiltinConcepts.CHICKEN_AND_EGG)
|
||||
|
||||
parsing_expression = parser.get_parsing_expression(my_map["bar"])
|
||||
parsing_expression = parser.get_parsing_expression(context, my_map["bar"])
|
||||
assert sheerka.isinstance(parsing_expression, BuiltinConcepts.CHICKEN_AND_EGG)
|
||||
assert sheerka.isinstance(parser.concepts_grammars.get(my_map["bar"].id), BuiltinConcepts.CHICKEN_AND_EGG)
|
||||
|
||||
def test_i_can_detect_longer_infinite_recursion(self):
|
||||
my_map = {
|
||||
"foo": self.bnf_concept("foo", ConceptExpression("bar")),
|
||||
"bar": self.bnf_concept("bar", ConceptExpression("baz")),
|
||||
"baz": self.bnf_concept("baz", ConceptExpression("qux")),
|
||||
"qux": self.bnf_concept("qux", ConceptExpression("foo")),
|
||||
}
|
||||
|
||||
sheerka, context, parser = self.init_parser(my_map, singleton=True)
|
||||
parser.context = context
|
||||
parser.sheerka = sheerka
|
||||
|
||||
# every obvious cyclic recursion are removed from concept_by_first_keyword dict
|
||||
parser.init_from_concepts(context, my_map.values())
|
||||
assert parser.concepts_by_first_keyword == {}
|
||||
|
||||
parsing_expression = parser.get_parsing_expression(context, my_map["foo"])
|
||||
assert sheerka.isinstance(parsing_expression, BuiltinConcepts.CHICKEN_AND_EGG)
|
||||
assert sheerka.isinstance(parser.concepts_grammars.get(my_map["foo"].id), BuiltinConcepts.CHICKEN_AND_EGG)
|
||||
assert parser.concepts_grammars.get(my_map["foo"].id).body == {"1001", "1002", "1003", "1004"}
|
||||
|
||||
assert sheerka.isinstance(parser.concepts_grammars.get(my_map["bar"].id), BuiltinConcepts.CHICKEN_AND_EGG)
|
||||
assert sheerka.isinstance(parser.concepts_grammars.get(my_map["baz"].id), BuiltinConcepts.CHICKEN_AND_EGG)
|
||||
assert sheerka.isinstance(parser.concepts_grammars.get(my_map["qux"].id), BuiltinConcepts.CHICKEN_AND_EGG)
|
||||
|
||||
@pytest.mark.parametrize("expr, expected", [
|
||||
(OrderedChoice(StrMatch("bar"), ConceptExpression("foo")), False),
|
||||
(OrderedChoice(ConceptExpression("foo"), StrMatch("bar")), True),
|
||||
(OrderedChoice(Sequence(StrMatch("bar"), ConceptExpression("foo")), StrMatch("baz")), False),
|
||||
(OrderedChoice(Sequence(ConceptExpression("foo"), StrMatch("bar")), StrMatch("baz")), True)
|
||||
])
|
||||
def test_i_can_detect_ordered_choice_infinite_recursion(self, expr, expected):
|
||||
my_map = {
|
||||
"foo": self.bnf_concept("foo", expr),
|
||||
}
|
||||
|
||||
sheerka, context, parser = self.init_parser(my_map, singleton=True)
|
||||
parser.init_from_concepts(context, my_map.values())
|
||||
parser.context = context
|
||||
parser.sheerka = sheerka
|
||||
|
||||
res = parser.get_parsing_expression(context, my_map["foo"])
|
||||
assert sheerka.isinstance(res, BuiltinConcepts.CHICKEN_AND_EGG) == expected
|
||||
|
||||
|
||||
def test_i_can_get_parsing_expression_when_concept_isa(self):
|
||||
my_map = {
|
||||
"one": Concept("one"),
|
||||
@@ -690,15 +856,87 @@ class TestBnfNodeParser(TestUsingMemoryBasedSheerka):
|
||||
sheerka.set_isa(context, sheerka.new("one"), my_map["number"])
|
||||
sheerka.set_isa(context, sheerka.new("twenty"), my_map["number"])
|
||||
|
||||
parsing_expression = parser.get_parsing_expression(my_map["twenties"])
|
||||
parser.concepts_grammars.clear() # make sure parsing expression is created from scratch
|
||||
|
||||
parsing_expression = parser.get_parsing_expression(context, my_map["twenties"])
|
||||
assert parsing_expression == Sequence(
|
||||
ConceptExpression(my_map["twenty"], rule_name="twenty"),
|
||||
ConceptExpression(my_map["number"], rule_name="number"))
|
||||
|
||||
assert parsing_expression.nodes[0].nodes == [StrMatch("twenty")]
|
||||
assert isinstance(parsing_expression.nodes[1].nodes[0], OrderedChoice)
|
||||
assert ConceptExpression(my_map["one"], rule_name="one") in parsing_expression.nodes[1].nodes[0].elements
|
||||
assert ConceptExpression(my_map["twenty"], rule_name="twenty") in parsing_expression.nodes[1].nodes[0].elements
|
||||
assert len(parsing_expression.nodes) == len(parsing_expression.elements)
|
||||
twenty_nodes = parsing_expression.nodes[0].nodes
|
||||
assert twenty_nodes == [StrMatch("twenty")]
|
||||
|
||||
number_nodes = parsing_expression.nodes[1].nodes
|
||||
assert len(number_nodes) == 1
|
||||
assert isinstance(number_nodes[0], OrderedChoice)
|
||||
assert len(number_nodes[0].nodes) == len(number_nodes[0].elements)
|
||||
assert ConceptExpression(my_map["one"], rule_name="one") in number_nodes[0].nodes
|
||||
assert ConceptExpression(my_map["twenty"], rule_name="twenty") in number_nodes[0].nodes
|
||||
|
||||
assert my_map["number"].id not in parser.concepts_grammars
|
||||
|
||||
#
|
||||
# def test_i_cannot_get_parsing_expression_when_concept_is_part_of_a_group(self):
|
||||
# """
|
||||
# In this test, twenties isa number
|
||||
# # So 'number' in Sequence(thirty, number) will spawn 'twenties' which, because there is no other indication,
|
||||
# # will create an infinite loop
|
||||
# :return:
|
||||
# """
|
||||
# my_map = {
|
||||
# "one": Concept("one"),
|
||||
# "twenty": Concept("twenty"),
|
||||
# "number": Concept("number"),
|
||||
# "twenties": self.bnf_concept("twenties", Sequence(ConceptExpression("twenty"), ConceptExpression("number")))
|
||||
# }
|
||||
# sheerka, context, parser = self.init_parser(my_map, singleton=True)
|
||||
# parser.context = context
|
||||
# parser.sheerka = sheerka
|
||||
# sheerka.set_isa(context, sheerka.new("one"), my_map["number"])
|
||||
# sheerka.set_isa(context, sheerka.new("twenty"), my_map["number"])
|
||||
# sheerka.set_isa(context, sheerka.new("twenties"), my_map["number"]) # <- twenties is also a number
|
||||
#
|
||||
# parser.concepts_grammars.clear() # make sure parsing expression is created from scratch
|
||||
#
|
||||
# parsing_expression = parser.get_parsing_expression(context, my_map["twenties"])
|
||||
# assert sheerka.isinstance(parsing_expression, BuiltinConcepts.CHICKEN_AND_EGG)
|
||||
# assert parsing_expression.body == {my_map["twenties"].id, my_map["number"].id}
|
||||
#
|
||||
# assert isinstance(parser.concepts_grammars.get(my_map["one"].id), ParsingExpression)
|
||||
# assert isinstance(parser.concepts_grammars.get(my_map["twenty"].id), ParsingExpression)
|
||||
|
||||
def test_i_can_get_parsing_expression_when_concept_is_part_of_a_group(self):
|
||||
my_map = {
|
||||
"one": Concept("one"),
|
||||
"twenty": Concept("twenty"),
|
||||
"number": Concept("number"),
|
||||
"twenties": self.bnf_concept("twenties", Sequence(ConceptExpression("twenty"), ConceptExpression("number")))
|
||||
}
|
||||
sheerka, context, parser = self.init_parser(my_map, singleton=True)
|
||||
parser.context = context
|
||||
parser.sheerka = sheerka
|
||||
sheerka.set_isa(context, sheerka.new("one"), my_map["number"])
|
||||
sheerka.set_isa(context, sheerka.new("twenty"), my_map["number"])
|
||||
sheerka.set_isa(context, sheerka.new("twenties"), my_map["number"]) # <- twenties is also a number
|
||||
|
||||
parser.concepts_grammars.clear() # make sure parsing expression is created from scratch
|
||||
|
||||
parsing_expression = parser.get_parsing_expression(context, my_map["twenties"])
|
||||
assert parsing_expression == Sequence(
|
||||
ConceptExpression(my_map["twenty"], rule_name="twenty"),
|
||||
ConceptExpression(my_map["number"], rule_name="number"))
|
||||
|
||||
assert len(parsing_expression.nodes) == len(parsing_expression.elements)
|
||||
twenty_nodes = parsing_expression.nodes[0].nodes
|
||||
assert twenty_nodes == [StrMatch("twenty")]
|
||||
|
||||
number_nodes = parsing_expression.nodes[1].nodes
|
||||
assert len(number_nodes) == 1
|
||||
assert isinstance(number_nodes[0], OrderedChoice)
|
||||
assert len(number_nodes[0].nodes) == len(number_nodes[0].elements)
|
||||
assert ConceptExpression(my_map["one"], rule_name="one") in number_nodes[0].nodes
|
||||
assert ConceptExpression(my_map["twenty"], rule_name="twenty") in number_nodes[0].nodes
|
||||
|
||||
def test_i_can_get_parsing_expression_when_sequence_of_concept(self):
|
||||
my_map = {
|
||||
@@ -709,7 +947,7 @@ class TestBnfNodeParser(TestUsingMemoryBasedSheerka):
|
||||
parser.context = context
|
||||
parser.sheerka = sheerka
|
||||
|
||||
parsing_expression = parser.get_parsing_expression(my_map["two_ones"])
|
||||
parsing_expression = parser.get_parsing_expression(context, my_map["two_ones"])
|
||||
assert parsing_expression == Sequence(
|
||||
ConceptExpression(my_map["one"], rule_name="one"),
|
||||
ConceptExpression(my_map["one"], rule_name="one"))
|
||||
@@ -726,7 +964,7 @@ class TestBnfNodeParser(TestUsingMemoryBasedSheerka):
|
||||
|
||||
self.validate_get_concepts_sequences(my_map, text, expected)
|
||||
|
||||
def test_i_can_recognize_unknown_then_they_look_like_known(self):
|
||||
def test_i_can_recognize_unknown_when_they_look_like_known(self):
|
||||
my_map = {
|
||||
"one two": self.bnf_concept("one two", Sequence("one", "two")),
|
||||
"three": self.bnf_concept("three")
|
||||
@@ -752,15 +990,13 @@ class TestBnfNodeParser(TestUsingMemoryBasedSheerka):
|
||||
assert len(sequence) == 1
|
||||
|
||||
@pytest.mark.parametrize("parser_input, expected_status, expected", [
|
||||
("one", True, [CNC("bnf one", source="one")]), # the bnf one is chosen
|
||||
("one two", True, [CN("one and two", source="one two")]),
|
||||
("three three three", True, [CN("one or more three", source="three three three")]),
|
||||
("twenty two", True, [CN("twenties", source="twenty two")]),
|
||||
("twenty four", True, [CN("twenties", source="twenty four")]),
|
||||
("twenty one", False, [UTN("twenty "), CN("bnf one", source="one")]),
|
||||
("twenty two + 1", True, [CN("twenties", source="twenty two"), " + 1"]),
|
||||
("baz", True, [CNC("bnf baz", source="baz")]), # the bnf one is chosen
|
||||
("foo bar", True, [CNC("foo then bar", source="foo bar", foo="foo", bar="bar")]),
|
||||
("bar", True, [CNC("foo or bar", source="bar", bar="bar", body="bar")]),
|
||||
("one plus two", True, [CNC("plus", source="one plus two", one="one", two="two")]),
|
||||
("twenty one", True, [CNC("t1", source="twenty one", unit="one", one="one")]),
|
||||
])
|
||||
def test_i_can_parse(self, parser_input, expected_status, expected):
|
||||
def test_i_can_parse_simple_expressions(self, parser_input, expected_status, expected):
|
||||
sheerka, context, parser = self.init_parser(init_from_sheerka=True)
|
||||
|
||||
res = parser.parse(context, ParserInput(parser_input))
|
||||
@@ -772,6 +1008,167 @@ class TestBnfNodeParser(TestUsingMemoryBasedSheerka):
|
||||
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
|
||||
assert concepts_nodes == expected_array
|
||||
|
||||
def test_i_can_when_multiple_times_the_same_variable(self):
|
||||
sheerka, context, parser = self.init_parser(init_from_sheerka=True)
|
||||
|
||||
text = "foo foo foo"
|
||||
expected_array = compute_expected_array(cmap, text, [CNC("one or more foo", source=text)])
|
||||
expected_array[0].compiled["foo"] = [cmap["foo"], cmap["foo"], cmap["foo"]]
|
||||
|
||||
res = parser.parse(context, ParserInput(text))
|
||||
parser_result = res.value
|
||||
concepts_nodes = res.value.value
|
||||
|
||||
assert res.status
|
||||
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
|
||||
assert concepts_nodes == expected_array
|
||||
|
||||
def test_i_can_test_when_expression_references_other_expressions(self):
|
||||
sheerka, context, parser = self.init_parser(init_from_sheerka=True)
|
||||
|
||||
text = "twenty four"
|
||||
expected = CNC("t2",
|
||||
source=text,
|
||||
unit=CC("three_four",
|
||||
source="four",
|
||||
four=CC("four", body=DoNotResolve("four")),
|
||||
body=CC("four", body=DoNotResolve("four"))),
|
||||
four="four")
|
||||
expected_array = compute_expected_array(cmap, text, [expected])
|
||||
|
||||
res = parser.parse(context, ParserInput(text))
|
||||
parser_result = res.value
|
||||
concepts_nodes = res.value.value
|
||||
|
||||
assert res.status
|
||||
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
|
||||
assert concepts_nodes == expected_array
|
||||
|
||||
# def test_i_cannot_parse_bnf_concept_mixed_with_isa_concepts(self):
|
||||
# sheerka, context, parser = self.init_parser(init_from_sheerka=True)
|
||||
#
|
||||
# # thirties = cls.update_bnf(context, Concept("thirties",
|
||||
# # definition="thirty number",
|
||||
# # where="number < 10",
|
||||
# # body="thirty + number").def_var("thirty").def_var("number"))
|
||||
# # with thirties isa number
|
||||
# # So number in 'thirty number' will spawn 'thirties' which, because there is no other indication, will
|
||||
# # create an infinite loop
|
||||
#
|
||||
# text = "thirty one"
|
||||
# expected = CNC("thirties",
|
||||
# source=text,
|
||||
# number=CC("number",
|
||||
# source="one",
|
||||
# one=CC("one", body=DoNotResolve("one")),
|
||||
# body=CC("one", body=DoNotResolve("one"))),
|
||||
# one=CC("one", body=DoNotResolve("one")),
|
||||
# thirty="thirty")
|
||||
# expected_array = compute_expected_array(cmap, text, [expected])
|
||||
#
|
||||
# res = parser.parse(context, ParserInput(text))
|
||||
# not_for_me = res.value
|
||||
# reason = res.value.body
|
||||
#
|
||||
# assert not res.status
|
||||
# assert sheerka.isinstance(not_for_me, BuiltinConcepts.NOT_FOR_ME)
|
||||
# assert sheerka.isinstance(reason, BuiltinConcepts.CHICKEN_AND_EGG)
|
||||
# assert reason.body == {cmap["thirties"].id, cmap["number"].id}
|
||||
|
||||
def test_i_can_parse_bnf_concept_mixed_with_isa_concepts(self):
|
||||
sheerka, context, parser = self.init_parser(init_from_sheerka=True)
|
||||
|
||||
# thirties = cls.update_bnf(context, Concept("thirties",
|
||||
# definition="thirty number",
|
||||
# where="number < 10",
|
||||
# body="thirty + number").def_var("thirty").def_var("number"))
|
||||
|
||||
text = "thirty one"
|
||||
expected = CNC("thirties",
|
||||
source=text,
|
||||
number=CC("number",
|
||||
source="one",
|
||||
one=CC("one", body=DoNotResolve("one")),
|
||||
body=CC("one", body=DoNotResolve("one"))),
|
||||
one=CC("one", body=DoNotResolve("one")),
|
||||
thirty="thirty")
|
||||
expected_array = compute_expected_array(cmap, text, [expected])
|
||||
|
||||
res = parser.parse(context, ParserInput(text))
|
||||
parser_result = res.value
|
||||
concepts_nodes = res.value.value
|
||||
|
||||
assert res.status
|
||||
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
|
||||
assert concepts_nodes == expected_array
|
||||
|
||||
def test_i_can_parse_bnf_concept_mixed_with_isa_concepts_2(self):
|
||||
# this time, three is a number, and also part of three_four, even if it is not relevant in t3
|
||||
sheerka, context, parser = self.init_parser(init_from_sheerka=True)
|
||||
|
||||
text = "thirty three"
|
||||
expected = CNC("thirties",
|
||||
source=text,
|
||||
number=CC("number",
|
||||
source="three",
|
||||
three=CC("three", body=DoNotResolve("three")),
|
||||
body=CC("three", body=DoNotResolve("three"))),
|
||||
three=CC("three", body=DoNotResolve("three")),
|
||||
thirty="thirty")
|
||||
expected_array = compute_expected_array(cmap, text, [expected])
|
||||
|
||||
res = parser.parse(context, ParserInput(text))
|
||||
parser_result = res.value
|
||||
concepts_nodes = res.value.value
|
||||
|
||||
assert res.status
|
||||
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
|
||||
assert concepts_nodes == expected_array
|
||||
|
||||
def test_i_can_parse_bnf_concept_mixed_with_isa_after_restart(self):
|
||||
sheerka, context, parser = self.init_parser(init_from_sheerka=True)
|
||||
sheerka.concepts_grammars.clear() # simulate restart
|
||||
for c in cmap.values():
|
||||
sheerka.get_by_id(c.id).bnf = None
|
||||
|
||||
text = "thirty three"
|
||||
expected = CNC("thirties",
|
||||
source=text,
|
||||
number=CC("number",
|
||||
source="three",
|
||||
three=CC("three", body=DoNotResolve("three")),
|
||||
body=CC("three", body=DoNotResolve("three"))),
|
||||
three=CC("three", body=DoNotResolve("three")),
|
||||
thirty="thirty")
|
||||
expected_array = compute_expected_array(cmap, text, [expected])
|
||||
|
||||
res = parser.parse(context, ParserInput(text))
|
||||
parser_result = res.value
|
||||
concepts_nodes = res.value.value
|
||||
|
||||
assert res.status
|
||||
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
|
||||
assert concepts_nodes == expected_array
|
||||
|
||||
text = "forty one"
|
||||
expected = CNC("forties",
|
||||
source=text,
|
||||
number=CC("number",
|
||||
source="one",
|
||||
one=CC("one", body=DoNotResolve("one")),
|
||||
body=CC("one", body=DoNotResolve("one"))),
|
||||
one=CC("one", body=DoNotResolve("one")),
|
||||
forty="forty")
|
||||
expected_array = compute_expected_array(cmap, text, [expected])
|
||||
|
||||
res = parser.parse(context, ParserInput(text))
|
||||
parser_result = res.value
|
||||
concepts_nodes = res.value.value
|
||||
|
||||
assert res.status
|
||||
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
|
||||
assert concepts_nodes == expected_array
|
||||
|
||||
def test_i_can_parse_when_keyword(self):
|
||||
sheerka, context, parser = self.init_parser(init_from_sheerka=True)
|
||||
|
||||
@@ -800,10 +1197,80 @@ class TestBnfNodeParser(TestUsingMemoryBasedSheerka):
|
||||
parser_result = res.value
|
||||
concepts_nodes = res.value.value
|
||||
|
||||
assert res.status == True
|
||||
assert res.status
|
||||
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
|
||||
assert concepts_nodes == expected_array
|
||||
|
||||
def test_i_can_parse_descent_grammar(self):
|
||||
my_map = {
|
||||
"factor": Concept("factor", definition="1 | 2 | 3"),
|
||||
"term": Concept("term", definition="factor ('*' factor)*"),
|
||||
"expr": Concept("expr", definition="term ('+' term)*"),
|
||||
}
|
||||
|
||||
sheerka, context, parser = self.init_parser(my_map, singleton=True)
|
||||
parser.init_from_concepts(context, my_map.values())
|
||||
|
||||
text = "1 + 2 * 3"
|
||||
|
||||
res = parser.parse(context, ParserInput(text))
|
||||
parser_result = res.value
|
||||
concepts_nodes = res.value.value
|
||||
|
||||
factor = my_map["factor"]
|
||||
term = my_map["term"]
|
||||
expr = my_map["expr"]
|
||||
|
||||
assert res.status
|
||||
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
|
||||
assert concepts_nodes == [CNC(expr,
|
||||
term=[CC(term,
|
||||
body=CC(factor, body=DoNotResolve("1")),
|
||||
factor=CC(factor, body=DoNotResolve("1"))),
|
||||
CC(term,
|
||||
body=DoNotResolve("2 * 3"),
|
||||
factor=[
|
||||
CC(factor, body=DoNotResolve("2")),
|
||||
CC(factor, body=DoNotResolve("3")),
|
||||
])],
|
||||
factor=[
|
||||
CC(factor, body=DoNotResolve("1")),
|
||||
CC(factor, body=DoNotResolve("2")),
|
||||
CC(factor, body=DoNotResolve("3"))],
|
||||
body=DoNotResolve("1 + 2 * 3"))]
|
||||
|
||||
def test_i_can_parse_recursive_descent_grammar(self):
|
||||
my_map = {
|
||||
"factor": Concept("factor", definition="1 | 2 | 3"),
|
||||
"term": self.bnf_concept("term", OrderedChoice(
|
||||
Sequence(ConceptExpression("factor"), StrMatch("*"), ConceptExpression("term")),
|
||||
ConceptExpression("factor"))),
|
||||
"expr": self.bnf_concept("expr", OrderedChoice(
|
||||
Sequence(ConceptExpression("term"), StrMatch("+"), ConceptExpression("expr")),
|
||||
ConceptExpression("term"))),
|
||||
}
|
||||
sheerka, context, parser = self.init_parser(my_map, singleton=True)
|
||||
parser.init_from_concepts(context, my_map.values())
|
||||
|
||||
text = "1 + 2 * 3"
|
||||
|
||||
res = parser.parse(context, ParserInput(text))
|
||||
# concepts_nodes = res.value.value is too complicated to be validated
|
||||
assert res.status
|
||||
|
||||
def test_i_can_parse_simple_recursive_grammar(self):
|
||||
my_map = {
|
||||
"foo": self.bnf_concept("foo", Sequence(StrMatch("foo"),
|
||||
OrderedChoice(StrMatch("bar"), ConceptExpression("foo")))),
|
||||
}
|
||||
|
||||
sheerka, context, parser = self.init_parser(my_map, singleton=True)
|
||||
parser.init_from_concepts(context, my_map.values())
|
||||
|
||||
assert parser.parse(context, ParserInput("foo bar")).status
|
||||
assert parser.parse(context, ParserInput("foo foo foo bar")).status
|
||||
assert not parser.parse(context, ParserInput("foo baz")).status
|
||||
|
||||
# @pytest.mark.parametrize("parser_input, expected", [
|
||||
# ("one", [
|
||||
# (True, [CNC("bnf_one", source="one", one="one", body="one")]),
|
||||
|
||||
@@ -54,12 +54,12 @@ class TestSyaNodeParser(TestUsingMemoryBasedSheerka):
|
||||
cmap["plus"].set_prop(BuiltinConcepts.ASSOCIATIVITY, "right")
|
||||
cmap["mult"].set_prop(BuiltinConcepts.ASSOCIATIVITY, "right")
|
||||
cmap["minus"].set_prop(BuiltinConcepts.ASSOCIATIVITY, "right")
|
||||
TestSyaNodeParser.sheerka.services[SheerkaComparisonManager.NAME].is_greater_than(context,
|
||||
BuiltinConcepts.PRECEDENCE,
|
||||
cmap["mult"], cmap["plus"])
|
||||
TestSyaNodeParser.sheerka.services[SheerkaComparisonManager.NAME].is_greater_than(context,
|
||||
BuiltinConcepts.PRECEDENCE,
|
||||
cmap["mult"], cmap["minus"])
|
||||
TestSyaNodeParser.sheerka.services[SheerkaComparisonManager.NAME].set_is_greater_than(context,
|
||||
BuiltinConcepts.PRECEDENCE,
|
||||
cmap["mult"], cmap["plus"])
|
||||
TestSyaNodeParser.sheerka.services[SheerkaComparisonManager.NAME].set_is_greater_than(context,
|
||||
BuiltinConcepts.PRECEDENCE,
|
||||
cmap["mult"], cmap["minus"])
|
||||
|
||||
# TestSyaNodeParser.sheerka.force_sya_def(context, [
|
||||
# (cmap["plus"].id, 5, SyaAssociativity.Right),
|
||||
|
||||
@@ -201,7 +201,7 @@ class TestSheerkaPickleHandler(TestUsingMemoryBasedSheerka):
|
||||
to_string = sheerkapickle.encode(sheerka, user_input)
|
||||
decoded = sheerkapickle.decode(sheerka, to_string)
|
||||
assert decoded == user_input
|
||||
assert to_string == '{"_sheerka/obj": "core.builtin_concepts.UserInputConcept", "concept/id": ["__USER_INPUT", "22"], "user_name": "my_user_name", "text": "my_text"}'
|
||||
assert to_string == '{"_sheerka/obj": "core.builtin_concepts.UserInputConcept", "concept/id": ["__USER_INPUT", "23"], "user_name": "my_user_name", "text": "my_text"}'
|
||||
|
||||
def test_i_can_encode_decode_user_input_when_tokens(self):
|
||||
sheerka = self.get_sheerka()
|
||||
@@ -213,7 +213,7 @@ class TestSheerkaPickleHandler(TestUsingMemoryBasedSheerka):
|
||||
to_string = sheerkapickle.encode(sheerka, user_input)
|
||||
decoded = sheerkapickle.decode(sheerka, to_string)
|
||||
assert decoded == sheerka.new(BuiltinConcepts.USER_INPUT, body=text, user_name="my_user_name")
|
||||
assert to_string == '{' + f'"_sheerka/obj": "core.builtin_concepts.UserInputConcept", "concept/id": ["__USER_INPUT", "22"], "user_name": "my_user_name", "text": "{text}"' + '}'
|
||||
assert to_string == '{' + f'"_sheerka/obj": "core.builtin_concepts.UserInputConcept", "concept/id": ["__USER_INPUT", "23"], "user_name": "my_user_name", "text": "{text}"' + '}'
|
||||
|
||||
def test_i_can_encode_decode_return_value(self):
|
||||
sheerka = self.get_sheerka()
|
||||
@@ -223,7 +223,7 @@ class TestSheerkaPickleHandler(TestUsingMemoryBasedSheerka):
|
||||
to_string = sheerkapickle.encode(sheerka, ret_val)
|
||||
decoded = sheerkapickle.decode(sheerka, to_string)
|
||||
assert decoded == ret_val
|
||||
assert to_string == '{"_sheerka/obj": "core.builtin_concepts.ReturnValueConcept", "concept/id": ["__RETURN_VALUE", "27"], "who": "who", "status": true, "value": 10}'
|
||||
assert to_string == '{"_sheerka/obj": "core.builtin_concepts.ReturnValueConcept", "concept/id": ["__RETURN_VALUE", "28"], "who": "who", "status": true, "value": 10}'
|
||||
|
||||
def test_i_can_encode_decode_return_value_with_parent(self):
|
||||
sheerka = self.get_sheerka()
|
||||
@@ -236,7 +236,7 @@ class TestSheerkaPickleHandler(TestUsingMemoryBasedSheerka):
|
||||
decoded = sheerkapickle.decode(sheerka, to_string)
|
||||
assert decoded == ret_val
|
||||
assert decoded.parents == ret_val.parents
|
||||
id_str = ', "concept/id": ["__RETURN_VALUE", "27"]'
|
||||
id_str = ', "concept/id": ["__RETURN_VALUE", "28"]'
|
||||
parents_str = '[{"_sheerka/obj": "core.builtin_concepts.ReturnValueConcept"' + id_str + ', "who": "parent_who", "status": true, "value": "10"}, {"_sheerka/id": 1}]'
|
||||
assert to_string == '{"_sheerka/obj": "core.builtin_concepts.ReturnValueConcept"' + id_str + ', "who": "who", "status": true, "value": 10, "parents": ' + parents_str + '}'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user