7dcaa9c111
Fixed #77 : Parser: ShortTermMemoryParser should be called separately Fixed #78 : Remove VariableNode usage Fixed #79 : ConceptManager: Implement compile caching Fixed #80 : SheerkaExecute : parsers_key is not correctly computed Fixed #81 : ValidateConceptEvaluator : Validate concept's where and pre clauses right after the parsing Fixed #82 : SheerkaIsAManager: isa() failed when the set as a body Fixed #83 : ValidateConceptEvaluator : Support BNF and SYA Concepts Fixed #84 : ExpressionParser: Implement the parser as a standard parser Fixed #85 : Services: Give order to services Fixed #86 : cannot manage smart_get_attr(the short, color)
217 lines
9.2 KiB
Python
217 lines
9.2 KiB
Python
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 CompiledCondition
|
|
from core.tokenizer import Tokenizer, Keywords
|
|
from core.utils import tokens_are_matching
|
|
from parsers.BaseCustomGrammarParser import KeywordNotFound, NameNode, SyntaxErrorNode
|
|
from parsers.BaseParser import UnexpectedEofParsingError
|
|
from parsers.DefRuleParser import DefRuleParser, DefExecRuleNode, DefFormatRuleNode
|
|
from parsers.FormatRuleActionParser import FormatAstNode
|
|
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
|
|
|
cmap = {
|
|
"foo": Concept("foo"),
|
|
"bar": Concept("bar"),
|
|
"is a": Concept("x is a y").def_var("x").def_var("y"),
|
|
"is a question": Concept("x is a y", pre="is_question()").def_var("x").def_var("y"),
|
|
"a is good": Concept("a is good", pre="is_question()").def_var("a"),
|
|
"b is good": Concept("b is good", pre="is_question()").def_var("b"),
|
|
"greetings": Concept("hello a").def_var("a")
|
|
}
|
|
|
|
|
|
class TestDefRuleParser(TestUsingMemoryBasedSheerka):
|
|
shared_ontology = None
|
|
|
|
@classmethod
|
|
def setup_class(cls):
|
|
init_test_ref = cls().init_test(cache_only=False, ontology="#TestDefRuleParser#")
|
|
sheerka, context, *updated = init_test_ref.with_concepts(*cmap.values(), create_new=True).unpack()
|
|
for i, concept_name in enumerate(cmap):
|
|
cmap[concept_name] = updated[i]
|
|
|
|
cls.shared_ontology = sheerka.get_ontology(context)
|
|
sheerka.pop_ontology(context)
|
|
|
|
def init_parser(self, my_concepts_map=None, **kwargs):
|
|
if my_concepts_map is None:
|
|
sheerka, context = self.init_test().unpack()
|
|
sheerka.add_ontology(context, self.shared_ontology)
|
|
else:
|
|
sheerka, context, *updated = self.init_test().with_concepts(*my_concepts_map.values(), **kwargs).unpack()
|
|
for i, pair in enumerate(my_concepts_map):
|
|
my_concepts_map[pair] = updated[i]
|
|
|
|
parser = DefRuleParser()
|
|
return sheerka, context, parser
|
|
|
|
def test_i_cannot_parse_when_parser_input_is_initialized_from_token(self):
|
|
sheerka, context, parser = self.init_parser()
|
|
res = parser.parse(context, ParserInput("", list(Tokenizer("init from tokens"))))
|
|
|
|
assert not res.status
|
|
assert sheerka.isinstance(res.body, BuiltinConcepts.NOT_FOR_ME)
|
|
|
|
def test_i_can_detect_empty_expression(self):
|
|
sheerka, context, parser = self.init_parser()
|
|
res = parser.parse(context, ParserInput(""))
|
|
|
|
assert not res.status
|
|
assert sheerka.isinstance(res.body, BuiltinConcepts.IS_EMPTY)
|
|
|
|
def test_input_must_be_a_parser_input(self):
|
|
sheerka, context, parser = self.init_parser()
|
|
assert parser.parse(context, "not a parser input") is None
|
|
|
|
def test_i_can_parse_simple_exec_rule_definition(self):
|
|
sheerka, context, parser = self.init_parser()
|
|
text = "when True then answer('that is true')"
|
|
|
|
res = parser.parse(context, ParserInput(text))
|
|
parser_result = res.body
|
|
parsed = res.body.body
|
|
|
|
assert res.status
|
|
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
|
|
assert isinstance(parsed, DefExecRuleNode)
|
|
|
|
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.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):
|
|
sheerka, context, parser = self.init_parser()
|
|
|
|
text = "when True print hello world!"
|
|
res = parser.parse(context, ParserInput(text))
|
|
parser_result = res.body
|
|
parsed = res.body.body
|
|
|
|
assert res.status
|
|
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
|
|
assert isinstance(parsed, DefFormatRuleNode)
|
|
|
|
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.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):
|
|
sheerka, context, parser = self.init_parser()
|
|
text = "def rule my rule as when True then answer('that is true')"
|
|
|
|
res = parser.parse(context, ParserInput(text))
|
|
parser_result = res.body
|
|
parsed = res.body.body
|
|
|
|
assert res.status
|
|
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
|
|
assert isinstance(parsed, DefExecRuleNode)
|
|
|
|
assert parsed.name == NameNode(list(Tokenizer("my rule")))
|
|
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.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.python
|
|
|
|
assert res.status
|
|
assert len(rules) == 1
|
|
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.python
|
|
|
|
assert res.status
|
|
assert len(rules) == 2
|
|
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'])]),
|
|
("def concept", [KeywordNotFound(None, keywords=['rule'])]),
|
|
("def other", [KeywordNotFound(None, keywords=['rule'])]),
|
|
("def rule name", [KeywordNotFound(None, keywords=['as'])]),
|
|
("def rule complicated long name", [KeywordNotFound(None, keywords=['as'])]),
|
|
("hello world", [KeywordNotFound(None, keywords=['when'])]),
|
|
("when True", [KeywordNotFound([], keywords=['then', 'print'])]),
|
|
("print True", [KeywordNotFound([], keywords=['when'])]),
|
|
("when True print 'hello world' then answer('yes')",
|
|
[SyntaxErrorNode([], message="Cannot have both 'print' and 'then' keywords")])
|
|
])
|
|
def test_i_can_detect_not_for_me(self, text, error):
|
|
sheerka, context, parser = self.init_parser()
|
|
|
|
res = parser.parse(context, ParserInput(text))
|
|
not_for_me = res.body
|
|
|
|
assert not res.status
|
|
assert sheerka.isinstance(not_for_me, BuiltinConcepts.NOT_FOR_ME)
|
|
assert not_for_me.reason == error
|
|
|
|
@pytest.mark.parametrize("text, expected_error", [
|
|
("when x x = False print 'hello world'", BuiltinConcepts.ERROR),
|
|
|
|
])
|
|
def test_i_can_detect_errors(self, text, expected_error):
|
|
sheerka, context, parser = self.init_parser()
|
|
|
|
res = parser.parse(context, ParserInput(text))
|
|
|
|
assert not res.status
|
|
assert sheerka.isinstance(res.body, expected_error)
|
|
|
|
@pytest.mark.parametrize("text, error_message", [
|
|
("def rule rule_name as", "While parsing 'when'."),
|
|
("def rule rule_name as ", "While parsing 'when'."),
|
|
])
|
|
def test_i_cannot_parse_when_unexpected_eof(self, text, error_message):
|
|
sheerka, context, parser = self.init_parser()
|
|
|
|
res = parser.parse(context, ParserInput(text))
|
|
not_for_me = res.body
|
|
|
|
assert not res.status
|
|
assert sheerka.isinstance(not_for_me, BuiltinConcepts.NOT_FOR_ME)
|
|
assert isinstance(not_for_me.reason[0], UnexpectedEofParsingError)
|
|
assert not_for_me.reason[0].message == error_message
|
|
|
|
def test_rule_name_is_mandatory_when_using_def_rule(self):
|
|
sheerka, context, parser = self.init_parser()
|
|
|
|
res = parser.parse(context, ParserInput("def rule as when True print 'true'"))
|
|
error = res.body
|
|
|
|
assert not res.status
|
|
assert sheerka.isinstance(error, BuiltinConcepts.ERROR)
|
|
assert isinstance(error.error[0], SyntaxErrorNode)
|
|
assert error.error[0].message == "Name is mandatory"
|