From 031bd0274e835bf17af440210b74023e59b0a2d8 Mon Sep 17 00:00:00 2001 From: Kodjo Sossouvi Date: Mon, 8 Mar 2021 17:35:30 +0100 Subject: [PATCH] Fixed #43 : BnfNodeParser: I can recognize when multiple level of ISA Fixed #44 : BnfNodeParser: I must simplify results when multiple levels of ISA Fixed #45 : Dynamic variables cannot be parsed at restart Fixed #46 : Concepts variables values are transformed into list by default Fixed #47 : SheerkaAdmin. Add min, max, mean time when restoring files --- _concepts_adjectives.txt | 35 ++++++++++ _concepts_default.txt | 8 +++ _concepts_full.txt | 1 + _concepts_numbers.txt | 1 + _concepts_python.txt | 2 +- src/core/concept.py | 5 +- src/core/sheerka/services/SheerkaAdmin.py | 29 +++++++-- .../sheerka/services/SheerkaConceptManager.py | 24 +++++-- .../sheerka/services/SheerkaDebugManager.py | 2 + src/core/sheerka/services/SheerkaExecute.py | 8 +-- src/evaluators/MultipleSuccessEvaluator.py | 25 +++++++- src/parsers/BnfNodeParser.py | 64 +++++++++++++++++-- tests/TestUsingFileBasedSheerka.py | 2 +- tests/core/test_SheerkaConceptManager.py | 49 ++++++++++++++ tests/core/test_sheerka.py | 8 +++ tests/core/test_utils.py | 4 +- tests/non_reg/test_sheerka_non_reg.py | 5 +- .../test_sheerka_non_reg_file_based.py | 24 ++++++- tests/non_reg/test_sheerka_non_reg_out.py | 2 + tests/parsers/test_BnfNodeParser.py | 38 +++++++++++ 20 files changed, 303 insertions(+), 33 deletions(-) create mode 100644 _concepts_adjectives.txt diff --git a/_concepts_adjectives.txt b/_concepts_adjectives.txt new file mode 100644 index 0000000..1d392f6 --- /dev/null +++ b/_concepts_adjectives.txt @@ -0,0 +1,35 @@ +# define adjectives +push_ontology("english") + +def concept adjective +def concept color +set_isa(color, adjective) + +def concept red +def concept blue +def concept orange +def concept yellow +def concept green +def concept indigo +def concept violet +def concept cyan +def concept magenta +def concept black +def concept white +def concept grey + +set_isa(red, color) +set_isa(blue, color) +set_isa(orange, color) +set_isa(yellow, color) +set_isa(green, color) +set_isa(indigo, color) +set_isa(violet, color) +set_isa(cyan, color) +set_isa(magenta, color) +set_isa(black, color) +set_isa(white, color) +set_isa(grey, color) + +def concept qualify x from bnf adjective x as set_attr(x, c:adjective:, adjective) ret x +def concept qualify x from bnf x 'is' adjective as set_attr(x, c:adjective:, adjective) ret x \ No newline at end of file diff --git a/_concepts_default.txt b/_concepts_default.txt index d32868f..fabeebb 100644 --- a/_concepts_default.txt +++ b/_concepts_default.txt @@ -27,15 +27,19 @@ def concept x has a y as hasa(x,y) pre is_question() def concept x has an y as hasa(x,y) pre is_question() # no need to auto eval as it's a question +# AND def concept x and y as x and y pre is_question() set_is_lesser(__PRECEDENCE, c:x and y:, 'Sya') set_is_less_than(__PRECEDENCE, c:q:, c:x and y:, 'Sya') +# OR def concept x or y as x or y pre is_question() set_is_lesser(__PRECEDENCE, c:x or y:, 'Sya') set_is_greater_than(__PRECEDENCE, c:x and y:, c:x or y:, 'Sya') set_is_less_than(__PRECEDENCE, c:q:, c:x or y:, 'Sya') +def concept the x ret memory(x) + # default def concept male def concept female @@ -46,6 +50,10 @@ woman is a female def concept human man is a human woman is a human +def concept boy +def concept boys +def concept girl +def concept girls # days of the week def concept monday diff --git a/_concepts_full.txt b/_concepts_full.txt index ffc2814..5c9c434 100644 --- a/_concepts_full.txt +++ b/_concepts_full.txt @@ -2,6 +2,7 @@ #import default #import python #import numbers +#import adjectives diff --git a/_concepts_numbers.txt b/_concepts_numbers.txt index f34af85..dd228dd 100644 --- a/_concepts_numbers.txt +++ b/_concepts_numbers.txt @@ -1,4 +1,5 @@ # define numbers +push_ontology("english") def concept one as 1 def concept two as 2 def concept three as 3 diff --git a/_concepts_python.txt b/_concepts_python.txt index 33c8b29..f7515c2 100644 --- a/_concepts_python.txt +++ b/_concepts_python.txt @@ -1,5 +1,5 @@ +push_ontology("english") def concept x is a string pre is_question() as isinstance(x, str) -def concept x is a int pre is_question() as isinstance(x, int) def concept x is a integer pre is_question() as isinstance(x, int) def concept x starts with y pre is_question() where x is a string as x.startswith(y) def concept sha256 from bnf r'[a-f0-9]{64}' diff --git a/src/core/concept.py b/src/core/concept.py index 139fbe5..51b77fd 100644 --- a/src/core/concept.py +++ b/src/core/concept.py @@ -82,7 +82,7 @@ def get_concept_attrs(concept): # create a class attribute with all_attributes_lock: - all_attributes = [k for k in concept.__dict__ if k[0] != "_" and k[0] != "#"] + all_attributes = [var_name for var_name, var_value in concept.get_metadata().variables] if concept.id and concept.key not in BuiltinDynamicAttrs: ALL_ATTRIBUTES[concept.id] = all_attributes return all_attributes @@ -91,7 +91,7 @@ def get_concept_attrs(concept): def freeze_concept_attrs(concept): with all_attributes_lock: if concept.key not in BuiltinDynamicAttrs: - ALL_ATTRIBUTES[concept.id] = [k for k in concept.__dict__ if k[0] != "_" and k[0] != "#"] + ALL_ATTRIBUTES[concept.id] = [var_name for var_name, var_value in concept.get_metadata().variables] def copy_concepts_attrs(): @@ -501,7 +501,6 @@ class Concept: def variables(self): return {k: v for k, v in self.values().items() if not k[0] == "#"} - # return dict([(k, v) for k, v in self.values.items() if isinstance(k, str)]) def set_hint(self, name, value): self._hints[name] = value diff --git a/src/core/sheerka/services/SheerkaAdmin.py b/src/core/sheerka/services/SheerkaAdmin.py index 80284d7..fd90a4c 100644 --- a/src/core/sheerka/services/SheerkaAdmin.py +++ b/src/core/sheerka/services/SheerkaAdmin.py @@ -78,6 +78,7 @@ class SheerkaAdmin(BaseService): def restore_from_file(file_name): _nb_lines, _nb_instructions, _nb_lines_in_error = 0, 0, 0 + _min_time, _max_time = None, None file_path = path.join(path.dirname(sys.argv[0]), file_name) if not path.exists(file_path): print(f"\u001b[31mFile '{file_path}' is not found !\u001b[0m") @@ -90,11 +91,15 @@ class SheerkaAdmin(BaseService): if line.startswith("#import "): to_import = "_concepts_" + line[8:] + ".txt" - print(f"Importing {to_import}") + print(f" ==== Importing {to_import} ==== ") res = restore_from_file(to_import) _nb_lines += res[0] _nb_instructions += res[1] _nb_lines_in_error += res[2] + if _min_time is None or res[3] < _min_time: + _min_time = res[3] + if _max_time is None or res[4] > _max_time: + _max_time = res[4] continue if line == "" or line.startswith("#"): @@ -102,12 +107,20 @@ class SheerkaAdmin(BaseService): print(line) _nb_instructions += 1 + stop_watch = time.time_ns() res = self.sheerka.evaluate_user_input(line) + user_input_elapsed = time.time_ns() - stop_watch + + if _min_time is None or user_input_elapsed < _min_time: + _min_time = user_input_elapsed + if _max_time is None or user_input_elapsed > _max_time: + _max_time = user_input_elapsed + if len(res) > 1 or not res[0].status: _nb_lines_in_error += 1 print("\u001b[31mError detected !\u001b[0m") - return _nb_lines, _nb_instructions, _nb_lines_in_error + return _nb_lines, _nb_instructions, _nb_lines_in_error, _min_time, _max_time if not concept_file.startswith("_concepts"): concept_file = f"_concepts_{concept_file}.txt" @@ -119,7 +132,7 @@ class SheerkaAdmin(BaseService): enable_process_return_values_previous_value = self.sheerka.enable_process_return_values self.sheerka.enable_process_return_values = False - nb_lines, nb_instructions, nb_lines_in_error = restore_from_file(concept_file) + nb_lines, nb_instructions, nb_lines_in_error, min_time, max_time = restore_from_file(concept_file) self.sheerka.enable_process_return_values = enable_process_return_values_previous_value self.sheerka.save_execution_context = True @@ -128,8 +141,13 @@ class SheerkaAdmin(BaseService): nano_sec = stop - start dt = nano_sec / 1e6 - elapsed = f"{dt} ms" if dt < 1000 else f"{dt / 1000} s" - print(f"Imported {nb_lines} line(s) in {elapsed}.") + elapsed = f"{dt:.3f} ms" if dt < 1000 else f"{dt / 1000:.3f} s" + min_str = f"{min_time / 1e6:.3f} ms" + max_str = f"{max_time / 1e6:.3f} ms" + mean_time = dt / nb_lines + mean_str = f"{mean_time:.3f} ms" if mean_time < 1000 else f"{mean_time / 1000:.3f} s" + + print(f"Imported {nb_lines} line(s) in {elapsed}. min={min_str}, max={max_str}, mean={mean_str}") print(f"{nb_instructions} instruction(s).") if nb_lines_in_error > 0: print(f"\u001b[31m{nb_lines_in_error} errors(s) found.\u001b[0m") @@ -153,6 +171,7 @@ class SheerkaAdmin(BaseService): "key": item.key, "definition": item.get_metadata().definition, "type": item.get_metadata().definition_type, + "hash": item.get_definition_hash(), "body": item.get_metadata().body, "where": item.get_metadata().where, "pre": item.get_metadata().pre, diff --git a/src/core/sheerka/services/SheerkaConceptManager.py b/src/core/sheerka/services/SheerkaConceptManager.py index 01bc118..2e0732e 100644 --- a/src/core/sheerka/services/SheerkaConceptManager.py +++ b/src/core/sheerka/services/SheerkaConceptManager.py @@ -397,6 +397,17 @@ class SheerkaConceptManager(BaseService): ensure_concept(concept) attr = attribute.str_id if isinstance(attribute, Concept) else attribute + + if (old_value := concept.get_value(attr)) is not NotInit: + if old_value == value: + return self.sheerka.ret(self.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS)) + + if isinstance(old_value, list): + old_value.append(value) + value = old_value + else: + value = [old_value, value] + concept.set_value(attr, value) return self.sheerka.ret(self.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS)) @@ -892,11 +903,14 @@ class SheerkaConceptManager(BaseService): else: concepts = [concept] - for concept in concepts: - ensure_bnf(context, concept) # need to make sure that it cannot fail - keywords = SheerkaConceptManager.get_first_tokens(sheerka, concept) - for keyword in keywords: - (to_resolve if keyword.startswith("c:|") else resolved).add(keyword) + for c in concepts: + if sheerka.isaset(context, c): + to_resolve.add(c.str_id) + else: + ensure_bnf(context, c) # need to make sure that it cannot fail + keywords = SheerkaConceptManager.get_first_tokens(sheerka, c) + for keyword in keywords: + (to_resolve if keyword.startswith("c:|") else resolved).add(keyword) for concept_to_resolve_str in to_resolve: res = resolve_concepts(concept_to_resolve_str) diff --git a/src/core/sheerka/services/SheerkaDebugManager.py b/src/core/sheerka/services/SheerkaDebugManager.py index cf4f44b..42adf77 100644 --- a/src/core/sheerka/services/SheerkaDebugManager.py +++ b/src/core/sheerka/services/SheerkaDebugManager.py @@ -10,6 +10,7 @@ from core.sheerka.ExecutionContext import ExecutionContext from core.sheerka.services.sheerka_service import BaseService from core.utils import CONSOLE_COLORS_MAP as CCM, CONSOLE_COLUMNS, PRIMITIVES_TYPES from core.utils import as_bag +from evaluators.MultipleSuccessEvaluator import MultipleSuccessEvaluator from parsers.BaseNodeParser import SourceCodeWithConceptNode, UnrecognizedTokensNode pp = pprint.PrettyPrinter(indent=2, width=CONSOLE_COLUMNS) @@ -349,6 +350,7 @@ class SheerkaDebugManager(BaseService): self.register_debug_vars("Exceptions", PythonEvaluator.NAME+"-eval", "exception") self.register_debug_vars("Exceptions", PythonEvaluator.NAME+"-eval", "trace") self.register_debug_vars(SyaNodeParser.NAME, "parse", "*") + self.register_debug_vars(MultipleSuccessEvaluator.NAME, "matches", "return_values") def initialize_deferred(self, context, is_first_time): self.restore_state() diff --git a/src/core/sheerka/services/SheerkaExecute.py b/src/core/sheerka/services/SheerkaExecute.py index d5d307b..7b948ea 100644 --- a/src/core/sheerka/services/SheerkaExecute.py +++ b/src/core/sheerka/services/SheerkaExecute.py @@ -368,7 +368,8 @@ class SheerkaExecute(BaseService): if pi is NotFound: # when CacheManager.cache_only is True pi = ParserInput(text) self.pi_cache.put(text, pi) - return ParserInput(text, tokens=pi.tokens, length=pi.length) # new instance, but no need to tokenize the text again + return ParserInput(text, tokens=pi.tokens, + length=pi.length) # new instance, but no need to tokenize the text again key = text or core.utils.get_text_from_tokens(tokens) pi = ParserInput(key, tokens=tokens, length=len(tokens)) @@ -705,9 +706,8 @@ class SheerkaExecute(BaseService): # disable all parsers but the requested ones if parsers != "all": sub_context.preprocess_parsers = parsers - # sub_context.add_preprocess(BaseParser.PREFIX + "*", enabled=False) - # for parser in parsers: - # sub_context.add_preprocess(BaseParser.PREFIX + parser, enabled=True) + else: + sub_context.preprocess_parsers = None if prop in (Keywords.WHERE, Keywords.PRE, ConceptParts.WHERE, ConceptParts.PRE, Keywords.WHEN): sub_context.protected_hints.add(BuiltinConcepts.EVAL_QUESTION_REQUESTED) diff --git a/src/evaluators/MultipleSuccessEvaluator.py b/src/evaluators/MultipleSuccessEvaluator.py index 75937fc..b3d5837 100644 --- a/src/evaluators/MultipleSuccessEvaluator.py +++ b/src/evaluators/MultipleSuccessEvaluator.py @@ -10,6 +10,7 @@ class MultipleSuccessEvaluator(AllReturnValuesEvaluator): So we cannot decide whether it's a MultipleSameSuccess or not All parser in error will be discarded Cannot match if there is at least one evaluator in error + Cannot match if there is at least one successful parser """ NAME = "MultipleSuccess" @@ -25,28 +26,50 @@ class MultipleSuccessEvaluator(AllReturnValuesEvaluator): nb_evaluators_in_success = 0 to_process = False + debugger = context.get_debugger(MultipleSuccessEvaluator.NAME, "matches") + debugger.debug_entering(return_value=return_values) + for ret in return_values: if ret.status and ret.who.startswith(BaseParser.PREFIX): + reason = "a successful parser found" + debugger.debug_log(f"Failed because {reason}. ret={ret}") return False + elif ret.who.startswith(BaseEvaluator.PREFIX) and not ret.status: + reason = "a failed evaluator found" + debugger.debug_log(f"Failed because {reason}. ret={ret}") return False + elif ret.status and context.sheerka.isinstance(ret.body, BuiltinConcepts.REDUCE_REQUESTED): to_process = True self.eaten.append(ret) + debugger.debug_log(f"BuiltinConcepts.REDUCE_REQUESTED is found. {to_process=}") + elif ret.status and ret.who.startswith(BaseEvaluator.PREFIX): if self.already_seen(context, ret): + reason = "of duplicate return value" + debugger.debug_log(f"Failed because {reason}. ret={ret}") return False nb_evaluators_in_success += 1 self.successful_return_values.append(ret) self.eaten.append(ret) + debugger.debug_log(f"Eating and keeping {ret=}. {nb_evaluators_in_success=}") + elif not ret.status and ret.who.startswith(BaseParser.PREFIX): self.eaten.append(ret) + debugger.debug_log(f"Eating {ret=}.") + # else: # other concepts. We do not care if there are successful or not # They won't be part of result nor part of the parent # --> So they will be handled by other evaluators - return to_process and nb_evaluators_in_success > 1 + res = to_process and nb_evaluators_in_success > 1 + if not res: + reason = "not to_process and nb_evaluators_in_success > 1" + debugger.debug_log(f"Failed because '{reason}', {to_process=}, {nb_evaluators_in_success=}") + + return res def eval(self, context, return_values): context.log(f"{len(self.successful_return_values)} successful return values, {len(self.eaten)} item(s) eaten", diff --git a/src/parsers/BnfNodeParser.py b/src/parsers/BnfNodeParser.py index 3476c05..942a429 100644 --- a/src/parsers/BnfNodeParser.py +++ b/src/parsers/BnfNodeParser.py @@ -115,6 +115,12 @@ class NonTerminalNode(ParseTreeNode): res = f"{self.parsing_expression.concept}=>" if isinstance(self.parsing_expression, ConceptExpression) else "" return res + ".".join([c.get_debug() for c in self.children]) + def get_depth(self): + if isinstance(self.parsing_expression, ConceptExpression): + return 1 + max([c.get_depth() for c in self.children]) + else: + return max([c.get_depth() for c in self.children]) + class TerminalNode(ParseTreeNode): """ @@ -150,6 +156,9 @@ class TerminalNode(ParseTreeNode): def get_debug(self): return str(self.value) + def get_depth(self): + return 0 + class MultiNode: """" @@ -246,6 +255,12 @@ class ParsingContext: res = f"ParsingContext('{self.node.get_debug()}', pos={self.pos})" return res + def get_depth(self): + if isinstance(self.node, list): + return max([n.get_depth() for n in self.node]) + else: + return self.node.get_depth() + class ParsingExpression: log_sink = [] @@ -738,8 +753,9 @@ class UnOrderedChoice(ParsingExpression): if parser_helper.debugger.is_enabled(): debug_prefix = self.debug_prefix("UnOrderedChoice", parser_helper) - debug_text = {"pos": parser_helper.pos, "text": self.debug_remaining_text(parser_helper)} - parser_helper.debug_concept(debug_prefix, raw=f"{CCM['green']}{debug_text}{CCM['reset']}") + debug_vars = {"pos": parser_helper.pos, "text": self.debug_remaining_text(parser_helper)} + debug_text = self.debug_to_raw(debug_vars) + parser_helper.debug_concept(debug_prefix, color="cyan", raw=debug_text) debug_text = "" for e in self.nodes: @@ -772,16 +788,52 @@ class UnOrderedChoice(ParsingExpression): parser_helper.seek(parsing_contexts[0].pos) - if len(parsing_contexts) == 1: - return parsing_contexts[0].node + # Try to simplify the parsing_context + simplified_parsing_contexts = self.simplify(parsing_contexts) + + if parser_helper.debugger.is_enabled() and len(simplified_parsing_contexts) != len(parsing_contexts): + parser_helper.debug_concept(debug_prefix, simplified=simplified_parsing_contexts) + + if len(simplified_parsing_contexts) == 1: + return simplified_parsing_contexts[0].node else: - parsing_contexts.sort(key=attrgetter("pos"), reverse=True) - return MultiNode(parsing_contexts) + simplified_parsing_contexts.sort(key=attrgetter("pos"), reverse=True) + return MultiNode(simplified_parsing_contexts) def __repr__(self): to_str = "# ".join(repr(n) for n in self.elements) return self.add_rule_name_if_needed(f"({to_str})") + @staticmethod + def simplify(parsing_contexts: List[ParsingContext]): + """ + Try to remove redundant parsing context + for example, if + color is an adjective + red is an adjective + red is a color + when parsing 'red' we will receive two parsing context + one for 'red' + one for 'color' -> 'red' + + The second one should be discarded + :param parsing_contexts: + :return: + """ + if len(parsing_contexts) == 1: + return parsing_contexts + + by_target = {} + for pc in parsing_contexts: + by_target.setdefault(pc.node.source, []).append((pc, pc.get_depth())) + + res = [] + for k, tuple_pc_pc_depth in by_target.items(): + min_depth = min([pc_depth for pc, pc_depth in tuple_pc_pc_depth]) + res.extend([pc for pc, pc_depth in tuple_pc_pc_depth if pc_depth == min_depth]) + + return res + class Optional(ParsingExpression): """ diff --git a/tests/TestUsingFileBasedSheerka.py b/tests/TestUsingFileBasedSheerka.py index 673f81e..58261df 100644 --- a/tests/TestUsingFileBasedSheerka.py +++ b/tests/TestUsingFileBasedSheerka.py @@ -26,7 +26,7 @@ class TestUsingFileBasedSheerka(BaseTest): TestUsingFileBasedSheerka.ontologies_created.clear() @staticmethod - def new_sheerka_instance(cache_only): + def new_sheerka_instance(cache_only=False): sheerka = Sheerka(cache_only=cache_only) sheerka.initialize(SHEERKA_TEST_FOLDER, save_execution_context=False, diff --git a/tests/core/test_SheerkaConceptManager.py b/tests/core/test_SheerkaConceptManager.py index d3da83e..850afcd 100644 --- a/tests/core/test_SheerkaConceptManager.py +++ b/tests/core/test_SheerkaConceptManager.py @@ -798,6 +798,34 @@ class TestSheerkaConceptManager(TestUsingMemoryBasedSheerka): assert sheerka.get_attr(foo, prop) == bar + def test_i_setting_twice_the_same_property_creates_a_list(self): + sheerka, context = self.init_concepts() + foo = Concept("foo") + prop = Concept("property") + bar = Concept("bar") + baz = Concept("baz") + qux = Concept("qux") + + res = sheerka.set_attr(foo, prop, bar) + assert res.status + assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS) + assert sheerka.get_attr(foo, prop) == bar + + res = sheerka.set_attr(foo, prop, bar) # again, same value as no effect + assert res.status + assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS) + assert sheerka.get_attr(foo, prop) == bar + + res = sheerka.set_attr(foo, prop, baz) + assert res.status + assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS) + assert sheerka.get_attr(foo, prop) == [bar, baz] + + res = sheerka.set_attr(foo, prop, qux) + assert res.status + assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS) + assert sheerka.get_attr(foo, prop) == [bar, baz, qux] + def test_i_cannot_remove_a_concept_which_has_reference(self): sheerka, context, one, twenty_one = self.init_test().with_concepts( Concept("one"), @@ -1067,6 +1095,27 @@ class TestSheerkaConceptManager(TestUsingMemoryBasedSheerka): 'hundred': ['1005', '1007'], } + def test_i_can_resolve_when_concepts_have_multiple_levels_of_sets(self): + sheerka, context, adjective, color, red, qualified_table = self.init_concepts( + "adjective", + "color", + "red", + Concept("qualified table", definition="adjective 'table'"), + ) + + sheerka.set_isa(context, color, adjective) + sheerka.set_isa(context, red, color) + + by_first_token = compute_concepts_by_first_token(context, [adjective, color, red, qualified_table]).body + resolved_ret_val = resolve_concepts_by_first_keyword(context, by_first_token) + + assert resolved_ret_val.status + assert resolved_ret_val.body == { + 'adjective': ['1001'], + 'color': ['1002'], + 'red': ['1003', '1004'], + } + def test_concepts_are_defined_once(self): sheerka = self.get_sheerka() context = self.get_context(sheerka) diff --git a/tests/core/test_sheerka.py b/tests/core/test_sheerka.py index 81b0298..e58d779 100644 --- a/tests/core/test_sheerka.py +++ b/tests/core/test_sheerka.py @@ -120,6 +120,14 @@ class TestSheerkaUsingMemoryBasedSheerka(TestUsingMemoryBasedSheerka): assert new.get_value("a") == 10 assert new.get_value("b") == "value" + def test_i_can_instantiate_concepts_when_variables_are_defined_in_constructor(self): + sheerka, context, foo = self.init_test().with_concepts( + Concept(name="foo", variables=[("x", "default value for x"), ("y", None)]), + create_new=True).unpack() + + new = sheerka.new(foo.key) + assert new.values() == {"x": NotInit, "y": NotInit} # default values are not used ? + def test_i_can_instantiate_multiple_when_same_key(self): sheerka, context, *concepts = self.init_test().with_concepts( Concept("foo", body="foo1"), diff --git a/tests/core/test_utils.py b/tests/core/test_utils.py index 4699cb5..1e91b24 100644 --- a/tests/core/test_utils.py +++ b/tests/core/test_utils.py @@ -386,8 +386,10 @@ def test_i_can_deep_copy_a_concept(): desc="concept2_desc", id="concept2_ids", props={"prop_name": concept1}, - variables=[("var1", "default_value1"), ("var2", "default_value2")], + variables=[("var21", "default_value21"), ("var22", "default_value22")], bound_body="var1") + concept2.set_value("var21", "default_value21") + concept2.set_value("var22", "default_value22") concept = Concept(name="my_name", is_builtin=True, diff --git a/tests/non_reg/test_sheerka_non_reg.py b/tests/non_reg/test_sheerka_non_reg.py index aa0e548..5e53290 100644 --- a/tests/non_reg/test_sheerka_non_reg.py +++ b/tests/non_reg/test_sheerka_non_reg.py @@ -7,8 +7,6 @@ from core.sheerka.services.SheerkaConceptManager import SheerkaConceptManager from evaluators.MutipleSameSuccessEvaluator import MultipleSameSuccessEvaluator from evaluators.OneSuccessEvaluator import OneSuccessEvaluator from evaluators.PythonEvaluator import PythonEvalError -from parsers.BnfNodeParser import Sequence, StrMatch, OrderedChoice, Optional, ConceptExpression -from tests.TestUsingFileBasedSheerka import TestUsingFileBasedSheerka from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka from tests.parsers.parsers_utils import CMV, CC, compare_with_test_object, CB @@ -1299,7 +1297,7 @@ as: assert sheerka.objvalue(res[0].body.get_value("qty")) == 2 - def test_i_can_implement_implement_the_concept_and(self): + def test_i_can_implement_the_concept_and(self): init = [ "def concept x and y as x and y", "set_is_lesser(__PRECEDENCE, c:x and y:, 'Sya')", @@ -1310,4 +1308,3 @@ as: assert len(res) == 1 assert res[0].status - diff --git a/tests/non_reg/test_sheerka_non_reg_file_based.py b/tests/non_reg/test_sheerka_non_reg_file_based.py index 049d84d..fea1db0 100644 --- a/tests/non_reg/test_sheerka_non_reg_file_based.py +++ b/tests/non_reg/test_sheerka_non_reg_file_based.py @@ -1,4 +1,5 @@ from core.builtin_concepts_ids import BuiltinConcepts +from core.concept import get_concept_attrs, Concept, ALL_ATTRIBUTES from core.sheerka.services.SheerkaConceptManager import SheerkaConceptManager from parsers.BnfNodeParser import OrderedChoice, StrMatch, Sequence, ConceptExpression, Optional from tests.TestUsingFileBasedSheerka import TestUsingFileBasedSheerka @@ -86,7 +87,7 @@ class TestSheerkaNonRegFile(TestUsingFileBasedSheerka): "set_isa(thirties, number)", ]) - sheerka = self.get_sheerka() # another instance + sheerka = self.new_sheerka_instance() assert sheerka.evaluate_user_input("eval twenty one")[0].body == 21 assert sheerka.evaluate_user_input("eval thirty one")[0].body == 31 @@ -95,10 +96,29 @@ class TestSheerkaNonRegFile(TestUsingFileBasedSheerka): sheerka = self.get_sheerka() sheerka.evaluate_user_input("push_ontology('test')") - sheerka = self.new_sheerka_instance(False) + sheerka = self.new_sheerka_instance() res = sheerka.evaluate_user_input("pop_ontology()") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].body, BuiltinConcepts.ONTOLOGY_REMOVED) + + def test_i_can_read_concept_with_dynamic_variables_at_restart(self): + sheerka = self.init_scenario([ + "def concept foo", + "def concept prop_name", + "def concept prop_value", + "eval foo", + "set_attr(foo, prop_name, prop_value)", + ]) + + ALL_ATTRIBUTES.clear() + + res = sheerka.evaluate_user_input("in_memory()") + res = sheerka.evaluate_user_input("memory(foo)") + assert len(res) == 1 + assert res[0].status + + assert get_concept_attrs(Concept("foo")) == [] + assert get_concept_attrs(res[0].body) == ['c:prop_name|1002:'] diff --git a/tests/non_reg/test_sheerka_non_reg_out.py b/tests/non_reg/test_sheerka_non_reg_out.py index 277e122..2012475 100644 --- a/tests/non_reg/test_sheerka_non_reg_out.py +++ b/tests/non_reg/test_sheerka_non_reg_out.py @@ -32,6 +32,7 @@ name : foo key : foo definition: None type : None +hash : 16f7fbb8bc509b8c652edaf3d0c0457d15a37f0a862fbe03fa357b0c77249c46 body : 1 where : None pre : None @@ -44,6 +45,7 @@ name : foo key : foo definition: None type : None +hash : 7036cd5ffa9294d2e1dc9bf9c9bbe2783ace5cf7f423bfce9b28c8d33c0d1d0c body : 2 where : None pre : None diff --git a/tests/parsers/test_BnfNodeParser.py b/tests/parsers/test_BnfNodeParser.py index 3fff468..668e71b 100644 --- a/tests/parsers/test_BnfNodeParser.py +++ b/tests/parsers/test_BnfNodeParser.py @@ -1889,6 +1889,44 @@ class TestBnfNodeParser(TestUsingMemoryBasedSheerka): assert isinstance(resolved.nodes[1], VariableExpression) assert resolved.nodes[0].nodes == [] + def test_i_can_simplify_unordered_choices_that_refer_to_the_same_isa(self): + my_map = { + "light_red": Concept("light red"), + "dark_red": Concept("dark red"), + "red colors": Concept("red colors"), + "color": Concept("color"), + "adjective": Concept("adjective"), + "qualified table": Concept("qualified table", definition="adjective 'table'"), + } + + sheerka, context, parser = self.init_parser(my_map, init_from_sheerka=True, create_new=True) + sheerka.set_isa(context, my_map["light_red"], my_map["adjective"]) + sheerka.set_isa(context, my_map["dark_red"], my_map["adjective"]) + sheerka.set_isa(context, my_map["light_red"], my_map["color"]) + sheerka.set_isa(context, my_map["dark_red"], my_map["color"]) + sheerka.set_isa(context, my_map["light_red"], my_map["red colors"]) + sheerka.set_isa(context, my_map["dark_red"], my_map["red colors"]) + sheerka.set_isa(context, my_map["color"], my_map["adjective"]) + sheerka.set_isa(context, my_map["red colors"], my_map["color"]) + sheerka.set_isa(context, my_map["red colors"], my_map["adjective"]) + + text = "light red table" + + expected = CNC("qualified table", + source=text, + body=DoNotResolve(text), + adjective=CC("adjective", + source="light red", + body=CC("light_red", source="light red"), + light_red=CC("light_red", source="light red"))) + expected_array = compute_expected_array(my_map, text, [expected]) + + res = parser.parse(context, ParserInput(text)) + # there should be only one result !! + assert not isinstance(res, list) + assert res.status + compare_with_test_object(res.value.value, expected_array) + # @pytest.mark.parametrize("parser_input, expected", [ # ("one", [ # (True, [CNC("bnf_one", source="one", one="one", body="one")]),