Fixed #131 : Implement ExprToConditions
Fixed #130 : ArithmeticOperatorParser Fixed #129 : python_wrapper : create_namespace Fixed #128 : ExpressionParser: Cannot parse func(x) infixed concept 'xxx'
This commit is contained in:
@@ -0,0 +1,500 @@
|
||||
import ast
|
||||
from dataclasses import dataclass
|
||||
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts_ids import BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from core.sheerka.services.SheerkaEvaluateRules import SheerkaEvaluateRules
|
||||
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||
from core.sheerka.services.SheerkaRuleManager import CompiledCondition
|
||||
from evaluators.PythonEvaluator import PythonEvaluator
|
||||
from parsers.BaseParser import ErrorSink
|
||||
from parsers.ExpressionParser import ExpressionParser
|
||||
from parsers.PythonParser import PythonNode
|
||||
from sheerkapython.ExprToConditions import ExprToConditionsVisitor
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
from tests.core.test_SheerkaRuleManager import PYTHON_EVALUATOR_NAME
|
||||
|
||||
|
||||
@dataclass
|
||||
class Obj:
|
||||
value: object
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, Obj) and self.value == other.value
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.value)
|
||||
|
||||
|
||||
cmap = {
|
||||
"one": Concept("one", body="1"),
|
||||
"two": Concept("two", body="2"),
|
||||
"three": Concept("three", body="3"),
|
||||
"twenties": Concept("twenties", definition="'twenty' (one|two)=unit", body="20 + unit").def_var("unit"),
|
||||
"equals": Concept("x equals y", pre="is_question()", body="x == y").def_var("x").def_var("y"),
|
||||
"isa1": Concept("x is a y", pre="is_question()", body="isa(x, y)").def_var("x").def_var("y"),
|
||||
"isa2": Concept("x is a y", pre="is_question()", body="isinstance(x, y)").def_var("x").def_var("y"),
|
||||
"isan1": Concept("x is an y", pre="is_question()", body="isa(x , y)").def_var("x").def_var("y"),
|
||||
"isan2": Concept("x is an y", body="set_isa(x, y)").def_var("x").def_var("y"),
|
||||
"foo": Concept("foo"),
|
||||
"bar": Concept("bar"),
|
||||
"baz": Concept("baz"),
|
||||
}
|
||||
|
||||
|
||||
class TestExprToConditionsVisitor(TestUsingMemoryBasedSheerka):
|
||||
shared_ontology = None
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
instance = cls()
|
||||
init_test_helper = instance.init_test(cache_only=False, ontology="#TestExprToConditionsVisitor#")
|
||||
sheerka, context, *updated = init_test_helper.with_concepts(*cmap.values(), create_new=True).unpack()
|
||||
for i, concept_name in enumerate(cmap):
|
||||
cmap[concept_name] = updated[i]
|
||||
|
||||
global_context = instance.get_context(sheerka, global_truth=True)
|
||||
sheerka.set_isa(global_context, cmap["baz"], cmap["foo"])
|
||||
|
||||
cls.shared_ontology = sheerka.get_ontology(context)
|
||||
sheerka.pop_ontology(context)
|
||||
|
||||
def initialize_test(self, concepts_map=None):
|
||||
if concepts_map is None:
|
||||
sheerka, context = self.init_test().unpack()
|
||||
sheerka.add_ontology(context, self.shared_ontology)
|
||||
else:
|
||||
sheerka, context, *updated = super().init_test().with_concepts(*concepts_map.values(),
|
||||
create_new=True).unpack()
|
||||
for i, concept_name in enumerate(concepts_map):
|
||||
concepts_map[concept_name] = updated[i]
|
||||
|
||||
return sheerka, context
|
||||
|
||||
@staticmethod
|
||||
def evaluate_conditions(context, conditions, namespace):
|
||||
with context.push(BuiltinConcepts.EXEC_CODE, None) as sub_context:
|
||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
|
||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_WHERE_REQUESTED)
|
||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_UNTIL_SUCCESS_REQUESTED)
|
||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_QUESTION_REQUESTED)
|
||||
sub_context.deactivate_push()
|
||||
|
||||
evaluation_service = sub_context.sheerka.services[SheerkaEvaluateRules.NAME]
|
||||
return evaluation_service.evaluate_conditions(sub_context, conditions, namespace)
|
||||
|
||||
@staticmethod
|
||||
def get_conditions_from_expression(context, expression, parser=None):
|
||||
parser = parser or ExpressionParser(old_style=False)
|
||||
error_sink = ErrorSink()
|
||||
parser_input = ParserInput(expression)
|
||||
parser.reset_parser_input(parser_input, error_sink)
|
||||
parsed = parser.parse_input(context, parser_input, error_sink)
|
||||
|
||||
assert not error_sink.has_error
|
||||
known_variables = parser.known_variables if hasattr(parser, "known_variables") else None
|
||||
visitor = ExprToConditionsVisitor(context, known_variables=known_variables)
|
||||
conditions = visitor.get_conditions(parsed)
|
||||
return conditions
|
||||
|
||||
@staticmethod
|
||||
def validate_condition(context, expression, condition, e_code, e_objects, e_variables, e_not_variables):
|
||||
sheerka = context.sheerka
|
||||
|
||||
# check what was compiled
|
||||
if e_code is None:
|
||||
# manage cases where we only check for variable existence
|
||||
assert condition.evaluator_type is None
|
||||
assert condition.return_value is None
|
||||
else:
|
||||
ast_ = ast.parse(e_code, "<source>", 'exec' if "\n" in e_code else 'eval')
|
||||
expected_python_node = PythonNode(e_code, ast_, expression)
|
||||
assert condition.evaluator_type == PYTHON_EVALUATOR_NAME
|
||||
assert sheerka.isinstance(condition.return_value, BuiltinConcepts.RETURN_VALUE)
|
||||
assert sheerka.objvalue(condition.return_value) == expected_python_node
|
||||
|
||||
# check the objects
|
||||
if e_objects is not None:
|
||||
resolved_objects = {k: v.id for k, v in condition.objects.items()}
|
||||
resolved_expected_objects = {k: cmap[v].id for k, v in e_objects.items()}
|
||||
assert resolved_objects == resolved_expected_objects
|
||||
|
||||
# check that variables detected
|
||||
if e_variables is not None:
|
||||
assert condition.variables == e_variables
|
||||
|
||||
if e_not_variables is not None:
|
||||
assert condition.not_variables == e_not_variables
|
||||
|
||||
def run_test_cases(self, context, conditions, test_suite):
|
||||
sheerka = context.sheerka
|
||||
|
||||
for test_data in test_suite:
|
||||
namespace, expected_value = test_data
|
||||
for k, v in namespace.items():
|
||||
if isinstance(v, str):
|
||||
try:
|
||||
namespace[k] = self.evaluate_from_source(context, v)
|
||||
except:
|
||||
pass
|
||||
|
||||
res = self.evaluate_conditions(context, conditions, namespace)
|
||||
value = sheerka.objvalue(res[0].body) if res else False
|
||||
assert value == expected_value
|
||||
|
||||
@staticmethod
|
||||
def evaluate_condition(context, expression, condition, objects):
|
||||
with context.push("Testing conditions SheerkaRuleManagerRulesCompilation", expression) as sub_context:
|
||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
|
||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_WHERE_REQUESTED)
|
||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_UNTIL_SUCCESS_REQUESTED)
|
||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_QUESTION_REQUESTED)
|
||||
sub_context.sheerka.add_many_to_short_term_memory(sub_context, objects)
|
||||
|
||||
evaluator = PythonEvaluator()
|
||||
for c in condition.concepts_to_reset:
|
||||
c.get_hints().is_evaluated = False
|
||||
|
||||
return evaluator.eval(sub_context, condition.return_value)
|
||||
|
||||
@pytest.mark.parametrize("expression, e_code, e_variables, e_not_variables, test_suite", [
|
||||
("var", None, {"var"}, set(), [({"var": "value"}, True), ({}, False)]),
|
||||
("True", "True", set(), set(), []),
|
||||
("var.value", None, {"var.value"}, set(), []),
|
||||
("not var", None, set(), {"var"}, [({"var": "value"}, False), ({}, True)]),
|
||||
("not not var", None, {"var"}, set(), []),
|
||||
("var and var2 and not var3", None, {"var", "var2"}, {"var3"}, []),
|
||||
("var and var.value == 3", "var.value == 3", {"var"}, set(), [({"var": Obj(3)}, True)]),
|
||||
("not v2 and v1.value == 3", "v1.value == 3", {"v1"}, {"v2"}, [({"v1": Obj(3)}, True), ({"v2": 0}, False)]),
|
||||
("func(var)", "func(var)", {"var"}, set(), []),
|
||||
("var in []", "var in []", {"var"}, set(), []),
|
||||
("a + b", "a + b", {"a", "b"}, set(), []),
|
||||
("foo x", "__o_00__", set(), set(), []), # foo is not a variable
|
||||
("foo y", "evaluate_question(__o_00__, x=y)", {"y"}, set(), []), # foo is not a variable
|
||||
("bar y", "call_concept(__o_00__, x=y)", {"y"}, set(), []), # bar is not a variable
|
||||
])
|
||||
def test_i_can_parse_and_manage_exists(self, expression, e_code, e_variables, e_not_variables, test_suite):
|
||||
sheerka, context, foo, bar = self.init_test().with_concepts(
|
||||
Concept("foo x", pre="is_question()").def_var("x"),
|
||||
Concept("bar x").def_var("x"),
|
||||
).unpack()
|
||||
conditions = self.get_conditions_from_expression(context, expression)
|
||||
|
||||
assert len(conditions) == 1
|
||||
assert isinstance(conditions[0], CompiledCondition)
|
||||
condition = conditions[0]
|
||||
|
||||
self.validate_condition(context, expression, condition, e_code, None, e_variables, e_not_variables)
|
||||
self.run_test_cases(context, conditions, test_suite)
|
||||
|
||||
@pytest.mark.parametrize("expression, e_code, e_objects, e_variables, test_suite", [
|
||||
# Concept
|
||||
("1 equals 1", "evaluate_question(__o_00__)", {"__o_00__": "equals"}, set(), [({}, True)]),
|
||||
("1 equals 2", "evaluate_question(__o_00__)", {"__o_00__": "equals"}, set(), [({}, False)]),
|
||||
("one equals one", "evaluate_question(__o_00__)", {"__o_00__": "equals"}, set(), [({}, True)]),
|
||||
("one equals two", "evaluate_question(__o_00__)", {"__o_00__": "equals"}, set(), [({}, False)]),
|
||||
("one equals twenty one", "evaluate_question(__o_00__)", {"__o_00__": "equals"}, set(), [({}, False)]),
|
||||
("self equals 1", "evaluate_question(__o_00__, x=self)", {"__o_00__": "equals"}, {"self"}, [
|
||||
({"self": 1}, True),
|
||||
({"self": 2}, False)]),
|
||||
("x equals 1", "evaluate_question(__o_00__, x=x)", {"__o_00__": "equals"}, {"x"}, [
|
||||
({"x": 1}, True),
|
||||
({"x": 2}, False)]),
|
||||
("one equals self", "evaluate_question(__o_00__, y=self)", {"__o_00__": "equals"}, {"self"}, [
|
||||
({"self": "one"}, True),
|
||||
({"self": "two"}, False)]),
|
||||
("self equals twenty two", "evaluate_question(__o_00__, x=self)", {"__o_00__": "equals"}, {"self"}, [
|
||||
({"self": "twenty two"}, True),
|
||||
({"self": "two"}, False)]
|
||||
),
|
||||
("x equals 1 and y equals 2",
|
||||
"evaluate_question(__o_00__, x=x) and evaluate_question(__o_01__, x=y)",
|
||||
{"__o_00__": "equals", "__o_01__": "equals"},
|
||||
{"x", "y"},
|
||||
[({"x": 1, "y": 2}, True), ({"x": "0", "y": "0"}, False)]
|
||||
),
|
||||
("x equals y", "__o_00__", {"__o_00__": "equals"}, set(), []),
|
||||
("func(self) equals twenty one",
|
||||
"evaluate_question(__o_00__, x=func(self))",
|
||||
{"__o_00__": "equals"},
|
||||
{"self"},
|
||||
[({"self": "twenty one", "func": lambda x: x}, True)]),
|
||||
("func(self) equals twenty one + 1",
|
||||
"evaluate_question(__o_01__, x=func(self), y=__o_00__ + 1)",
|
||||
{"__o_01__": "equals", "__o_00__": "twenties"},
|
||||
{"self"},
|
||||
[({"self": "22", "func": lambda x: x}, True)]),
|
||||
|
||||
# equality
|
||||
("a == 10", "a == 10", {}, {"a"}, [({"a": 10}, True), ({"a": 20}, False), ({}, False)]),
|
||||
("__ret.status == True", "__ret.status == True", {}, {"__ret"}, []),
|
||||
("self == sheerka", "is_sheerka(self)", {}, {"self"}, [
|
||||
({"self": "sheerka"}, True),
|
||||
({"self": "other"}, False),
|
||||
]),
|
||||
("self == BuiltinConcepts.TO_DICT", "self == BuiltinConcepts.TO_DICT", {}, {"self"}, [
|
||||
({"self": "BuiltinConcepts.TO_DICT"}, True),
|
||||
({"self": "other"}, False),
|
||||
]),
|
||||
|
||||
# other Comparisons
|
||||
("a + self > 10", "a + self > 10", {}, {"a", "self"}, [
|
||||
({"a": 10, "self": 1}, True),
|
||||
({"a": 10, "self": 0}, False),
|
||||
]),
|
||||
("10 < one + self", "10 < __o_00__ + self", {"__o_00__": "one"}, {"self"}, [
|
||||
({"self": 10}, True),
|
||||
({"self": 1}, False),
|
||||
]),
|
||||
("23 < twenty one + self", "23 < __o_00__ + self", {"__o_00__": "twenties"}, {"self"}, [
|
||||
({"self": 10}, True),
|
||||
({"self": 1}, False),
|
||||
]),
|
||||
("a equals b and c equals d",
|
||||
"evaluate_question(__o_00__, x=a, y=b) and evaluate_question(__o_01__, x=c, y=d)",
|
||||
{"__o_00__": "equals", "__o_01__": "equals"},
|
||||
{"a", "b", "c", "d"},
|
||||
[]),
|
||||
|
||||
# simple expressions
|
||||
("True", "True", {}, set(), [({}, True)]),
|
||||
("False", "False", {}, set(), [({}, False)]),
|
||||
("10 + 5", "10 + 5", {}, set(), [({}, 15)]),
|
||||
("a + self", "a + self", {}, {"a", "self"}, [({"a": 10, "self": 5}, 15)]),
|
||||
("a + twenty one", "a + __o_00__", {"__o_00__": "twenties"}, {"a"}, [({"a": 10}, 31)]),
|
||||
|
||||
# functions
|
||||
("isinstance('hello', str)", "isinstance('hello', str)", {}, set(), [({}, True)]),
|
||||
("isinstance(a, str)", "isinstance(a, str)", {}, {"a"}, [({"a": "an_str"}, True), ({"a": 1}, False)]),
|
||||
("f(BuiltinConcepts.TO_DICT)", "f(BuiltinConcepts.TO_DICT)", {}, set(), [({"f": lambda x: x}, "__TO_DICT")])
|
||||
|
||||
])
|
||||
def test_i_can_parse(self, expression, e_code, e_objects, e_variables, test_suite):
|
||||
sheerka, context = self.initialize_test()
|
||||
conditions = self.get_conditions_from_expression(context, expression)
|
||||
|
||||
assert len(conditions) == 1
|
||||
assert isinstance(conditions[0], CompiledCondition)
|
||||
condition = conditions[0]
|
||||
|
||||
self.validate_condition(context, expression, condition, e_code, e_objects, e_variables, None)
|
||||
self.run_test_cases(context, conditions, test_suite)
|
||||
|
||||
def test_i_can_force_variable(self):
|
||||
sheerka, context = self.initialize_test()
|
||||
parser = ExpressionParser(known_variables={"one"})
|
||||
conditions = self.get_conditions_from_expression(context, "one < two", parser)
|
||||
python_source = conditions[0].return_value.body.body.source
|
||||
assert python_source == "one < __o_00__"
|
||||
|
||||
resolved_objects = {k: v.id for k, v in conditions[0].objects.items()}
|
||||
resolved_expected_objects = {k: cmap[v].id for k, v in {"__o_00__": "two"}.items()}
|
||||
assert resolved_objects == resolved_expected_objects
|
||||
|
||||
def test_i_can_parse_when_variables_are_missing(self):
|
||||
sheerka, context = self.initialize_test()
|
||||
expression = "x equals 1"
|
||||
e_code = "evaluate_question(__o_00__, x=x)"
|
||||
e_objects = {"__o_00__": "equals"}
|
||||
e_variables = {"x"}
|
||||
test_suite = [({}, False), ({"y": 1}, False)]
|
||||
conditions = self.get_conditions_from_expression(context, expression)
|
||||
|
||||
assert len(conditions) == 1
|
||||
assert isinstance(conditions[0], CompiledCondition)
|
||||
condition = conditions[0]
|
||||
|
||||
self.validate_condition(context, expression, condition, e_code, e_objects, e_variables, None)
|
||||
self.run_test_cases(context, conditions, test_suite)
|
||||
|
||||
def test_question_concept_is_chosen_other_non_question_concept(self):
|
||||
sheerka, context = self.initialize_test()
|
||||
expression = "a is an b"
|
||||
e_code = "evaluate_question(__o_00__, x=a, y=b)"
|
||||
e_objects = {"__o_00__": "isan1"}
|
||||
conditions = self.get_conditions_from_expression(context, expression)
|
||||
|
||||
assert len(conditions) == 1
|
||||
assert isinstance(conditions[0], CompiledCondition)
|
||||
condition = conditions[0]
|
||||
|
||||
self.validate_condition(context, expression, condition, e_code, e_objects, None, None)
|
||||
|
||||
def test_i_can_manage_when_multiple_concepts(self):
|
||||
sheerka, context = self.initialize_test()
|
||||
expression = "a is a b"
|
||||
conditions = self.get_conditions_from_expression(context, expression)
|
||||
|
||||
assert len(conditions) == 2
|
||||
|
||||
condition = conditions[0]
|
||||
assert isinstance(condition, CompiledCondition)
|
||||
e_code = "evaluate_question(__o_00__, x=a, y=b)"
|
||||
e_objects = {"__o_00__": "isa1"}
|
||||
e_variables = {"a", "b"}
|
||||
self.validate_condition(context, expression, condition, e_code, e_objects, e_variables, None)
|
||||
|
||||
condition = conditions[1]
|
||||
assert isinstance(condition, CompiledCondition)
|
||||
e_code = "evaluate_question(__o_01__, x=a, y=b)"
|
||||
e_objects = {"__o_01__": "isa2"}
|
||||
e_variables = {"a", "b"}
|
||||
self.validate_condition(context, expression, condition, e_code, e_objects, e_variables, None)
|
||||
|
||||
# testing
|
||||
|
||||
namespace = {"a": sheerka.new("foo"), "b": sheerka.new("bar")}
|
||||
res = self.evaluate_conditions(context, conditions, namespace)
|
||||
assert len(res) == 2
|
||||
assert isinstance(res[0].value, bool) and not res[0].value
|
||||
assert isinstance(res[1].value, bool) and not res[1].value
|
||||
|
||||
namespace = {"a": sheerka.new("foo"), "b": sheerka.new("foo")}
|
||||
res = self.evaluate_conditions(context, conditions, namespace)
|
||||
assert len(res) == 1
|
||||
assert isinstance(res[0].value, bool) and res[0].value
|
||||
|
||||
namespace = {"a": sheerka.new("baz"), "b": sheerka.new("foo")}
|
||||
res = self.evaluate_conditions(context, conditions, namespace)
|
||||
assert len(res) == 1
|
||||
assert isinstance(res[0].value, bool) and res[0].value
|
||||
|
||||
def test_i_can_manage_or_expressions(self):
|
||||
sheerka, context = self.initialize_test()
|
||||
expression = "isinstance(self, foo) or self is a bar"
|
||||
conditions = self.get_conditions_from_expression(context, expression)
|
||||
|
||||
assert len(conditions) == 3
|
||||
|
||||
condition = conditions[0]
|
||||
assert isinstance(condition, CompiledCondition)
|
||||
e_code = "isinstance(self, __o_00__)"
|
||||
e_objects = {"__o_00__": "foo"}
|
||||
e_variables = {"self"}
|
||||
self.validate_condition(context, "isinstance(self, foo)", condition, e_code, e_objects, e_variables, None)
|
||||
|
||||
condition = conditions[1]
|
||||
assert isinstance(condition, CompiledCondition)
|
||||
e_code = "evaluate_question(__o_01__, x=self)"
|
||||
e_objects = {"__o_01__": "isa1"}
|
||||
e_variables = {"self"}
|
||||
self.validate_condition(context, "self is a bar", condition, e_code, e_objects, e_variables, None)
|
||||
|
||||
condition = conditions[2]
|
||||
assert isinstance(condition, CompiledCondition)
|
||||
e_code = "evaluate_question(__o_02__, x=self)"
|
||||
e_objects = {"__o_02__": "isa2"}
|
||||
e_variables = {"self"}
|
||||
self.validate_condition(context, "self is a bar", condition, e_code, e_objects, e_variables, None)
|
||||
|
||||
def test_i_can_manage_multiple_concepts_melt_with_and_expressions(self):
|
||||
sheerka, context = self.initialize_test()
|
||||
expression = "isinstance(self, foo) and self is a bar"
|
||||
conditions = self.get_conditions_from_expression(context, expression)
|
||||
|
||||
assert len(conditions) == 2
|
||||
|
||||
condition = conditions[0]
|
||||
assert isinstance(condition, CompiledCondition)
|
||||
e_code = "isinstance(self, __o_00__) and evaluate_question(__o_01__, x=self)"
|
||||
e_objects = {"__o_00__": "foo", "__o_01__": "isa1"}
|
||||
e_variables = {"self"}
|
||||
self.validate_condition(context, expression, condition, e_code, e_objects, e_variables, None)
|
||||
|
||||
condition = conditions[1]
|
||||
assert isinstance(condition, CompiledCondition)
|
||||
e_code = "isinstance(self, __o_00__) and evaluate_question(__o_02__, x=self)"
|
||||
e_objects = {"__o_00__": "foo", "__o_02__": "isa2"}
|
||||
e_variables = {"self"}
|
||||
self.validate_condition(context, expression, condition, e_code, e_objects, e_variables, None)
|
||||
|
||||
@pytest.mark.parametrize("expression, expected", [
|
||||
("self is a 'foo'", {"x is a y"}),
|
||||
("set self is a 'foo'", set()),
|
||||
])
|
||||
def test_i_can_get_concept_to_reset(self, expression, expected):
|
||||
"""
|
||||
When compiled conditions, sometimes there are concepts to reset between two usages
|
||||
:param expression:
|
||||
:param expected:
|
||||
:return:
|
||||
"""
|
||||
concepts_map = {
|
||||
"isa": Concept("x is a y", pre="is_question()").def_var("x").def_var("y"),
|
||||
"set_isa": Concept("set x is a y").def_var("x").def_var("y"),
|
||||
}
|
||||
sheerka, context = self.initialize_test(concepts_map)
|
||||
conditions = self.get_conditions_from_expression(context, expression)
|
||||
|
||||
assert len(conditions) == 1
|
||||
assert {c.name for c in conditions[0].concepts_to_reset} == expected
|
||||
|
||||
def test_i_can_reset_concepts_when_multiple_levels(self):
|
||||
"""
|
||||
When compiled conditions, sometimes there are concepts to reset between two usages
|
||||
:return:
|
||||
"""
|
||||
sheerka, context, is_instance, is_int, is_integer = self.init_concepts(
|
||||
Concept("x is an instance of y", pre="is_question()", body="isinstance(x, y)").def_var("x").def_var("y"),
|
||||
Concept("x is a int", pre="is_question()", body="x is an instance of int").def_var("x"),
|
||||
Concept("x is an integer", pre="is_question()", body="x is a int").def_var("x"),
|
||||
)
|
||||
|
||||
expression = "self is an integer"
|
||||
conditions = self.get_conditions_from_expression(context, expression)
|
||||
|
||||
assert len(conditions) == 1
|
||||
assert {c.name for c in conditions[0].concepts_to_reset} == {"x is an instance of y",
|
||||
"x is a int",
|
||||
"x is an integer"}
|
||||
|
||||
# So I can evaluate multiple times
|
||||
res = self.evaluate_condition(context, expression, conditions[0], {'self': 10})
|
||||
assert res.status
|
||||
assert sheerka.objvalue(res.body)
|
||||
|
||||
res = self.evaluate_condition(context, expression, conditions[0], {'self': "string"})
|
||||
assert res.status
|
||||
assert not sheerka.objvalue(res.body)
|
||||
|
||||
def test_i_can_reset_concepts_when_multiple_levels_and_concept_node(self):
|
||||
"""
|
||||
When compiled conditions, sometimes there are concepts to reset between two usages
|
||||
:return:
|
||||
"""
|
||||
# in this example, x + 2 is an int won't be parsed as an ExactNodeConcept, but as a ConceptNode
|
||||
sheerka, context, is_int, is_integer = self.init_concepts(
|
||||
Concept("x is a int", pre="is_question()", body="isinstance(x, int)").def_var("x"),
|
||||
Concept("x is an integer", pre="is_question()", body="x + 2 is a int").def_var("x"),
|
||||
create_new=True
|
||||
)
|
||||
|
||||
expression = "self is an integer"
|
||||
conditions = self.get_conditions_from_expression(context, expression)
|
||||
|
||||
assert len(conditions) == 1
|
||||
assert set(c.name for c in conditions[0].concepts_to_reset) == {"x is a int",
|
||||
"x is an integer"}
|
||||
|
||||
# So I can evaluate multiple times
|
||||
res = self.evaluate_condition(context, expression, conditions[0], {'self': 10})
|
||||
assert res.status
|
||||
assert sheerka.objvalue(res.body)
|
||||
|
||||
res = self.evaluate_condition(context, expression, conditions[0], {'self': "string"})
|
||||
assert not res.status
|
||||
|
||||
def test_long_name_concepts_are_not_considered_as_variables(self):
|
||||
sheerka, context, one, number = self.init_concepts(
|
||||
"one",
|
||||
"all numbers",
|
||||
)
|
||||
sheerka.set_isa(context, one, number)
|
||||
|
||||
expression = "all numbers < 5"
|
||||
conditions = self.get_conditions_from_expression(context, expression)
|
||||
|
||||
assert len(conditions) == 1
|
||||
assert conditions[0].return_value.body.body.source == '__o_00__ < 5'
|
||||
+355
-41
@@ -1,23 +1,23 @@
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts_ids import BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from core.sheerka.services.SheerkaEvaluateConcept import EvaluationHints
|
||||
from core.concept import Concept, DEFINITION_TYPE_DEF
|
||||
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||
from evaluators.PythonEvaluator import PythonEvaluator
|
||||
from parsers.BaseParser import ErrorSink
|
||||
from parsers.ExpressionParser import ExpressionParser
|
||||
from parsers.FunctionParser import FunctionParser
|
||||
from parsers.ListComprehensionParser import ListComprehensionParser
|
||||
from parsers.PythonParser import PythonNode
|
||||
from sheerkapython.ExprToPython import PythonExprVisitor
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
class TestExprToPython(TestUsingMemoryBasedSheerka):
|
||||
class TestPythonExprVisitor(TestUsingMemoryBasedSheerka):
|
||||
|
||||
@staticmethod
|
||||
def get_expr_node(context, expression, parser=None):
|
||||
parser = parser or ExpressionParser()
|
||||
parser = parser or ExpressionParser(old_style=False)
|
||||
error_sink = ErrorSink()
|
||||
parser_input = ParserInput(expression)
|
||||
parser.reset_parser_input(parser_input, error_sink)
|
||||
@@ -37,6 +37,7 @@ class TestExprToPython(TestUsingMemoryBasedSheerka):
|
||||
context.add_to_short_term_memory(k, v)
|
||||
|
||||
res = evaluator.eval(context, return_value)
|
||||
|
||||
assert res.status
|
||||
return res.body
|
||||
|
||||
@@ -49,25 +50,63 @@ class TestExprToPython(TestUsingMemoryBasedSheerka):
|
||||
("foo a or bar b",
|
||||
"call_concept(__o_00__, x=a) or call_concept(__o_01__, y=b)",
|
||||
{"__o_00__": "foo", "__o_01__": "bar"}),
|
||||
("not foo w", "not call_concept(__o_00__, x=w)", {"__o_00__": "foo"}),
|
||||
("not foo w", "not (call_concept(__o_00__, x=w))", {"__o_00__": "foo"}),
|
||||
("foo a >= bar b",
|
||||
"call_concept(__o_00__, x=a) >= call_concept(__o_01__, y=b)",
|
||||
{"__o_00__": "foo", "__o_01__": "bar"}),
|
||||
("function(foo a, bar b)",
|
||||
"function(call_concept(__o_00__, x=a), call_concept(__o_01__, y=b))",
|
||||
{"__o_00__": "foo", "__o_01__": "bar"})
|
||||
|
||||
{"__o_00__": "foo", "__o_01__": "bar"}),
|
||||
("set_isa(one, number)", "set_isa(__o_00__, __o_01__)", {"__o_00__": "one", "__o_01__": "number"}),
|
||||
("set_isa(twenty two, number)", "set_isa(__o_00__, __o_01__)", {"__o_00__": "twenties", "__o_01__": "number"}),
|
||||
("foo and twenty one", "__o_00__ and __o_01__", {"__o_00__": "foo", "__o_01__": "twenties"}),
|
||||
("func(z) is a 'foo'", "call_concept(__o_00__, x=func(z))", {"__o_00__": "isa"}),
|
||||
("isinstance('hello', str)", "isinstance('hello', str)", {}),
|
||||
("not (a and b)", "not (a and b)", {}),
|
||||
("twenty one is a number", "__o_00__", {"__o_00__": "isa"}),
|
||||
("x is a number", "call_concept(__o_00__, x=x)", {"__o_00__": "isa"}),
|
||||
("y is a number", "call_concept(__o_00__, x=y)", {"__o_00__": "isa"}),
|
||||
("number is a x", "call_concept(__o_00__, y=x)", {"__o_00__": "isa"}),
|
||||
("number is a y", "call_concept(__o_00__, y=y)", {"__o_00__": "isa"}),
|
||||
("x is a y", "__o_00__", {"__o_00__": "isa"}),
|
||||
("foo x + 1", "call_concept(__o_00__, x=x + 1)", {"__o_00__": "foo"}),
|
||||
("twenty two + 3", "__o_00__ + 3", {"__o_00__": "twenties"}),
|
||||
("twenty two * 2 - 1", "__o_00__ * 2 - 1", {"__o_00__": "twenties"}),
|
||||
("x * bar a + 2", "x * call_concept(__o_00__, y=a + 2)", {"__o_00__": "bar"}),
|
||||
("foo x * bar a + 2",
|
||||
"call_concept(__o_01__, x=x * call_concept(__o_00__, y=a + 2))",
|
||||
{"__o_00__": "bar", "__o_01__": "foo"}),
|
||||
("(foo y) * (bar a) + 2",
|
||||
"call_concept(__o_00__, x=y) * call_concept(__o_01__, y=a) + 2",
|
||||
{"__o_00__": "foo", "__o_01__": "bar"}),
|
||||
("func([one, two])", "func([__o_00__, __o_01__])", {"__o_00__": "one", "__o_01__": "two"}),
|
||||
("func([twenty one, two])", "func([__o_00__, __o_01__])", {"__o_00__": "twenties", "__o_01__": "two"}),
|
||||
("func((twenty one, two))", "func((__o_00__, __o_01__))", {"__o_00__": "twenties", "__o_01__": "two"}),
|
||||
("func({twenty one, two})", "func({__o_00__, __o_01__})", {"__o_00__": "twenties", "__o_01__": "two"}),
|
||||
("foo twenty one + self",
|
||||
"call_concept(__o_01__, x=__o_00__ + self)",
|
||||
{"__o_01__": "foo", "__o_00__": "twenties"}),
|
||||
])
|
||||
def test_i_can_compile_concept_when_is_question_is_false(self, expression, source, objects):
|
||||
sheerka, context, foo, bar = self.init_test().with_concepts(
|
||||
Concept("foo x", body="x").def_var("x"),
|
||||
sheerka, context, foo, bar, isa, one, two, twenties, number = self.init_test().with_concepts(
|
||||
Concept("foo", definition="foo x", definition_type=DEFINITION_TYPE_DEF, body="x").def_var("x"),
|
||||
Concept("bar y", body="y").def_var("y"),
|
||||
Concept("x is a y").def_var("x").def_var("y"),
|
||||
Concept("one", body="1"),
|
||||
Concept("two", body="2"),
|
||||
Concept("twenties", definition="'twenty' (one|two)=unit").def_var("unit"),
|
||||
Concept("number"),
|
||||
create_new=True
|
||||
).unpack()
|
||||
|
||||
concepts = {
|
||||
"foo": foo,
|
||||
"bar": bar
|
||||
"bar": bar,
|
||||
"isa": isa,
|
||||
"one": one,
|
||||
"two": two,
|
||||
"twenties": twenties,
|
||||
"number": number
|
||||
}
|
||||
|
||||
node = self.get_expr_node(context, expression)
|
||||
@@ -80,6 +119,7 @@ class TestExprToPython(TestUsingMemoryBasedSheerka):
|
||||
assert python_node.original_source == expression
|
||||
assert python_node.source == source
|
||||
|
||||
assert len(objects) == len(python_node.objects)
|
||||
for obj_name, obj_value in objects.items():
|
||||
assert obj_name in python_node.objects
|
||||
|
||||
@@ -87,7 +127,7 @@ class TestExprToPython(TestUsingMemoryBasedSheerka):
|
||||
if isinstance(obj, Concept):
|
||||
assert sheerka.isinstance(obj, concepts[obj_value])
|
||||
assert obj.get_hints().use_copy
|
||||
assert obj.get_hints().is_evaluated
|
||||
# assert obj.get_hints().is_evaluated
|
||||
else:
|
||||
assert False
|
||||
|
||||
@@ -106,8 +146,9 @@ class TestExprToPython(TestUsingMemoryBasedSheerka):
|
||||
}
|
||||
|
||||
node = self.get_expr_node(context, expression)
|
||||
context.add_to_protected_hints(BuiltinConcepts.EVAL_QUESTION_REQUESTED)
|
||||
visitor = PythonExprVisitor(context)
|
||||
ret = visitor.compile(node, EvaluationHints(eval_body=True, eval_question=True))
|
||||
ret = visitor.compile(node)
|
||||
|
||||
assert len(ret) == 1
|
||||
python_node = ret[0].body.body
|
||||
@@ -125,6 +166,61 @@ class TestExprToPython(TestUsingMemoryBasedSheerka):
|
||||
else:
|
||||
assert False
|
||||
|
||||
@pytest.mark.parametrize("expression, expected", [
|
||||
("foo w", [
|
||||
("call_concept(__o_00__, x=w)", {"__o_00__": "foo1"}),
|
||||
("call_concept(__o_01__, y=w)", {"__o_01__": "foo2"})
|
||||
]),
|
||||
("foo z + 2", [
|
||||
("call_concept(__o_00__, x=z + 2)", {"__o_00__": "foo1"}),
|
||||
("call_concept(__o_01__, y=z + 2)", {"__o_01__": "foo2"}),
|
||||
]),
|
||||
("foo bar a + 2", [
|
||||
("call_concept(__o_00__, x=call_concept(__o_01__, x=a + 2))", {"__o_00__": "foo1", "__o_01__": "bar1"}),
|
||||
("call_concept(__o_02__, y=call_concept(__o_03__, x=a + 2))", {"__o_02__": "foo2", "__o_03__": "bar1"}),
|
||||
("call_concept(__o_04__, y=call_concept(__o_05__, y=a + 2))", {"__o_04__": "foo2", "__o_05__": "bar2"}),
|
||||
("call_concept(__o_06__, x=call_concept(__o_07__, y=a + 2))", {"__o_06__": "foo1", "__o_07__": "bar2"}),
|
||||
]),
|
||||
])
|
||||
def test_i_can_compile_when_multiple_concept_are_possible(self, expression, expected):
|
||||
sheerka, context, foo1, foo2, bar1, bar2, = self.init_test().with_concepts(
|
||||
Concept("foo x", body="x").def_var("x"),
|
||||
Concept("foo y", body="y").def_var("y"),
|
||||
Concept("bar x", body="x").def_var("x"),
|
||||
Concept("bar y", body="y").def_var("y"),
|
||||
create_new=True
|
||||
).unpack()
|
||||
|
||||
concepts = {
|
||||
"foo1": foo1,
|
||||
"foo2": foo2,
|
||||
"bar1": bar1,
|
||||
"bar2": bar2
|
||||
}
|
||||
|
||||
node = self.get_expr_node(context, expression)
|
||||
visitor = PythonExprVisitor(context)
|
||||
|
||||
ret = visitor.compile(node)
|
||||
|
||||
assert len(ret) == len(expected)
|
||||
|
||||
for ret, (source, objects) in zip(ret, expected):
|
||||
python_node = ret.body.body
|
||||
assert python_node.original_source == expression
|
||||
assert python_node.source == source
|
||||
|
||||
for obj_name, obj_value in objects.items():
|
||||
assert obj_name in python_node.objects
|
||||
|
||||
obj = python_node.objects[obj_name]
|
||||
if isinstance(obj, Concept):
|
||||
assert sheerka.isinstance(obj, concepts[obj_value])
|
||||
assert obj.get_hints().use_copy
|
||||
assert obj.get_hints().is_evaluated
|
||||
else:
|
||||
assert False
|
||||
|
||||
def test_i_can_compile_simple_list_comprehension(self):
|
||||
sheerka, context = self.init_test().unpack()
|
||||
|
||||
@@ -193,6 +289,39 @@ class TestExprToPython(TestUsingMemoryBasedSheerka):
|
||||
|
||||
assert self.eval(context, ret[0]) == ["a", "b"]
|
||||
|
||||
def test_i_can_compile_list_comprehension_when_element_is_a_concept_using_bnf(self):
|
||||
sheerka, context, one, two, twenties, foo = self.init_test().with_concepts(
|
||||
Concept("one", body="1"),
|
||||
Concept("two", body="2"),
|
||||
Concept("twenties", definition="'twenty' (one|two)=unit").def_var("unit"),
|
||||
Concept("foo x y", body="{x, y}").def_var("x").def_var("y"),
|
||||
create_new=True,
|
||||
).unpack()
|
||||
|
||||
expression = "[ foo x twenty one for x in ['a', 'b'] ]"
|
||||
node = self.get_expr_node(context, expression, parser=ListComprehensionParser())
|
||||
visitor = PythonExprVisitor(context)
|
||||
|
||||
ret = visitor.compile(node)
|
||||
|
||||
assert len(ret) == 1
|
||||
assert sheerka.isinstance(ret[0], BuiltinConcepts.RETURN_VALUE)
|
||||
assert sheerka.isinstance(ret[0].body, BuiltinConcepts.PARSER_RESULT)
|
||||
assert isinstance(ret[0].body.body, PythonNode)
|
||||
|
||||
python_node = ret[0].body.body
|
||||
assert python_node.original_source == expression
|
||||
assert python_node.source == "[ call_concept(__o_00__, x=x) for x in ['a', 'b'] ]"
|
||||
assert "__o_00__" in python_node.objects
|
||||
|
||||
concept = python_node.objects["__o_00__"]
|
||||
assert sheerka.isinstance(concept, foo)
|
||||
assert concept.get_hints().use_copy
|
||||
assert concept.get_hints().is_evaluated
|
||||
|
||||
twenty_one = self.evaluate_from_source(context, "twenty one")
|
||||
assert self.eval(context, ret[0]) == [{"a", twenty_one}, {"b", twenty_one}]
|
||||
|
||||
def test_i_can_compile_list_comprehension_when_concept_with_complex_parameter(self):
|
||||
sheerka, context, foo = self.init_test().with_concepts(
|
||||
Concept("foo x", body="x").def_var("x"),
|
||||
@@ -242,19 +371,56 @@ class TestExprToPython(TestUsingMemoryBasedSheerka):
|
||||
|
||||
python_node = ret[0].body.body
|
||||
assert python_node.original_source == expression
|
||||
assert python_node.source == "[ call_concept(__o_00__, x=x) for x in call_concept(__o_01__) ]"
|
||||
assert python_node.source == "[ call_concept(__o_01__, x=x) for x in __o_00__ ]"
|
||||
assert "__o_00__" in python_node.objects
|
||||
assert "__o_01__" in python_node.objects
|
||||
|
||||
concept0 = python_node.objects["__o_00__"]
|
||||
concept0 = python_node.objects["__o_01__"]
|
||||
assert sheerka.isinstance(concept0, foo)
|
||||
assert concept0.get_hints().use_copy
|
||||
assert concept0.get_hints().is_evaluated
|
||||
|
||||
concept1 = python_node.objects["__o_01__"]
|
||||
concept1 = python_node.objects["__o_00__"]
|
||||
assert sheerka.isinstance(concept1, "colors")
|
||||
|
||||
assert set(self.eval(context, ret[0])) == {red, blue}
|
||||
|
||||
def test_i_can_compile_list_comprehension_when_target_is_the_name_of_a_concept_concept(self):
|
||||
sheerka, context, red, blue, color, foo = self.init_test().with_concepts(
|
||||
"red",
|
||||
"blue",
|
||||
"color",
|
||||
Concept("foo x", body="x").def_var("x")
|
||||
).unpack()
|
||||
|
||||
global_truth_context = self.get_context(sheerka, global_truth=True)
|
||||
sheerka.set_isa(global_truth_context, red, color)
|
||||
sheerka.set_isa(global_truth_context, blue, color)
|
||||
|
||||
expression = "[ foo color for color in colors ]"
|
||||
node = self.get_expr_node(context, expression, parser=ListComprehensionParser())
|
||||
visitor = PythonExprVisitor(context)
|
||||
|
||||
ret = visitor.compile(node)
|
||||
|
||||
assert len(ret) == 1
|
||||
assert sheerka.isinstance(ret[0], BuiltinConcepts.RETURN_VALUE)
|
||||
assert sheerka.isinstance(ret[0].body, BuiltinConcepts.PARSER_RESULT)
|
||||
assert isinstance(ret[0].body.body, PythonNode)
|
||||
|
||||
python_node = ret[0].body.body
|
||||
assert python_node.original_source == expression
|
||||
assert python_node.source == "[ call_concept(__o_01__, x=color) for color in __o_00__ ]"
|
||||
assert "__o_00__" in python_node.objects
|
||||
assert "__o_01__" in python_node.objects
|
||||
|
||||
concept0 = python_node.objects["__o_01__"]
|
||||
assert sheerka.isinstance(concept0, foo)
|
||||
assert concept0.get_hints().use_copy
|
||||
assert concept0.get_hints().is_evaluated
|
||||
|
||||
concept1 = python_node.objects["__o_00__"]
|
||||
assert sheerka.isinstance(concept1, "colors")
|
||||
assert concept1.get_hints().use_copy
|
||||
assert concept1.get_hints().is_evaluated
|
||||
|
||||
assert set(self.eval(context, ret[0])) == {red, blue}
|
||||
|
||||
@@ -264,7 +430,8 @@ class TestExprToPython(TestUsingMemoryBasedSheerka):
|
||||
"blue",
|
||||
"color",
|
||||
Concept("foo x", body="x").def_var("x"),
|
||||
Concept("x starts with y", body="x.name.startswith(y)", pre="is_question()").def_var("x").def_var("y")
|
||||
Concept("x starts with y", body="x.name.startswith(y)", pre="is_question()").def_var("x").def_var("y"),
|
||||
create_new=True
|
||||
).unpack()
|
||||
global_truth_context = self.get_context(sheerka, global_truth=True)
|
||||
sheerka.set_isa(global_truth_context, red, color)
|
||||
@@ -284,7 +451,7 @@ class TestExprToPython(TestUsingMemoryBasedSheerka):
|
||||
|
||||
python_node = ret[0].body.body
|
||||
assert python_node.original_source == expression
|
||||
assert python_node.source == "[ call_concept(__o_00__, x=x) for x in call_concept(__o_01__) if evaluate_question(__o_02__, x=x, y='b') ]"
|
||||
assert python_node.source == "[ call_concept(__o_02__, x=x) for x in __o_00__ if evaluate_question(__o_01__, x=x) ]"
|
||||
assert "__o_00__" in python_node.objects
|
||||
assert "__o_01__" in python_node.objects
|
||||
assert "__o_02__" in python_node.objects
|
||||
@@ -312,15 +479,15 @@ class TestExprToPython(TestUsingMemoryBasedSheerka):
|
||||
assert len(ret) == 8
|
||||
python_node = ret[0].body.body
|
||||
object_to_compare = {k: v.name for k, v in python_node.objects.items()}
|
||||
assert python_node.source == '[ call_concept(__o_00__, x=a) for a in call_concept(__o_02__) if evaluate_question(__o_04__, x=a) ]'
|
||||
assert object_to_compare == {"__o_00__": "foo x", "__o_02__": "colors", "__o_04__": "bar x"}
|
||||
assert python_node.source == '[ call_concept(__o_04__, x=a) for a in __o_00__ if evaluate_question(__o_02__, x=a) ]'
|
||||
assert object_to_compare == {"__o_04__": "foo x", "__o_00__": "colors", "__o_02__": "bar x"}
|
||||
|
||||
# ...
|
||||
|
||||
python_node = ret[7].body.body
|
||||
object_to_compare = {k: v.name for k, v in python_node.objects.items()}
|
||||
assert python_node.source == '[ call_concept(__o_01__, y=a) for a in call_concept(__o_03__) if evaluate_question(__o_05__, y=a) ]'
|
||||
assert object_to_compare == {"__o_01__": "foo y", "__o_03__": "colors", "__o_05__": "bar y"}
|
||||
assert python_node.source == '[ call_concept(__o_05__, y=a) for a in __o_01__ if evaluate_question(__o_03__, y=a) ]'
|
||||
assert object_to_compare == {"__o_05__": "foo y", "__o_01__": "colors", "__o_03__": "bar y"}
|
||||
|
||||
def test_i_can_compile_list_comprehension_when_missing_concept_parameter(self):
|
||||
sheerka, context, foo = self.init_test().with_concepts(
|
||||
@@ -339,7 +506,7 @@ class TestExprToPython(TestUsingMemoryBasedSheerka):
|
||||
assert python_node.source == "[ call_concept(__o_00__, x=x, y=k) for x in ['a', 'b'] ]"
|
||||
assert object_to_compare == {"__o_00__": "foo x y"}
|
||||
|
||||
def test_i_can_compile_simple_list_comprehension_when_multiple_for(self):
|
||||
def test_i_can_compile_list_comprehension_when_multiple_for(self):
|
||||
sheerka, context = self.init_test().unpack()
|
||||
|
||||
expression = "[ (x, y) for x in ['a', 'b'] if x == 'a' for y in ['c', 'd'] if y == 'c' ]"
|
||||
@@ -359,7 +526,66 @@ class TestExprToPython(TestUsingMemoryBasedSheerka):
|
||||
|
||||
assert self.eval(context, ret[0]) == [("a", "c")]
|
||||
|
||||
def test_i_can_compile_and_when_multiple_results(self):
|
||||
def test_i_can_compile_list_comprehension_when_element_is_missing_its_parenthesis(self):
|
||||
sheerka, context, foo = self.init_test().with_concepts(
|
||||
Concept("foo x", body="x").def_var("x")
|
||||
).unpack()
|
||||
|
||||
expression = "[ w, foo w for w in ['a', 'b'] ]"
|
||||
node = self.get_expr_node(context, expression, parser=ListComprehensionParser())
|
||||
visitor = PythonExprVisitor(context)
|
||||
|
||||
ret = visitor.compile(node)
|
||||
|
||||
assert len(ret) == 1
|
||||
assert sheerka.isinstance(ret[0], BuiltinConcepts.RETURN_VALUE)
|
||||
assert sheerka.isinstance(ret[0].body, BuiltinConcepts.PARSER_RESULT)
|
||||
assert isinstance(ret[0].body.body, PythonNode)
|
||||
|
||||
python_node = ret[0].body.body
|
||||
assert python_node.original_source == expression
|
||||
assert python_node.source == "[ (w, call_concept(__o_00__, x=w)) for w in ['a', 'b'] ]"
|
||||
assert "__o_00__" in python_node.objects
|
||||
|
||||
concept = python_node.objects["__o_00__"]
|
||||
assert sheerka.isinstance(concept, foo)
|
||||
assert concept.get_hints().use_copy
|
||||
assert concept.get_hints().is_evaluated
|
||||
|
||||
assert self.eval(context, ret[0]) == [("a", "a"), ("b", "b")]
|
||||
|
||||
def test_i_can_compile_list_comprehension_when_multiple_targets(self):
|
||||
sheerka, context, foo = self.init_test().with_concepts(
|
||||
Concept("foo x y", body="(x, y)").def_var("x").def_var("y")
|
||||
).unpack()
|
||||
|
||||
items = {"one": 1, "two": 2}
|
||||
sheerka.add_to_short_term_memory(None, "items", items)
|
||||
|
||||
expression = "[ foo x y for x, y in items.items() ]"
|
||||
node = self.get_expr_node(context, expression, parser=ListComprehensionParser())
|
||||
visitor = PythonExprVisitor(context)
|
||||
|
||||
ret = visitor.compile(node)
|
||||
|
||||
assert len(ret) == 1
|
||||
assert sheerka.isinstance(ret[0], BuiltinConcepts.RETURN_VALUE)
|
||||
assert sheerka.isinstance(ret[0].body, BuiltinConcepts.PARSER_RESULT)
|
||||
assert isinstance(ret[0].body.body, PythonNode)
|
||||
|
||||
python_node = ret[0].body.body
|
||||
assert python_node.original_source == expression
|
||||
assert python_node.source == "[ call_concept(__o_00__, x=x, y=y) for x, y in items.items() ]"
|
||||
assert "__o_00__" in python_node.objects
|
||||
|
||||
concept0 = python_node.objects["__o_00__"]
|
||||
assert sheerka.isinstance(concept0, foo)
|
||||
assert concept0.get_hints().use_copy
|
||||
assert concept0.get_hints().is_evaluated
|
||||
|
||||
assert self.eval(context, ret[0]) == [("one", 1), ("two", 2)]
|
||||
|
||||
def test_i_can_compile_when_multiple_results(self):
|
||||
sheerka, context, foo, foo2, bar, bar2 = self.init_test().with_concepts(
|
||||
Concept("foo x", body="x").def_var("x"),
|
||||
Concept("foo y", body="y").def_var("y"),
|
||||
@@ -393,30 +619,118 @@ class TestExprToPython(TestUsingMemoryBasedSheerka):
|
||||
assert python_node.source == 'call_concept(__o_01__, y=a) and call_concept(__o_03__, y=b)'
|
||||
assert object_to_compare == {"__o_01__": "foo y", "__o_03__": "bar y"}
|
||||
|
||||
def test_i_can_compile_when_element_is_missing_its_parenthesis(self):
|
||||
sheerka, context, foo = self.init_test().with_concepts(
|
||||
Concept("foo x", body="x").def_var("x")
|
||||
def test_i_can_compile_function_of_function(self):
|
||||
sheerka, context, one, two = self.init_test().with_concepts(
|
||||
Concept("one"),
|
||||
Concept("two"),
|
||||
).unpack()
|
||||
|
||||
expression = "[ w, foo w for w in ['a', 'b'] ]"
|
||||
node = self.get_expr_node(context, expression, parser=ListComprehensionParser())
|
||||
expression = "func(func1(func2(one, two)), 'foo')"
|
||||
node = self.get_expr_node(context, expression)
|
||||
visitor = PythonExprVisitor(context)
|
||||
|
||||
ret = visitor.compile(node)
|
||||
|
||||
assert len(ret) == 1
|
||||
assert sheerka.isinstance(ret[0], BuiltinConcepts.RETURN_VALUE)
|
||||
assert sheerka.isinstance(ret[0].body, BuiltinConcepts.PARSER_RESULT)
|
||||
assert isinstance(ret[0].body.body, PythonNode)
|
||||
|
||||
python_node = ret[0].body.body
|
||||
assert python_node.original_source == expression
|
||||
assert python_node.source == "[ (w, call_concept(__o_00__, x=w)) for w in ['a', 'b'] ]"
|
||||
assert "__o_00__" in python_node.objects
|
||||
assert python_node.source == "func(func1(func2(__o_00__, __o_01__)), 'foo')"
|
||||
|
||||
concept = python_node.objects["__o_00__"]
|
||||
assert sheerka.isinstance(concept, foo)
|
||||
assert concept.get_hints().use_copy
|
||||
assert concept.get_hints().is_evaluated
|
||||
def test_i_can_compile_function_when_keyword_parameters(self):
|
||||
sheerka, context, foo = self.init_test().with_concepts(
|
||||
Concept("foo x", body="x").def_var("x")).unpack()
|
||||
|
||||
assert self.eval(context, ret[0]) == [("a", "a"), ("b", "b")]
|
||||
def func(**kwargs):
|
||||
# Uncomment to test that PythonEvalutor tests until success
|
||||
# if not isinstance(kwargs["y"], str):
|
||||
# raise Exception()
|
||||
return kwargs
|
||||
|
||||
sheerka.add_to_short_term_memory(None, "func", func)
|
||||
|
||||
expression = "func(x=5, y=foo 'a')"
|
||||
node = self.get_expr_node(context, expression)
|
||||
visitor = PythonExprVisitor(context)
|
||||
|
||||
ret = visitor.compile(node)
|
||||
|
||||
assert len(ret) == 1
|
||||
python_node = ret[0].body.body
|
||||
assert python_node.original_source == expression
|
||||
assert python_node.source == "func(x=5, y=__o_00__)"
|
||||
assert python_node.objects["__o_00__"].name == "foo x"
|
||||
|
||||
res = self.eval(context, ret[0])
|
||||
assert res["x"] == 5
|
||||
assert sheerka.isinstance(res["y"], foo)
|
||||
assert res["y"].body == "a"
|
||||
|
||||
def test_i_can_compile_function_when_keyword_parameters_and_multiple_results(self):
|
||||
sheerka, context, foo, foo2, bar, bar2 = self.init_test().with_concepts(
|
||||
Concept("foo x", body="x").def_var("x"),
|
||||
Concept("foo y", body="y").def_var("y"),
|
||||
Concept("bar x", body="x").def_var("x"),
|
||||
Concept("bar y", body="y").def_var("y"),
|
||||
create_new=True
|
||||
).unpack()
|
||||
|
||||
def func(**kwargs):
|
||||
return kwargs
|
||||
|
||||
sheerka.add_to_short_term_memory(None, "func", func)
|
||||
|
||||
expression = "func(foo 'a', y=bar 'b')"
|
||||
|
||||
node = self.get_expr_node(context, expression)
|
||||
visitor = PythonExprVisitor(context)
|
||||
ret = visitor.compile(node)
|
||||
|
||||
assert len(ret) == 4
|
||||
|
||||
python_node = ret[0].body.body
|
||||
object_to_compare = {k: v.name for k, v in python_node.objects.items()}
|
||||
assert python_node.source == "func(__o_00__, y=__o_02__)"
|
||||
assert object_to_compare == {"__o_00__": "foo x", "__o_02__": "bar x"}
|
||||
|
||||
# ...
|
||||
|
||||
python_node = ret[3].body.body
|
||||
object_to_compare = {k: v.name for k, v in python_node.objects.items()}
|
||||
assert python_node.source == "func(__o_01__, y=__o_03__)"
|
||||
assert object_to_compare == {"__o_01__": "foo y", "__o_03__": "bar y"}
|
||||
|
||||
def test_i_can_compile_sequence_with_function_call(self):
|
||||
sheerka, context, foo, bar = self.init_test().with_concepts(
|
||||
Concept("foo x", body="f'x={x}'").def_var("x"),
|
||||
Concept("x bar", body="x").def_var("x"),
|
||||
create_new=True
|
||||
).unpack()
|
||||
|
||||
def func(x):
|
||||
return x + 1
|
||||
|
||||
sheerka.add_to_short_term_memory(None, "func", func)
|
||||
|
||||
expression = "foo func(self)"
|
||||
|
||||
node = self.get_expr_node(context, expression, parser=FunctionParser(strict=False))
|
||||
visitor = PythonExprVisitor(context)
|
||||
ret = visitor.compile(node)
|
||||
|
||||
assert len(ret) == 1
|
||||
python_node = ret[0].body.body
|
||||
assert python_node.original_source == expression
|
||||
assert python_node.source == "call_concept(__o_00__, x=func(self))"
|
||||
assert python_node.objects["__o_00__"].name == "foo x"
|
||||
|
||||
assert self.eval(context, ret[0], {"self": 1}) == "x=2"
|
||||
|
||||
def test_i_cannot_compile_function_when_invalid_keyword_parameters(self):
|
||||
sheerka, context = self.init_test().unpack()
|
||||
|
||||
expression = "func(x=5=2)"
|
||||
node = self.get_expr_node(context, expression)
|
||||
visitor = PythonExprVisitor(context)
|
||||
|
||||
with pytest.raises(SyntaxError):
|
||||
visitor.compile(node)
|
||||
@@ -1,12 +1,13 @@
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts_ids import BuiltinConcepts
|
||||
from core.builtin_helpers import ensure_asts
|
||||
from core.concept import Concept
|
||||
from core.global_symbols import SyaAssociativity
|
||||
from core.sheerka.ExecutionContext import ExecutionContext
|
||||
from core.sheerka.services.SheerkaAdmin import SheerkaAdmin
|
||||
from sheerkapython.python_wrapper import Expando, create_namespace, inject_context, get_sheerka_method, Pipe, \
|
||||
MethodAccessError
|
||||
from sheerkapython.python_wrapper import Expando, MethodAccessError, Pipe, create_namespace, get_sheerka_method, \
|
||||
get_variables_from_concept_asts, inject_context
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
@@ -135,6 +136,27 @@ class TestPythonWrapper(TestUsingMemoryBasedSheerka):
|
||||
res = create_namespace(context, "TestPythonWrapper", ["foo"], None, objects, False)
|
||||
assert res == {"foo": objects["foo"]}
|
||||
|
||||
def test_external_value_takes_precedence_over_concept_parameter(self):
|
||||
"""
|
||||
To manage :
|
||||
Concept("x is a y").def_var("x").def_var("y"),
|
||||
"y is a number" -> "call_concept(__o_00__, x=y)"
|
||||
If 'y' is not given, it will use the concept parameter 'y'
|
||||
But if y is given (as a short term memory) it must be prioritized
|
||||
:return:
|
||||
"""
|
||||
sheerka, context = self.init_test().unpack()
|
||||
obj = Concept("x is a y").def_var("x").def_var("y", "concept value").auto_init()
|
||||
context.obj = obj
|
||||
objects = {"y": "object value"}
|
||||
|
||||
res = create_namespace(context, "TestPythonWrapper", ["y"], None, objects, False)
|
||||
assert res == {'y': 'concept value'}
|
||||
|
||||
context.add_to_short_term_memory("y", "stm value")
|
||||
res = create_namespace(context, "TestPythonWrapper", ["y"], None, objects, False)
|
||||
assert res == {'y': 'stm value'}
|
||||
|
||||
def test_i_can_get_sheerka_method(self):
|
||||
context = self.get_context()
|
||||
|
||||
@@ -167,3 +189,28 @@ class TestPythonWrapper(TestUsingMemoryBasedSheerka):
|
||||
|
||||
res = get_sheerka_method(context, "TestPythonWrapper", "where", True)
|
||||
assert isinstance(res, Pipe)
|
||||
|
||||
@pytest.mark.parametrize("concept, known_variables, expected", [
|
||||
(Concept("foo").def_var("x", "True"), set(), {}),
|
||||
(Concept("foo").def_var("x"), set(), {}),
|
||||
(Concept("foo").def_var("x", "self"), set(), {"x": {"self"}}),
|
||||
(Concept("foo").def_var("x", "self + a"), set(), {"x": {"self", "a"}}),
|
||||
(Concept("foo").def_var("x", "self + a").def_var("y", "b"), set(), {'x': {'a', 'self'}, 'y': {'b'}}),
|
||||
(Concept("foo", body="x").def_var("x"), set(), {}), # 'x' is a concept var, so it can be resolved
|
||||
(Concept("foo", body="x").def_var("x", "x"), set(), {'x': {'x'}}),
|
||||
(Concept("foo").def_var("x", "func(y)"), set(), {"x": {"y"}}),
|
||||
(Concept("foo").def_var("x", "x"), set(), {'x': {'x'}}),
|
||||
(Concept("foo").def_var("x", "y"), set(), {'x': {'y'}}),
|
||||
(Concept("foo").def_var("x", "x"), {"x"}, {"x": {"x"}}),
|
||||
(Concept("foo").def_var("x"), {"x"}, {}), # var x has no value, there no way to link the two 'x's
|
||||
(Concept("foo", body="x").def_var("x"), {"x"}, {"#body#": {"x"}}),
|
||||
(Concept("foo").def_var("x", "bar"), set(), {}),
|
||||
(Concept("foo").def_var("x", "bar"), {"bar"}, {"x": {"bar"}}),
|
||||
])
|
||||
def test_get_variables_from_concept_asts(self, concept, known_variables, expected):
|
||||
sheerka, context, foo, bar = self.init_concepts(concept, "bar")
|
||||
|
||||
ensure_asts(context, concept)
|
||||
variables = get_variables_from_concept_asts(context, concept, known_variables, parameters_only=False)
|
||||
|
||||
assert variables == expected
|
||||
|
||||
Reference in New Issue
Block a user