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:
@@ -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
|
||||
Reference in New Issue
Block a user