Fixed #48 : RelationalExpressionParser: Implement relational operator parser

Fixed #49 : ExpressionParser: Implement ExpressionParser
Fixed #50 : Implement ReteConditionExprVisitor
Fixed #51 : Implement PythonConditionExprVisitor
Fixed #52 : SheerkaConceptManager: I can get and set concept property
This commit is contained in:
2021-03-23 11:35:10 +01:00
parent f8e47e2b38
commit 6cda2686fb
25 changed files with 1083 additions and 978 deletions
+24 -11
View File
@@ -1,7 +1,6 @@
import ast
import re
from dataclasses import dataclass
from typing import Union
from typing import Union, List
from core.builtin_concepts import ReturnValueConcept
from core.builtin_helpers import CreateObjectIdentifiers
@@ -18,7 +17,7 @@ from parsers.FunctionParser import FunctionNode
from parsers.PythonParser import PythonNode
from parsers.SyaNodeParser import SyaConceptParserHelper
from sheerkarete.common import V
from sheerkarete.conditions import Condition, AndConditions
from sheerkarete.conditions import Condition, AndConditions, NegatedCondition, NegatedConjunctiveConditions
@dataclass
@@ -933,6 +932,16 @@ class FN:
raise NotImplementedError(f"FN, {other=}")
@dataclass()
class NEGCOND:
condition: str
@dataclass()
class NCCOND:
conditions: List[str]
comparison_type_mapping = {
"EQ": ComparisonType.EQUALS,
"NEQ": ComparisonType.NOT_EQUAlS,
@@ -1295,10 +1304,10 @@ def resolve_test_concept(concept_map, hint):
raise NotImplementedError()
def get_rete_conditions(*conditions_as_string):
def get_rete_conditions(*conditions):
"""
Transform a list of string into a list of Condition (Rete conditions)
:param conditions_as_string: conditions in the form 'identifier|attribute|value'
:param conditions: conditions in the form 'identifier|attribute|value'
when one argument starts with "#" it means that it's a variables
ex : "#__x_00__|__name__|'__ret'" -> Condition(V('#__x_00__'), '__name__', '__ret')
@@ -1317,13 +1326,17 @@ def get_rete_conditions(*conditions_as_string):
return int(obj)
res = []
for as_string in conditions_as_string:
if as_string.startswith("$"):
fn_match = re.match(r"(?P<function>\w+)\s?\((?P<args>.+)\)", as_string[1:])
as_dict = fn_match.groupdict()
pass
for cond in conditions:
if isinstance(cond, Condition):
res.append(cond)
elif isinstance(cond, NEGCOND):
inner_cond = get_rete_conditions(cond.condition).conditions[0]
res.append(NegatedCondition(inner_cond.identifier, inner_cond.attribute, inner_cond.value))
elif isinstance(cond, NCCOND):
inner_conds = get_rete_conditions(*cond.conditions).conditions
res.append(NegatedConjunctiveConditions(*inner_conds))
else:
parts = as_string.split("|")
parts = cond.split("|")
identifier = get_value(parts[0])
attribute = parts[1]
value = get_value(parts[2])
+19 -17
View File
@@ -3,7 +3,7 @@ import pytest
from core.builtin_concepts_ids import BuiltinConcepts
from core.concept import Concept
from core.sheerka.services.SheerkaExecute import ParserInput
from core.sheerka.services.SheerkaRuleManager import RuleCompiledPredicate, FormatAstNode
from core.sheerka.services.SheerkaRuleManager import FormatAstNode, CompiledCondition
from core.tokenizer import Tokenizer, Keywords
from core.utils import tokens_are_matching
from parsers.BaseCustomGrammarParser import KeywordNotFound, NameNode, SyntaxErrorNode
@@ -80,9 +80,9 @@ class TestDefRuleParser(TestUsingMemoryBasedSheerka):
assert len(parsed.tokens) == 2
assert tokens_are_matching(parsed.tokens[Keywords.WHEN], Tokenizer("when True"))
assert tokens_are_matching(parsed.tokens[Keywords.THEN], Tokenizer("then answer('that is true')"))
assert isinstance(parsed.when, list)
assert len(parsed.when) == 1
assert isinstance(parsed.when[0], RuleCompiledPredicate)
assert isinstance(parsed.python, list)
assert len(parsed.python) == 1
assert isinstance(parsed.python[0], CompiledCondition)
assert sheerka.isinstance(parsed.then, BuiltinConcepts.RETURN_VALUE)
def test_i_can_parse_simple_format_rule_definition(self):
@@ -100,9 +100,9 @@ class TestDefRuleParser(TestUsingMemoryBasedSheerka):
assert len(parsed.tokens) == 2
assert tokens_are_matching(parsed.tokens[Keywords.WHEN], Tokenizer("when True"))
assert tokens_are_matching(parsed.tokens[Keywords.PRINT], Tokenizer("print hello world!"))
assert isinstance(parsed.when, list)
assert len(parsed.when) == 1
assert isinstance(parsed.when[0], RuleCompiledPredicate)
assert isinstance(parsed.python, list)
assert len(parsed.python) == 1
assert isinstance(parsed.python[0], CompiledCondition)
assert isinstance(parsed.print, FormatAstNode)
def test_i_can_parse_exec_rule_with_name(self):
@@ -121,36 +121,38 @@ class TestDefRuleParser(TestUsingMemoryBasedSheerka):
assert len(parsed.tokens) == 2
assert tokens_are_matching(parsed.tokens[Keywords.WHEN], Tokenizer("when True"))
assert tokens_are_matching(parsed.tokens[Keywords.THEN], Tokenizer("then answer('that is true')"))
assert isinstance(parsed.when, list)
assert len(parsed.when) == 1
assert isinstance(parsed.when[0], RuleCompiledPredicate)
assert isinstance(parsed.python, list)
assert len(parsed.python) == 1
assert isinstance(parsed.python[0], CompiledCondition)
assert sheerka.isinstance(parsed.then, BuiltinConcepts.RETURN_VALUE)
@pytest.mark.skip("Not ready for that")
def test_when_is_parsed_in_the_context_of_a_question(self):
sheerka, context, parser = self.init_parser()
text = "when foo is a bar print hello world"
res = parser.parse(context, ParserInput(text))
format_rule = res.body.body
rules = format_rule.when
rules = format_rule.python
assert res.status
assert len(rules) == 1
assert isinstance(rules[0], RuleCompiledPredicate)
assert rules[0].predicate.body.body.get_metadata().pre == "is_question()"
assert isinstance(rules[0], CompiledCondition)
assert rules[0].return_value.body.body.get_metadata().pre == "is_question()"
@pytest.mark.skip("Not ready for that")
def test_when_can_support_multiple_possibilities_when_question_only(self):
sheerka, context, parser = self.init_parser()
text = "when foo is good print hello world"
res = parser.parse(context, ParserInput(text))
format_rule = res.body.body
rules = format_rule.when
rules = format_rule.python
assert res.status
assert len(rules) == 2
assert rules[0].predicate.body.body.get_metadata().name == "a is good"
assert rules[1].predicate.body.body.get_metadata().name == "b is good"
assert rules[0].return_value.body.body.get_metadata().name == "a is good"
assert rules[1].return_value.body.body.get_metadata().name == "b is good"
@pytest.mark.parametrize("text, error", [
("def", [KeywordNotFound(None, keywords=['rule'])]),
@@ -175,7 +177,7 @@ class TestDefRuleParser(TestUsingMemoryBasedSheerka):
assert not_for_me.reason == error
@pytest.mark.parametrize("text, expected_error", [
("when x x print 'hello world'", BuiltinConcepts.TOO_MANY_ERRORS),
("when x x = False print 'hello world'", BuiltinConcepts.ERROR),
])
def test_i_can_detect_errors(self, text, expected_error):
+4 -1
View File
@@ -95,10 +95,13 @@ class TestExpressionParser(TestUsingMemoryBasedSheerka):
@pytest.mark.parametrize("expression, expected", [
("ret.status in ('a', 1 , func())", "new_var in ('a', 1 , func())"),
("ret.status not in ('a', 1 , func())", "new_var not in ('a', 1 , func())"),
("ret.status == 10", "new_var == 10"),
("ret.status == 'a'", "new_var == 'a'"),
])
def test_i_can_rebuild_source(self, expression, expected):
sheerka, context, parser, parser_input, error_sink = self.init_parser_with_source(expression)
parsed = parser.parse_input(context, parser_input, error_sink)
assert ComparisonNode.rebuild_source("new_var", parsed.comp, parsed.right.get_source()) == expected
new_source = ComparisonNode.rebuild_source("new_var", parsed.comp, parsed.right.get_source())
assert new_source == expected