Added set of set handling (thru concept ISA)
This commit is contained in:
@@ -59,6 +59,7 @@ class SheerkaCreateNewConcept:
|
||||
return self.sheerka.ret(self.logger_name, False, ErrorConcept(init_ret_value.value))
|
||||
|
||||
# save the new concept in sdp
|
||||
concept.metadata.full_serialization = True
|
||||
try:
|
||||
# TODO : needs to make these calls atomic (or at least one single call)
|
||||
# save the new concept
|
||||
@@ -90,6 +91,7 @@ class SheerkaCreateNewConcept:
|
||||
error.args[0])
|
||||
|
||||
# Updates the caches
|
||||
concept.metadata.full_serialization = False
|
||||
self.sheerka.cache_by_key[concept.key] = self.sheerka.sdp.get_safe(self.sheerka.CONCEPTS_ENTRY, concept.key)
|
||||
self.sheerka.cache_by_id[concept.id] = concept
|
||||
if init_ret_value is not None and init_ret_value.status:
|
||||
|
||||
@@ -31,6 +31,8 @@ class SheerkaDump:
|
||||
|
||||
def dump_desc(self, *concept_names, eval=False):
|
||||
first = True
|
||||
event = Event(f"Dumping description", "")
|
||||
context = ExecutionContext("dump_desc", event, self.sheerka)
|
||||
for concept_name in concept_names:
|
||||
if isinstance(concept_name, Concept):
|
||||
concepts = concept_name
|
||||
@@ -45,8 +47,6 @@ class SheerkaDump:
|
||||
|
||||
for c in concepts:
|
||||
if eval:
|
||||
event = Event(f"Evaluating {c}", "")
|
||||
context = ExecutionContext("dump_desc", event, self.sheerka)
|
||||
evaluated = self.sheerka.evaluate_concept(context, c)
|
||||
value = evaluated.body if evaluated.key == c.key else evaluated
|
||||
|
||||
@@ -60,8 +60,8 @@ class SheerkaDump:
|
||||
self.sheerka.log.info(f"value : {value}")
|
||||
self.sheerka.log.info(f"digest : {c.get_digest()}")
|
||||
|
||||
if self.sheerka.isaset(c):
|
||||
items = self.sheerka.get_set_elements(c)
|
||||
if self.sheerka.isaset(context, c):
|
||||
items = self.sheerka.get_set_elements(context, c)
|
||||
self.sheerka.log.info(f"elements : {items}")
|
||||
|
||||
first = False
|
||||
|
||||
@@ -235,6 +235,12 @@ class SheerkaEvaluateConcept:
|
||||
concept.set_prop(prop_name, resolved)
|
||||
else:
|
||||
part_key = ConceptParts(metadata_to_eval)
|
||||
|
||||
# do not evaluate where when the body is a set
|
||||
# Indeed, the way that the where clause is expressed is not a valid python or concept code
|
||||
if part_key == ConceptParts.WHERE and self.sheerka.isaset(context, concept.body):
|
||||
continue
|
||||
|
||||
if part_key in concept.compiled and concept.compiled[part_key] is not None:
|
||||
metadata_ast = concept.compiled[part_key]
|
||||
resolved = self.resolve(context, metadata_ast, part_key, concept, logger)
|
||||
@@ -244,7 +250,7 @@ class SheerkaEvaluateConcept:
|
||||
concept.values[part_key] = self.get_infinite_recursion_resolution(resolved) or resolved
|
||||
|
||||
# validate where clause
|
||||
if concept.metadata.where is not None:
|
||||
if ConceptParts.WHERE in concept.values:
|
||||
where_value = concept.values[ConceptParts.WHERE]
|
||||
if not (where_value is None or self.sheerka.value(where_value) is True):
|
||||
return self.sheerka.new(BuiltinConcepts.WHERE_CLAUSE_FAILED, body=concept)
|
||||
|
||||
@@ -68,7 +68,7 @@ class SheerkaHistoryManager:
|
||||
events = list(self.sheerka.sdp.load_events(depth_or_digest, start))
|
||||
for event in events:
|
||||
try:
|
||||
result = self.sheerka.sdp.load_result(self.sheerka, event.get_digest())
|
||||
result = self.sheerka.sdp.load_result(event.get_digest())
|
||||
except (IOError, KeyError):
|
||||
result = None
|
||||
yield History(event, result)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
|
||||
|
||||
class SheerkaModifyConcept:
|
||||
def __init__(self, sheerka):
|
||||
self.sheerka = sheerka
|
||||
self.logger_name = self.modify_concept.__name__
|
||||
|
||||
def modify_concept(self, context, concept, logger=None):
|
||||
logger = logger or self.sheerka.log
|
||||
|
||||
self.sheerka.sdp.modify(context.event.get_digest(), self.sheerka.CONCEPTS_ENTRY, concept.key, concept)
|
||||
|
||||
ret = self.sheerka.ret(self.logger_name, True, self.sheerka.new(BuiltinConcepts.NEW_CONCEPT, body=concept))
|
||||
return ret
|
||||
@@ -1,5 +1,7 @@
|
||||
from core.ast.nodes import python_to_concept
|
||||
from core.builtin_concepts import BuiltinConcepts, ErrorConcept
|
||||
from core.concept import Concept
|
||||
from core.concept import Concept, ConceptParts
|
||||
import core.builtin_helpers
|
||||
|
||||
GROUP_PREFIX = 'All_'
|
||||
|
||||
@@ -9,6 +11,30 @@ class SheerkaSetsManager:
|
||||
self.sheerka = sheerka
|
||||
self.logger_name = self.add_concept_to_set.__name__
|
||||
|
||||
def set_isa(self, context, concept, concept_set, logger=None):
|
||||
"""
|
||||
Defines that concept a is b is another concept
|
||||
:param context:
|
||||
:param concept:
|
||||
:param concept_set:
|
||||
:param logger:
|
||||
:return:
|
||||
"""
|
||||
|
||||
logger = logger or self.sheerka.log
|
||||
context.log(logger, f"Setting that concept {concept} is a {concept_set}", who=self.logger_name)
|
||||
|
||||
isa = [] if BuiltinConcepts.ISA not in concept.props else concept.get_prop(BuiltinConcepts.ISA)
|
||||
if concept_set not in isa:
|
||||
isa.append(concept_set)
|
||||
|
||||
concept.set_prop(BuiltinConcepts.ISA, isa)
|
||||
res = self.sheerka.modify_concept(context, concept, logger)
|
||||
if not res.status:
|
||||
return res
|
||||
|
||||
return self.add_concept_to_set(context, concept, concept_set, logger)
|
||||
|
||||
def add_concept_to_set(self, context, concept, concept_set, logger=None):
|
||||
"""
|
||||
Add an entry in sdp to tell that concept isa concept_set
|
||||
@@ -53,24 +79,51 @@ class SheerkaSetsManager:
|
||||
context.log_error(logger, "Failed to add to set.", who=self.logger_name)
|
||||
return self.sheerka.ret(self.logger_name, False, ErrorConcept(error), error.args[0])
|
||||
|
||||
def get_set_elements(self, concept):
|
||||
def get_set_elements(self, context, concept, logger=None):
|
||||
"""
|
||||
Concept is supposed to be a set
|
||||
Returns all elements if the set
|
||||
:param context:
|
||||
:param concept:
|
||||
:param logger:
|
||||
:return:
|
||||
"""
|
||||
|
||||
assert concept.id
|
||||
logger = logger or self.sheerka.log
|
||||
|
||||
ids = self.sheerka.sdp.get_safe(GROUP_PREFIX + concept.id)
|
||||
if ids is None:
|
||||
return self.sheerka.new(BuiltinConcepts.NOT_A_SET, body=concept)
|
||||
# noinspection PyShadowingNames
|
||||
def _get_set_elements(context, concept, sub_concept):
|
||||
if not (isinstance(sub_concept, Concept) and sub_concept.id):
|
||||
return self.sheerka.new(BuiltinConcepts.NOT_A_SET, body=concept)
|
||||
|
||||
elements = [self.sheerka.get_by_id(element_id) for element_id in ids]
|
||||
return elements
|
||||
ids = self.sheerka.sdp.get_safe(GROUP_PREFIX + sub_concept.id)
|
||||
if ids:
|
||||
if concept.metadata.where:
|
||||
new_condition = self._validate_where_clause(concept)
|
||||
if not new_condition:
|
||||
return self.sheerka.new(BuiltinConcepts.WHERE_CLAUSE_FAILED, body=concept)
|
||||
else:
|
||||
# This methods sucks, but I don't have enough tools (like proper AST manipulation functions)
|
||||
# to do it properly now. It will be enhanced later
|
||||
concepts = self._get_concepts(context, ids, True, logger)
|
||||
globals_ = {"xx__concepts__xx": concepts, "sheerka": self.sheerka}
|
||||
locals_ = {}
|
||||
exec(new_condition, globals_, locals_)
|
||||
return locals_["result"]
|
||||
else:
|
||||
return self._get_concepts(context, ids, False, logger)
|
||||
|
||||
def isa(self, a, b):
|
||||
# it may be a concept that references a set
|
||||
if not sub_concept.metadata.is_evaluated:
|
||||
with context.push(desc=f"Evaluating concept {sub_concept}") as sub_context:
|
||||
evaluated = self.sheerka.evaluate_concept(sub_context, sub_concept)
|
||||
if evaluated.key != concept.key:
|
||||
return False
|
||||
return _get_set_elements(context, concept, sub_concept.body)
|
||||
|
||||
return _get_set_elements(context, concept, concept)
|
||||
|
||||
def isinset(self, a, b):
|
||||
"""
|
||||
return true if the concept a is a b
|
||||
Will handle when the keyword isa will be implemented
|
||||
@@ -82,17 +135,101 @@ class SheerkaSetsManager:
|
||||
if isinstance(a, BuiltinConcepts): # common KSI error ;-)
|
||||
raise SyntaxError("Remember that the first parameter of isinstance MUST be a concept")
|
||||
|
||||
assert isinstance(a, Concept)
|
||||
assert isinstance(b, Concept)
|
||||
if not (isinstance(a, Concept) and isinstance(b, Concept)):
|
||||
return False
|
||||
|
||||
# TODO, first check the 'isa' property of a
|
||||
if not (a.id and b.id):
|
||||
return False
|
||||
|
||||
return self.sheerka.sdp.exists(GROUP_PREFIX + b.id, a.id)
|
||||
if self.sheerka.sdp.exists(GROUP_PREFIX + b.id, a.id):
|
||||
return True
|
||||
|
||||
def isaset(self, concept):
|
||||
"""True if exists All_<concept_id> in sdp"""
|
||||
if not concept.id:
|
||||
return False
|
||||
|
||||
def isa(self, a, b):
|
||||
|
||||
if BuiltinConcepts.ISA not in a.props:
|
||||
return False
|
||||
|
||||
for c in a.get_prop(BuiltinConcepts.ISA):
|
||||
if c == b:
|
||||
return True
|
||||
if self.isa(c, b):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def isaset(self, context, concept, logger=None):
|
||||
"""
|
||||
True if exists All_<concept_id> in sdp or if concept references to a concept that has all_<concept_id>
|
||||
:param context:
|
||||
:param concept:
|
||||
:param logger:
|
||||
:return:
|
||||
"""
|
||||
""""""
|
||||
|
||||
logger = logger or self.sheerka.log
|
||||
|
||||
if not (isinstance(concept, Concept) and concept.id):
|
||||
return None
|
||||
|
||||
# it may be a concept that references a set
|
||||
if not concept.metadata.is_evaluated:
|
||||
with context.push(desc=f"Evaluating concept {concept}") as sub_context:
|
||||
evaluated = self.sheerka.evaluate_concept(sub_context, concept, logger)
|
||||
if evaluated.key != concept.key:
|
||||
return False
|
||||
|
||||
if concept.body:
|
||||
return self.isaset(context, concept.body, logger)
|
||||
|
||||
res = self.sheerka.sdp.get_safe(GROUP_PREFIX + concept.id)
|
||||
return res is not None
|
||||
|
||||
def _validate_where_clause(self, concept):
|
||||
python_parser_result = [r for r in concept.compiled[ConceptParts.WHERE] if r.who == "parsers.Python"]
|
||||
if not python_parser_result or not python_parser_result[0].status:
|
||||
return None
|
||||
|
||||
ast_ = python_parser_result[0].body.body.ast_
|
||||
ast_as_concepts = python_to_concept(ast_)
|
||||
names = core.builtin_helpers.get_names(self.sheerka, ast_as_concepts)
|
||||
if len(names) != 1 or names[0] != concept.metadata.body:
|
||||
return None
|
||||
|
||||
condition = concept.metadata.where.replace(concept.metadata.body, "sheerka.value(x)")
|
||||
expression = f"""
|
||||
result=[]
|
||||
for x in xx__concepts__xx:
|
||||
try:
|
||||
if {condition}:
|
||||
result.append(x)
|
||||
except Exception:
|
||||
pass
|
||||
"""
|
||||
return expression
|
||||
|
||||
def _get_concepts(self, context, ids, evaluate, logger):
|
||||
"""
|
||||
Gets concepts from a list of concepts ids
|
||||
:param ids:
|
||||
:param evaluate: if True, all the elements are evaluated before returned
|
||||
:return:
|
||||
"""
|
||||
|
||||
if not ids:
|
||||
return []
|
||||
|
||||
if not evaluate:
|
||||
return [self.sheerka.get_by_id(element_id) for element_id in ids]
|
||||
|
||||
result = []
|
||||
with context.push(desc=f"Evaluating concepts of a set") as sub_context:
|
||||
for element_id in ids:
|
||||
concept = self.sheerka.get_by_id(element_id)
|
||||
evaluated = self.sheerka.evaluate_concept(context, concept, logger)
|
||||
result.append(evaluated)
|
||||
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user