Fixed #101 : Implement PLURIAL

Fixed #103 : Implement PlurialNodeParser
Fixed #104 : Implement dynamic concept
Fixed #107 : PrepareEvalxxxEvaluator: context hints are lost on a second evaluation
This commit is contained in:
2021-08-05 19:07:21 +02:00
parent c798c2c570
commit 71d1b1d1ca
31 changed files with 600 additions and 105 deletions
@@ -403,7 +403,6 @@ class SheerkaConceptManager(BaseService):
ensure_concept(concept)
attr = attribute.str_id if isinstance(attribute, Concept) else attribute
old_value = concept.get_value(attr)
if context.in_context(BuiltinConcepts.EVAL_GLOBAL_TRUTH_REQUESTED):
old_value = self.sheerka.get_by_id(concept.id).get_value(attr)
@@ -582,7 +581,9 @@ class SheerkaConceptManager(BaseService):
def get_by_hash(self, concept_hash, concept_id=None):
return self.internal_get("hash", concept_hash, self.CONCEPTS_BY_HASH_ENTRY, concept_id)
def get_by_id(self, concept_id):
def get_by_id(self, concept_id, allow_dynamic=False):
if allow_dynamic and (index := core.utils.safe_index(concept_id, "-")) > 0:
concept_id = concept_id[:index]
return self.internal_get("id", concept_id, self.CONCEPTS_BY_ID_ENTRY, None)
def has_id(self, concept_id):
@@ -123,7 +123,7 @@ class SheerkaEvaluateConcept(BaseService):
assert concept_part_source is not None
tokens = [t.str_value for t in Tokenizer(concept_part_source)]
tokens = [t.str_value for t in Tokenizer(concept_part_source, yield_eof=False)]
if check_vars:
for var_name in (v[0] for v in concept.get_metadata().variables):
@@ -486,24 +486,30 @@ class SheerkaEvaluateConcept(BaseService):
sub_context.add_values(return_values=ret_val)
return ret_val.body
evaluating_concept_part = current_prop in AllConceptParts
path = get_path(context, current_prop)
desc = f"Evaluating {path} (concept={current_concept})"
with context.push(BuiltinConcepts.EVALUATING_ATTRIBUTE,
current_prop,
desc=desc,
obj=current_concept) as sub_context:
obj=current_concept if evaluating_concept_part else None) as sub_context:
sub_context.add_inputs(path=path)
if force_evaluation:
sub_context.protected_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
if forbid_methods_with_side_effect:
sub_context.protected_hints.add(BuiltinConcepts.VALIDATION_ONLY_REQUESTED)
sub_context.protected_hints.add(BuiltinConcepts.EXPRESSION_ONLY_REQUESTED)
if current_prop in (ConceptParts.WHERE, ConceptParts.PRE):
sub_context.protected_hints.add(BuiltinConcepts.EVAL_UNTIL_SUCCESS_REQUESTED)
sub_context.protected_hints.add(BuiltinConcepts.EVALUATING_PRE_OR_WHERE_CLAUSE)
if current_prop in current_concept.get_compiled_context_hints():
for hint in current_concept.get_compiled_context_hints()[current_prop]:
sub_context.protected_hints.add(hint)
# when it's a concept, evaluate it
if isinstance(to_resolve, Concept) and \
not context.sheerka.isinstance(to_resolve, BuiltinConcepts.RETURN_VALUE):
@@ -521,7 +527,7 @@ class SheerkaEvaluateConcept(BaseService):
# otherwise, execute all return values to find out what is the value
else:
# update short term memory with current concept variables
if current_concept:
if evaluating_concept_part: # only update when variables are supposed to be initialized
for var in current_concept.get_metadata().variables:
value = current_concept.get_value(var[0])
if value != NotInit:
@@ -636,8 +642,8 @@ class SheerkaEvaluateConcept(BaseService):
sub_context.protected_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
if validation_only:
# Never eval the body
sub_context.protected_hints.add(BuiltinConcepts.VALIDATION_ONLY_REQUESTED)
# Never call methods with side effect in this concept or sub concepts
sub_context.protected_hints.add(BuiltinConcepts.EXPRESSION_ONLY_REQUESTED)
# auto evaluate commands
if context.sheerka.isa(concept, context.sheerka.new(BuiltinConcepts.AUTO_EVAL)):
@@ -656,6 +662,8 @@ class SheerkaEvaluateConcept(BaseService):
# to make sure of the order, it don't use ConceptParts.get_parts()
# variables must be evaluated first, body must be evaluated before where
all_metadata_to_eval = metadata or self.compute_metadata_to_eval(sub_context, concept)
if validation_only and ConceptParts.BODY in all_metadata_to_eval:
all_metadata_to_eval.remove(ConceptParts.BODY)
for metadata_to_eval in all_metadata_to_eval:
if metadata_to_eval == "variables":
@@ -671,7 +679,7 @@ class SheerkaEvaluateConcept(BaseService):
sub_context,
prop_ast,
var_name,
None,
concept,
True,
not sub_context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED),
w_clause)
@@ -680,7 +688,7 @@ class SheerkaEvaluateConcept(BaseService):
resolved = self.resolve(sub_context,
prop_ast,
var_name,
None,
concept,
True,
not sub_context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED),
w_clause)
@@ -721,7 +729,7 @@ class SheerkaEvaluateConcept(BaseService):
if isinstance(resolved, Concept) and not sub_context.sheerka.is_success(resolved):
if not (part_key == ConceptParts.BODY and
self.sheerka.has_error(context, resolved, body=BuiltinConcepts.METHOD_ACCESS_ERROR) and
sub_context.in_context(BuiltinConcepts.VALIDATION_ONLY_REQUESTED)):
sub_context.in_context(BuiltinConcepts.EXPRESSION_ONLY_REQUESTED)):
return resolved
else:
# BuiltinConcepts.METHOD_ACCESS_ERROR is returned only when the access to side effect
@@ -751,7 +759,7 @@ class SheerkaEvaluateConcept(BaseService):
# if len(concept.get_metadata().variables) == 0:
# self.sheerka.om.put(self.sheerka.CONCEPTS_BY_ID_ENTRY, concept.id, concept)
if not concept.get_metadata().is_builtin:
if not concept.get_metadata().is_builtin and concept.get_hints().is_evaluated:
self.sheerka.register_object(sub_context, concept.name, concept)
# manage RET metadata
@@ -786,6 +794,7 @@ class SheerkaEvaluateConcept(BaseService):
def compute_metadata_to_eval(self, context, concept):
to_eval = []
# variables, body are shortcut for 'variables are already added' and 'body is already added'
needed, variables, body = self.get_needed_metadata(concept, ConceptParts.PRE, True, True)
to_eval.extend(needed)
@@ -799,6 +808,15 @@ class SheerkaEvaluateConcept(BaseService):
to_eval.extend(needed)
if context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED):
if context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED):
if not variables:
to_eval.append('variables')
variables = True
if not body:
to_eval.append(ConceptParts.BODY)
body = True
needed, v, b = self.get_needed_metadata(concept, ConceptParts.RET, not variables, not body)
variables |= v
body |= b
@@ -809,13 +827,6 @@ class SheerkaEvaluateConcept(BaseService):
body |= b
to_eval.extend(needed)
if context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED):
if not variables:
to_eval.append('variables')
if not body:
to_eval.append(ConceptParts.BODY)
return to_eval
def set_auto_eval(self, context, concept):
@@ -12,7 +12,7 @@ class SheerkaHasAManager(BaseService):
def initialize(self):
self.sheerka.bind_service_method(self.NAME, self.set_hasa, True)
self.sheerka.bind_service_method(self.NAME, self.hasa, True)
self.sheerka.bind_service_method(self.NAME, self.hasa, False)
def set_hasa(self, context, concept_a, concept_b):
"""
@@ -26,8 +26,13 @@ class SheerkaHasAManager(BaseService):
context.log(f"Setting concept {concept_a} has a {concept_b}", who=self.NAME)
ensure_concept(concept_a, concept_b)
if (BuiltinConcepts.HASA in concept_a.get_metadata().props and
concept_b in concept_a.get_metadata().props[BuiltinConcepts.HASA]):
if context.in_context(BuiltinConcepts.EVAL_GLOBAL_TRUTH_REQUESTED):
concept_to_use = self.sheerka.get_by_id(concept_a.id)
else:
concept_to_use = concept_a
if (BuiltinConcepts.HASA in concept_to_use.get_metadata().props and
concept_b in concept_to_use.get_metadata().props[BuiltinConcepts.HASA]):
return self.sheerka.ret(
self.NAME,
False,
@@ -40,7 +45,9 @@ class SheerkaHasAManager(BaseService):
if context.in_context(BuiltinConcepts.EVAL_GLOBAL_TRUTH_REQUESTED):
to_add = {"props": {BuiltinConcepts.HASA: merged_concepts}}
return self.sheerka.modify_concept(context, concept_a, to_add, modify_source=True)
res = self.sheerka.modify_concept(context, concept_to_use, to_add)
concept_a.set_prop(BuiltinConcepts.HASA, merged_concepts)
return res
else:
concept_a.set_prop(BuiltinConcepts.HASA, merged_concepts)
return self.sheerka.ret(self.NAME, True, concept_a)
@@ -43,8 +43,13 @@ class SheerkaIsAManager(BaseService):
context.log(f"Setting concept {concept} is a {concept_set}", who=self.NAME)
core.builtin_helpers.ensure_concept(concept, concept_set)
if BuiltinConcepts.ISA in concept.get_metadata().props and \
concept_set in concept.get_metadata().props[BuiltinConcepts.ISA]:
if context.in_context(BuiltinConcepts.EVAL_GLOBAL_TRUTH_REQUESTED):
concept_to_use = self.sheerka.get_by_id(concept.id)
else:
concept_to_use = concept
if BuiltinConcepts.ISA in concept_to_use.get_metadata().props and \
concept_set in concept_to_use.get_metadata().props[BuiltinConcepts.ISA]:
return self.sheerka.ret(
self.NAME,
False,
@@ -56,12 +61,11 @@ class SheerkaIsAManager(BaseService):
if context.in_context(BuiltinConcepts.EVAL_GLOBAL_TRUTH_REQUESTED):
to_add = {"props": {BuiltinConcepts.ISA: new_concept_set}}
res = self.sheerka.modify_concept(context, concept, to_add, modify_source=True)
res = self.sheerka.modify_concept(context, concept_to_use, to_add)
if not res.status:
return res
else:
concept = res.body.body
concept.set_prop(BuiltinConcepts.ISA, new_concept_set)
res = self.add_concept_to_set(context, concept, concept_set)
return res
else:
@@ -0,0 +1,100 @@
from dataclasses import dataclass
from cache.Cache import Cache
from core.builtin_concepts import BuiltinConcepts
from core.builtin_helpers import ensure_concept
from core.global_symbols import NotFound
from core.sheerka.services.sheerka_service import BaseService, ServiceObj
@dataclass
class KnownPluralObj(ServiceObj):
"""
Order to store
"""
concept: str # id of the concept
plural: str # id of its plural
class SheerkaPluralManager(BaseService):
NAME = "PluralManager"
KNOWN_PLURAL_ENTRY = "SheerkaPluralManager:KnownPlural"
def __init__(self, sheerka):
super().__init__(sheerka, order=22)
def initialize(self):
cache = Cache().auto_configure(self.KNOWN_PLURAL_ENTRY)
self.sheerka.om.register_cache(self.KNOWN_PLURAL_ENTRY, cache)
self.sheerka.bind_service_method(self.NAME, self.set_plural, True)
self.sheerka.bind_service_method(self.NAME, self.is_plural, False)
self.sheerka.bind_service_method(self.NAME, self.known_plural, False)
def set_plural(self, context, concept_a, concept_b):
"""
Set that a concept_a is the plural of concept_b
:param context:
:param concept_a:
:param concept_b:
:return:
"""
context.log(f"Setting concept {concept_a} as plural of {concept_b}", who=self.NAME)
ensure_concept(concept_a, concept_b)
if context.in_context(BuiltinConcepts.EVAL_GLOBAL_TRUTH_REQUESTED):
concept_to_use = self.sheerka.get_by_id(concept_a.id)
else:
concept_to_use = concept_a
if concept_to_use.get_prop(BuiltinConcepts.PLURAL) == concept_b:
return self.sheerka.ret(
self.NAME,
False,
self.sheerka.new(BuiltinConcepts.PROPERTY_ALREADY_DEFINED,
property_value=concept_b,
property_name=BuiltinConcepts.PLURAL,
concept=concept_a))
if context.in_context(BuiltinConcepts.EVAL_GLOBAL_TRUTH_REQUESTED):
# update the list of known plurals
known_plural = KnownPluralObj(context.event.get_digest(), concept_b.id, concept_a.id)
self.sheerka.om.put(self.KNOWN_PLURAL_ENTRY, concept_b.id, known_plural)
# update the concept
to_add = {"props": {BuiltinConcepts.PLURAL: concept_b}}
res = self.sheerka.modify_concept(context, concept_to_use, to_add)
concept_a.set_prop(BuiltinConcepts.PLURAL, concept_b) # do it AFTER calling modify_concept()
return res
else:
concept_a.set_prop(BuiltinConcepts.PLURAL, concept_b)
return self.sheerka.ret(self.NAME, True, concept_a)
def is_plural(self, concept_a, concept_b=None):
"""
True if concept_a is a plural if concept_b is omitted
True if concept_a is the plural of concept_b if concept_b is given
:param concept_a:
:param concept_b:
:return:
"""
ensure_concept(concept_a)
if concept_b is None:
return BuiltinConcepts.PLURAL in concept_a.get_metadata().props
ensure_concept(concept_b)
return concept_a.get_prop(BuiltinConcepts.PLURAL) == concept_b
def known_plural(self, concept):
"""
Return the id of the concept's known plural if any. NotFound otherwise
:param concept:
:return:
"""
ensure_concept(concept)
res = self.sheerka.om.get(self.KNOWN_PLURAL_ENTRY, concept.id)
if res is NotFound:
return NotFound
return res.plural