diff --git a/core/builtin_concepts.py b/core/builtin_concepts.py index 21e83f7..d4db061 100644 --- a/core/builtin_concepts.py +++ b/core/builtin_concepts.py @@ -25,6 +25,7 @@ class BuiltinConcepts(Enum): PARSING = 16 # activated during the parsing. It contains the text to parse AFTER_PARSING = 17 # activated when the parsing process seems to be finished CONCEPT_ALREADY_DEFINED = 18 # when you try to add the same concept twice + NOP = 19 # no operation concept. Does nothing """ diff --git a/core/sheerka.py b/core/sheerka.py index 8d60fd2..d2c1a2c 100644 --- a/core/sheerka.py +++ b/core/sheerka.py @@ -208,7 +208,8 @@ class Sheerka(Concept): return result def process(self, context, return_values, contextual_concepts=None): - log.debug("Evaluating parsing result.") + contextual_concepts_values = [c.value for c in contextual_concepts] if contextual_concepts else [] + log.debug(f"Processing parsing result. context concept={contextual_concepts_values}") # return_values must be a list if not isinstance(return_values, list): @@ -317,8 +318,12 @@ class Sheerka(Concept): """ for part_key in ConceptParts: source = getattr(concept, part_key.value) - if source is None or source == "": + if source is None or not isinstance(source, str) or source == "": + + # the only sources that I am sure to parse are strings + # I refuse empty strings for performance, I don't want to handle useless NOPConcepts continue + ret_val = self.expect_one(context, self.parse(context, source)) concept.codes[part_key] = ret_val diff --git a/evaluators/ConceptEvaluator.py b/evaluators/ConceptEvaluator.py index 054ddaf..58a6b46 100644 --- a/evaluators/ConceptEvaluator.py +++ b/evaluators/ConceptEvaluator.py @@ -1,7 +1,10 @@ +from core.builtin_concepts import ParserResultConcept from core.concept import Concept, ConceptParts from evaluators.BaseEvaluator import OneReturnValueEvaluator import logging +from parsers.BaseParser import BaseParser + log = logging.getLogger(__name__) @@ -11,8 +14,9 @@ class ConceptEvaluator(OneReturnValueEvaluator): def matches(self, context, return_value): return return_value.status and \ - return_value.who == "Parsers:ConceptParser" and \ - isinstance(return_value.value, Concept) + return_value.who.startswith(BaseParser.PREFIX) and \ + isinstance(return_value.value, Concept) and \ + not isinstance(return_value.value, ParserResultConcept) # because there are specific evaluators def eval(self, context, return_value): sheerka = context.sheerka @@ -27,9 +31,12 @@ class ConceptEvaluator(OneReturnValueEvaluator): # TODO; check pre # if pre is not true, return Concept with a false value - body = concept.codes[ConceptParts.BODY] - if body is None: - return None # nothing to do + if ConceptParts.BODY in concept.codes: + body = concept.codes[ConceptParts.BODY] + if body is None: + return None # nothing to do - return sheerka.ret(self.name, True, body.value, parents=[return_value]) + return sheerka.ret(self.name, True, body.value, parents=[return_value]) + else: + return sheerka.ret(self.name, True, concept, parents=[return_value]) diff --git a/evaluators/PythonEvaluator.py b/evaluators/PythonEvaluator.py index 327ae73..93173c1 100644 --- a/evaluators/PythonEvaluator.py +++ b/evaluators/PythonEvaluator.py @@ -29,4 +29,4 @@ class PythonEvaluator(OneReturnValueEvaluator): error = sheerka.new(BuiltinConcepts.ERROR, body=error) return sheerka.ret(self.name, False, error, parents=[return_value]) else: - raise NotImplementedError() + return sheerka.ret(self.name, False, sheerka.new(BuiltinConcepts.ERROR), parents=[return_value]) diff --git a/parsers/EmptyStringParser.py b/parsers/EmptyStringParser.py new file mode 100644 index 0000000..4113eec --- /dev/null +++ b/parsers/EmptyStringParser.py @@ -0,0 +1,26 @@ +from core.builtin_concepts import BuiltinConcepts +from parsers.BaseParser import BaseParser +import logging + +log = logging.getLogger(__name__) + + +class EmptyStringParser(BaseParser): + """ + To parse empty or blank strings + """ + + def __init__(self): + BaseParser.__init__(self, "NullParser") + + def parse(self, context, text): + sheerka = context.sheerka + + if isinstance(text, str) and text.strip() == "" or \ + isinstance(text, list) and text == [] or \ + text is None: + log.debug(f"Recognized '{text}' as BuiltinConcepts.NOP.") + return sheerka.ret(self.name, True, sheerka.new(BuiltinConcepts.NOP)) + + log.debug(f"Failed to recognize '{text}'") + return sheerka.ret(self.name, False, sheerka.new(BuiltinConcepts.NOT_FOR_ME)) diff --git a/parsers/ExactConceptParser.py b/parsers/ExactConceptParser.py index aa8b17f..bca9b72 100644 --- a/parsers/ExactConceptParser.py +++ b/parsers/ExactConceptParser.py @@ -28,7 +28,7 @@ class ExactConceptParser(BaseParser): sheerka = context.sheerka words = self.get_words(text) if len(words) > self.MAX_WORDS_SIZE: - return ReturnValueConcept(self.name, False, sheerka.new(BuiltinConcepts.CONCEPT_TOO_LONG, obj=text)) + return sheerka.ret(self.name, False, sheerka.new(BuiltinConcepts.CONCEPT_TOO_LONG, obj=text)) recognized = False for combination in self.combinations(words): @@ -53,7 +53,7 @@ class ExactConceptParser(BaseParser): return res log.debug(f"Failed to recognize {words}") - return ReturnValueConcept(self.name, False, sheerka.new(BuiltinConcepts.UNKNOWN_CONCEPT, obj=text)) + return sheerka.ret(self.name, False, sheerka.new(BuiltinConcepts.UNKNOWN_CONCEPT, obj=text)) @staticmethod def get_words(text): diff --git a/tests/test_sheerka.py b/tests/test_sheerka.py index 9e75963..a3c8eb7 100644 --- a/tests/test_sheerka.py +++ b/tests/test_sheerka.py @@ -446,6 +446,21 @@ as: assert sheerka.isinstance(res[0].value, BuiltinConcepts.CONCEPT_ALREADY_DEFINED) +@pytest.mark.parametrize("text", [ + "", + " ", + "\n", +]) +def test_i_can_eval_a_empty_input(text): + sheerka = get_sheerka() + + res = sheerka.eval(text) + + assert len(res) == 1 + assert res[0].status + assert sheerka.isinstance(res[0].value, BuiltinConcepts.NOP) + + def get_sheerka(): sheerka = Sheerka() sheerka.initialize(root_folder)