Added SheerkaComparisonManager

This commit is contained in:
2020-05-17 20:19:26 +02:00
parent 56e0a9d338
commit 08e3086820
29 changed files with 586 additions and 148 deletions
+8
View File
@@ -259,3 +259,11 @@ class CacheManager:
self.caches[cache_name].cache.init_from(content) self.caches[cache_name].cache.init_from(content)
return self return self
def reset(self, cache_only):
"""For unit test speed enhancement"""
self.cache_only = cache_only
self.caches.clear()
self.concept_caches.clear()
self.is_dirty = False
+2
View File
@@ -59,6 +59,8 @@ class BuiltinConcepts(Enum):
CHICKEN_AND_EGG = "chicken and egg" # infinite recursion when declaring concept CHICKEN_AND_EGG = "chicken and egg" # infinite recursion when declaring concept
ISA = "is a" # builtin concept to express that a concept is an instance of another one ISA = "is a" # builtin concept to express that a concept is an instance of another one
EXPLANATION = "explanation" EXPLANATION = "explanation"
PRECEDENCE = "precedence" # use to set priority among concepts when parsing
ASSOCIATIVITY = "associativity" # use to set priority among concepts when parsing
NODE = "node" NODE = "node"
GENERIC_NODE = "generic node" GENERIC_NODE = "generic node"
+15 -5
View File
@@ -342,20 +342,30 @@ class Concept:
return self return self
def add_prop(self, concept_key, value): def add_prop(self, property_name, value):
""" """
Set or add a behaviour to a concept Set or add a behaviour to a concept
A behaviour is a value from another concept (ex BuiltinConcepts.ISA A behaviour is a value from another concept (ex BuiltinConcepts.ISA
:param concept_key: Concept key :param property_name: Concept key
:param value: :param value:
:return: :return:
""" """
if concept_key in self.metadata.props: if property_name in self.metadata.props:
self.metadata.props[concept_key].add(value) self.metadata.props[property_name].add(value)
else: else:
self.metadata.props[concept_key] = {value} # a set self.metadata.props[property_name] = {value} # a set
return self return self
def set_prop(self, property_name, value):
"""
Set a behaviour to a concept
A behaviour is a value from another concept (ex BuiltinConcepts.ISA
:param property_name: Concept key
:param value:
:return:
"""
self.metadata.props[property_name] = value
def get_prop(self, concept_key): def get_prop(self, concept_key):
""" """
Gets a behaviour of a concept Gets a behaviour of a concept
+1 -1
View File
@@ -3,7 +3,7 @@ import time
from core.builtin_concepts import BuiltinConcepts from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept from core.concept import Concept
from core.sheerka.Services.SheerkaExecute import NO_MATCH from core.sheerka.services.SheerkaExecute import NO_MATCH
from core.sheerka_logger import get_logger from core.sheerka_logger import get_logger
from sdp.sheerkaDataProvider import Event from sdp.sheerkaDataProvider import Event
+48 -10
View File
@@ -12,14 +12,14 @@ from core.builtin_concepts import BuiltinConcepts, ErrorConcept, ReturnValueConc
UnknownConcept UnknownConcept
from core.concept import Concept, ConceptParts, PROPERTIES_FOR_NEW from core.concept import Concept, ConceptParts, PROPERTIES_FOR_NEW
from core.sheerka.ExecutionContext import ExecutionContext from core.sheerka.ExecutionContext import ExecutionContext
from core.sheerka.Services.SheerkaCreateNewConcept import SheerkaCreateNewConcept from core.sheerka.services.SheerkaCreateNewConcept import SheerkaCreateNewConcept
from core.sheerka.Services.SheerkaDump import SheerkaDump from core.sheerka.services.SheerkaDump import SheerkaDump
from core.sheerka.Services.SheerkaEvaluateConcept import SheerkaEvaluateConcept from core.sheerka.services.SheerkaEvaluateConcept import SheerkaEvaluateConcept
from core.sheerka.Services.SheerkaExecute import SheerkaExecute from core.sheerka.services.SheerkaExecute import SheerkaExecute
from core.sheerka.Services.SheerkaHistoryManager import SheerkaHistoryManager from core.sheerka.services.SheerkaHistoryManager import SheerkaHistoryManager
from core.sheerka.Services.SheerkaModifyConcept import SheerkaModifyConcept from core.sheerka.services.SheerkaModifyConcept import SheerkaModifyConcept
from core.sheerka.Services.SheerkaSetsManager import SheerkaSetsManager from core.sheerka.services.SheerkaSetsManager import SheerkaSetsManager
from core.sheerka.Services.SheerkaVariableManager import SheerkaVariableManager from core.sheerka.services.SheerkaVariableManager import SheerkaVariableManager
from core.sheerka_logger import console_handler from core.sheerka_logger import console_handler
from printer.SheerkaPrinter import SheerkaPrinter from printer.SheerkaPrinter import SheerkaPrinter
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event from sdp.sheerkaDataProvider import SheerkaDataProvider, Event
@@ -72,6 +72,8 @@ class Sheerka(Concept):
self.sdp: SheerkaDataProvider = None # SheerkaDataProvider self.sdp: SheerkaDataProvider = None # SheerkaDataProvider
self.cache_manager = CacheManager(cache_only) self.cache_manager = CacheManager(cache_only)
self.services = {} # sheerka plugins
self.builtin_cache = {} # cache for builtin concepts self.builtin_cache = {} # cache for builtin concepts
self.parsers = {} # cache for builtin parsers self.parsers = {} # cache for builtin parsers
self.evaluators = [] # cache for builtin evaluators self.evaluators = [] # cache for builtin evaluators
@@ -114,6 +116,20 @@ class Sheerka(Concept):
def concepts_grammars(self): def concepts_grammars(self):
return self.cache_manager.caches[self.CONCEPTS_GRAMMARS_ENTRY].cache return self.cache_manager.caches[self.CONCEPTS_GRAMMARS_ENTRY].cache
def bind_service_method(self, instance, method, as_name=None):
"""
Bind service method to sheerka instance for ease to use
:param instance:
:param method:
:param as_name:
:return:
"""
if as_name is None:
as_name = method.__name__
bound_method = method.__get__(instance, instance.__class__)
setattr(self, as_name, bound_method)
def initialize(self, root_folder: str = None, save_execution_context=True): def initialize(self, root_folder: str = None, save_execution_context=True):
""" """
Starting Sheerka Starting Sheerka
@@ -139,6 +155,7 @@ class Sheerka(Concept):
if self.sdp.first_time: if self.sdp.first_time:
self.first_time_initialisation(exec_context) self.first_time_initialisation(exec_context)
self.initialize_services()
self.initialize_builtin_parsers() self.initialize_builtin_parsers()
self.initialize_builtin_evaluators() self.initialize_builtin_evaluators()
self.initialize_builtin_concepts() self.initialize_builtin_concepts()
@@ -212,6 +229,21 @@ class Sheerka(Concept):
self.cache_manager.put(self.CONCEPTS_KEYS_ENTRY, self.USER_CONCEPTS_KEYS, 1000) self.cache_manager.put(self.CONCEPTS_KEYS_ENTRY, self.USER_CONCEPTS_KEYS, 1000)
self.variable_handler.record(context, self.name, "save_execution_context", True) self.variable_handler.record(context, self.name, "save_execution_context", True)
def initialize_services(self):
"""
Introspect to find services and bind them
:return:
"""
self.init_log.debug("Initializing services")
core.utils.import_module_and_sub_module('core.sheerka.services')
base_class = "core.sheerka.services.sheerka_service.BaseService"
for service in core.utils.get_sub_classes("core.sheerka.services", base_class):
instance = service(self)
if hasattr(instance, "initialize"):
instance.initialize()
self.services[service.NAME] = instance
def initialize_builtin_concepts(self): def initialize_builtin_concepts(self):
""" """
Initializes the builtin concepts Initializes the builtin concepts
@@ -300,8 +332,14 @@ class Sheerka(Concept):
self.cache_manager.put(self.RESOLVED_CONCEPTS_BY_FIRST_KEYWORD_ENTRY, False, res.body) self.cache_manager.put(self.RESOLVED_CONCEPTS_BY_FIRST_KEYWORD_ENTRY, False, res.body)
def reset(self, cache_only=False): def reset(self, cache_only=False):
self.cache_manager.clear() if self.cache_manager.cache_only != cache_only:
self.cache_manager.cache_only = cache_only self.cache_manager.reset(cache_only)
self.initialize_caching()
for service in self.services.values():
if hasattr(service, "initialize"):
service.initialize()
else:
self.cache_manager.clear()
self.printer_handler.reset() self.printer_handler.reset()
self.sdp.reset() self.sdp.reset()
@@ -0,0 +1,206 @@
from dataclasses import dataclass
from cache.Cache import Cache
from cache.ListCache import ListCache
from core.builtin_concepts import BuiltinConcepts
from core.sheerka.services.sheerka_service import ServiceObj, BaseService
@dataclass
class ComparisonObj(ServiceObj):
"""
Order to store
"""
property: str # property to compare
a: int # id of concept a
b: int # id of concept b
op: str # comparison operation
context: str = "#" # context when the comparison is right
class SheerkaComparisonManager(BaseService):
"""
Manage partitioning of concepts
"""
NAME = "ComparisonManager"
COMPARISON_ENTRY = "Comparison"
RESOLVED_COMPARISON_ENTRY = "Resolved_Comparison"
def __init__(self, sheerka):
super().__init__(sheerka)
@staticmethod
def _compute_key(prop_name, comparison_context):
return f"{prop_name}|{comparison_context}"
@staticmethod
def _compute_weights(comparison_objs):
"""
For every element in greater_than_s, give it a weight
if weight(a) > weight(b) it means that a > b
:param comparison_objs: list of greater than objects
:return:
"""
values = {}
for comparison_obj in comparison_objs:
values[comparison_obj.a] = 1
values[comparison_obj.b] = 1
for _ in range(len(comparison_objs)):
for comparison_obj in comparison_objs:
if comparison_obj.op == ">":
values[comparison_obj.a] = values[comparison_obj.b] + 1
else:
values[comparison_obj.b] = values[comparison_obj.a] + 1
return values
@staticmethod
def _get_partition(weighted_concepts):
res = {}
for k, v in weighted_concepts.items():
res.setdefault(v, []).append(k)
return res
def _inner_add_comparison(self, comparison_obj):
key = self._compute_key(comparison_obj.property, comparison_obj.context)
previous = self.sheerka.cache_manager.get(self.COMPARISON_ENTRY, key)
new = previous.copy() if previous else []
new.append(comparison_obj)
cycles = self.detect_cycles(new)
if cycles:
concepts_in_cycle = [self.sheerka.get_by_id(c) for c in cycles]
chicken_an_egg = self.sheerka.new(BuiltinConcepts.CHICKEN_AND_EGG, body=concepts_in_cycle)
return self.sheerka.ret(self.NAME, False, chicken_an_egg)
self.sheerka.cache_manager.put(self.RESOLVED_COMPARISON_ENTRY, key, self._compute_weights(new))
self.sheerka.cache_manager.put(self.COMPARISON_ENTRY, key, comparison_obj)
return self.sheerka.ret(self.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
def initialize(self):
cache = ListCache(default=lambda k: self.sheerka.sdp.get(self.COMPARISON_ENTRY, k))
self.sheerka.cache_manager.register_cache(self.COMPARISON_ENTRY, cache, True, True)
cache = Cache()
self.sheerka.cache_manager.register_cache(self.RESOLVED_COMPARISON_ENTRY, cache, persist=False)
self.sheerka.bind_service_method(self, SheerkaComparisonManager.is_greater_than)
self.sheerka.bind_service_method(self, SheerkaComparisonManager.is_less_than)
self.sheerka.bind_service_method(self, SheerkaComparisonManager.get_partition)
self.sheerka.bind_service_method(self, SheerkaComparisonManager.get_concepts_weights)
def is_greater_than(self, context, prop_name, concept_a, concept_b, comparison_context="#"):
"""
Records that the property of concept a is greater than concept b's one
:param context:
:param prop_name:
:param concept_a:
:param concept_b:
:param comparison_context:
:return:
"""
context.log(f"Setting concept {concept_a} is greater than {concept_b}", who=self.NAME)
event_digest = context.event.get_digest()
comparison_obj = ComparisonObj(event_digest, prop_name, concept_a.id, concept_b.id, ">", comparison_context)
return self._inner_add_comparison(comparison_obj)
def is_less_than(self, context, prop_name, concept_a, concept_b, comparison_context="#"):
"""
Records that the property of concept a is lesser than concept b's one
:param context:
:param prop_name:
:param concept_a:
:param concept_b:
:param comparison_context:
:return:
"""
context.log(f"Setting concept {concept_a} is less than {concept_b}", who=self.NAME)
event_digest = context.event.get_digest()
comparison_obj = ComparisonObj(event_digest, prop_name, concept_a.id, concept_b.id, "<", comparison_context)
return self._inner_add_comparison(comparison_obj)
def get_partition(self, prop_name, comparison_context="#"):
weighted_concept = self.get_concepts_weights(prop_name, comparison_context)
return self._get_partition(weighted_concept)
def get_concepts_weights(self, prop_name, comparison_context="#"):
weighted_concept = self.sheerka.cache_manager.get(
self.RESOLVED_COMPARISON_ENTRY,
self._compute_key(prop_name, comparison_context))
if weighted_concept is None:
key = self._compute_key(prop_name, comparison_context)
entries = self.sheerka.cache_manager.get(self.COMPARISON_ENTRY, key)
if entries is None:
return {}
else:
weighted_concept = self._compute_weights(entries)
self.sheerka.cache_manager.put(self.RESOLVED_COMPARISON_ENTRY, key, weighted_concept)
return weighted_concept
@staticmethod
def detect_cycles(comparison_objs):
"""
# Thanks to Divyanshu Mehta for contributing this code
# https://www.geeksforgeeks.org/detect-cycle-in-a-graph/?ref=lbp
:param comparison_objs:
:return:
"""
latest = comparison_objs[-1]
if latest.op == "=":
return None
def get_graph_and_vertices():
_graph = {}
_vertices = set()
for obj in comparison_objs:
if obj.op == "=":
continue
_vertices.add(obj.a)
_vertices.add(obj.b)
if obj.op == ">":
_graph.setdefault(obj.a, []).append(obj.b)
else:
_graph.setdefault(obj.b, []).append(obj.a)
return _graph, _vertices
def is_cyclic(v):
# Mark current node as visited and
# adds to recursion stack
visited[v] = True
rec_stack[v] = True
# Recur for all neighbours
# if any neighbour is visited and in
# recStack then graph is cyclic
if v in graph:
for neighbour in graph[v]:
if not visited[neighbour]:
if is_cyclic(neighbour):
return True
elif rec_stack[neighbour]:
return True
# The node needs to be poped from
# recursion stack before function ends
rec_stack[v] = False
return False
graph, vertices = get_graph_and_vertices()
visited = {k: False for k in vertices}
rec_stack = {k: False for k in vertices}
if is_cyclic(latest.a): # only need to check from the latest add, since the graph was not cyclic before
return [k for k, v in rec_stack.items() if v]
return None
@@ -14,7 +14,7 @@ class SheerkaCreateNewConcept:
def __init__(self, sheerka): def __init__(self, sheerka):
self.sheerka = sheerka self.sheerka = sheerka
self.logger_name = self.create_new_concept.__name__ self.logger_name = "CreateNewConcept"
self.bnp = core.utils.get_class(BASE_NODE_PARSER_CLASS) # BaseNodeParser self.bnp = core.utils.get_class(BASE_NODE_PARSER_CLASS) # BaseNodeParser
def create_new_concept(self, context, concept: Concept): def create_new_concept(self, context, concept: Concept):
@@ -11,7 +11,7 @@ CONCEPT_EVALUATION_STEPS = [
class SheerkaEvaluateConcept: class SheerkaEvaluateConcept:
def __init__(self, sheerka): def __init__(self, sheerka):
self.sheerka = sheerka self.sheerka = sheerka
self.logger_name = self.evaluate_concept.__name__ self.logger_name = "EvaluateConcept"
@staticmethod @staticmethod
def infinite_recursion_detected(context, concept): def infinite_recursion_detected(context, concept):
@@ -88,7 +88,7 @@ class SheerkaEvaluateConcept:
if source.strip() == "": if source.strip() == "":
concept.compiled[part_key] = DoNotResolve(source) concept.compiled[part_key] = DoNotResolve(source)
else: else:
with context.push(desc=f"Initializing compiled for {part_key}") as sub_context: with context.push(desc=f"Initializing *compiled* for {part_key}") as sub_context:
sub_context.add_inputs(source=source) sub_context.add_inputs(source=source)
to_parse = self.sheerka.ret(context.who, True, to_parse = self.sheerka.ret(context.who, True,
self.sheerka.new(BuiltinConcepts.USER_INPUT, body=source)) self.sheerka.new(BuiltinConcepts.USER_INPUT, body=source))
@@ -107,11 +107,11 @@ class SheerkaEvaluateConcept:
if default_value.strip() == "": if default_value.strip() == "":
concept.compiled[var_name] = DoNotResolve(default_value) concept.compiled[var_name] = DoNotResolve(default_value)
else: else:
with context.push(desc=f"Initializing AST for property {var_name}") as sub_context: with context.push(desc=f"Initializing *compiled* for property {var_name}") as sub_context:
sub_context.add_inputs(source=default_value) sub_context.add_inputs(source=default_value)
to_parse = self.sheerka.ret(context.who, True, to_parse = self.sheerka.ret(context.who, True,
self.sheerka.new(BuiltinConcepts.USER_INPUT, body=default_value)) self.sheerka.new(BuiltinConcepts.USER_INPUT, body=default_value))
res = self.sheerka.execute(context, to_parse, steps) res = self.sheerka.execute(sub_context, to_parse, steps)
only_success = only_successful(sub_context, res) only_success = only_successful(sub_context, res)
concept.compiled[var_name] = only_success.body.body if is_only_successful(only_success) else res concept.compiled[var_name] = only_success.body.body if is_only_successful(only_success) else res
sub_context.add_values(return_values=res) sub_context.add_values(return_values=res)
@@ -4,7 +4,7 @@ from core.builtin_concepts import BuiltinConcepts
class SheerkaModifyConcept: class SheerkaModifyConcept:
def __init__(self, sheerka): def __init__(self, sheerka):
self.sheerka = sheerka self.sheerka = sheerka
self.logger_name = self.modify_concept.__name__ self.logger_name = "ModifyConcept"
def modify_concept(self, context, concept): def modify_concept(self, context, concept):
old_version = self.sheerka.get_by_id(concept.id) old_version = self.sheerka.get_by_id(concept.id)
@@ -9,7 +9,7 @@ GROUP_PREFIX = 'All_'
class SheerkaSetsManager: class SheerkaSetsManager:
def __init__(self, sheerka): def __init__(self, sheerka):
self.sheerka = sheerka self.sheerka = sheerka
self.logger_name = self.add_concept_to_set.__name__ self.logger_name = "SetsManager"
def set_isa(self, context, concept, concept_set): def set_isa(self, context, concept, concept_set):
""" """
@@ -1,20 +1,21 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import List from typing import List
from core.sheerka.services.sheerka_service import ServiceObj
@dataclass @dataclass
class Variable: class Variable(ServiceObj):
""" """
Variable to store Variable to store
""" """
event_id: str # event where the variable is modified
who: str # who is the modifier who: str # who is the modifier
key: str # key of the variable key: str # key of the variable
value: object # value value: object # value
parents: List[str] # previous references of the variable (Note that there should be only one parent) parents: List[str] # previous references of the variable (Note that there should be only one parent)
def get_key(self): def get_key(self):
return f"{self.who}.{self.key}" return f"{self.who}|{self.key}"
class SheerkaVariableManager: class SheerkaVariableManager:
@@ -36,11 +37,11 @@ class SheerkaVariableManager:
self.sheerka.cache_manager.put(self.sheerka.VARIABLES_ENTRY, variable.get_key(), variable) self.sheerka.cache_manager.put(self.sheerka.VARIABLES_ENTRY, variable.get_key(), variable)
def load(self, who, key): def load(self, who, key):
variable = self.sheerka.cache_manager.get(self.sheerka.VARIABLES_ENTRY, who + "." + key) variable = self.sheerka.cache_manager.get(self.sheerka.VARIABLES_ENTRY, who + "|" + key)
if variable is None: if variable is None:
return None return None
return variable.value return variable.value
def delete(self, context, who, key): def delete(self, context, who, key):
self.sheerka.cache_manager.delete(self.sheerka.VARIABLES_ENTRY, who + "." + key) self.sheerka.cache_manager.delete(self.sheerka.VARIABLES_ENTRY, who + "|" + key)
@@ -0,0 +1,14 @@
from dataclasses import dataclass
@dataclass
class ServiceObj:
event_id: str # event where the object is created / modified
class BaseService:
"""
Base class for services
"""
def __init__(self, sheerka):
self.sheerka = sheerka
+28 -1
View File
@@ -29,6 +29,14 @@ def sysarg_to_string(argv):
return result return result
def get_all_loaded_modules(prefix):
import sys
if prefix:
return [m for m in sys.modules.keys() if m.startswith(prefix)]
else:
return sys.modules.keys()
def get_class(qname): def get_class(qname):
""" """
Loads a class from its full qualified name Loads a class from its full qualified name
@@ -117,12 +125,31 @@ def get_classes_from_package(package_name):
def init_package_import(package_name): def init_package_import(package_name):
pkg = __import__(package_name) pkg = __import__(package_name)
prefix = pkg.__name__ + "." prefix = pkg.__name__ + "."
# prefix = package_name + "."
for (module_loader, name, ispkg) in pkgutil.iter_modules(pkg.__path__, prefix): for (module_loader, name, ispkg) in pkgutil.iter_modules(pkg.__path__, prefix):
importlib.import_module(name) importlib.import_module(name)
importlib.import_module(package_name)
def import_module_and_sub_module(module_name):
"""
Import the module, and one sub level
:param module_name:
:return:
"""
mod = get_module(module_name)
for (module_loader, name, ispkg) in pkgutil.iter_modules(mod.__path__, module_name + "."):
importlib.import_module(name)
def get_sub_classes(package_name, base_class): def get_sub_classes(package_name, base_class):
base_class = get_class(base_class) if isinstance(base_class, str) else base_class def _get_class(name):
modname, _, clsname = name.rpartition('.')
mod = importlib.import_module(modname)
cls = getattr(mod, clsname)
return cls
base_class = _get_class(base_class) if isinstance(base_class, str) else base_class
all_class = set(base_class.__subclasses__()).union( all_class = set(base_class.__subclasses__()).union(
[s for c in base_class.__subclasses__() for s in get_sub_classes(package_name, c)]) [s for c in base_class.__subclasses__() for s in get_sub_classes(package_name, c)])
+2 -1
View File
@@ -70,7 +70,8 @@ class PythonEvaluator(OneReturnValueEvaluator):
"concepts": context.sheerka.dump_handler.dump_concepts, "concepts": context.sheerka.dump_handler.dump_concepts,
"history": context.sheerka.dump_handler.dump_history, "history": context.sheerka.dump_handler.dump_history,
"state": context.sheerka.dump_handler.dump_state, "state": context.sheerka.dump_handler.dump_state,
"Concept": core.concept.Concept "Concept": core.concept.Concept,
"BuiltinConcepts": core.builtin_concepts.BuiltinConcepts,
} }
if context.obj: if context.obj:
context.log(f"Concept '{context.obj}' is in context. Adding it and its properties to locals.", self.name) context.log(f"Concept '{context.obj}' is in context. Adding it and its properties to locals.", self.name)
+3 -1
View File
@@ -241,7 +241,6 @@ class AtomNodeParser(BaseNodeParser):
:param concept: :param concept:
:return: :return:
""" """
# return len(concept.metadata.props) == 0 or concept.metadata.definition_type == DEFINITION_TYPE_BNF
return len(concept.metadata.variables) == 0 and concept.metadata.definition_type != DEFINITION_TYPE_BNF return len(concept.metadata.variables) == 0 and concept.metadata.definition_type != DEFINITION_TYPE_BNF
def get_concepts_sequences(self): def get_concepts_sequences(self):
@@ -325,6 +324,9 @@ class AtomNodeParser(BaseNodeParser):
continue continue
for node in parser_helper.sequence: for node in parser_helper.sequence:
if isinstance(node, ConceptNode):
if len(node.concept.metadata.variables) > 0:
node.concept.metadata.is_evaluated = True # Do not try to evaluate those concepts
node.tokens = self.tokens[node.start:node.end + 1] node.tokens = self.tokens[node.start:node.end + 1]
node.fix_source() node.fix_source()
+9 -99
View File
@@ -952,107 +952,17 @@ class SyaNodeParser(BaseNodeParser):
sya_concept_def.precedence = sya_def[0] sya_concept_def.precedence = sya_def[0]
if sya_def[1] is not None: if sya_def[1] is not None:
sya_concept_def.associativity = sya_def[1] sya_concept_def.associativity = sya_def[1]
if parser.sheerka:
concept_weight = parser.sheerka.get_concepts_weights(BuiltinConcepts.PRECEDENCE)
if concept.id in concept_weight:
sya_concept_def.precedence = concept_weight[concept.id]
if associativity := concept.get_prop(BuiltinConcepts.ASSOCIATIVITY):
sya_concept_def.associativity = SyaAssociativity(associativity)
return sya_concept_def return sya_concept_def
# def reset_parser(self, context, text):
# self.context = context
# self.sheerka = context.sheerka
# self.text = text
#
# try:
# self.tokens = list(self.get_input_as_tokens(text))
# except LexerError as e:
# self.add_error(self.sheerka.new(BuiltinConcepts.ERROR, body=e), False)
# return False
#
# self.token = None
# self.pos = -1
# return True
#
# def add_error(self, error, next_token=True):
# self.error_sink.append(error)
# if next_token:
# self.next_token()
# return error
#
# def get_token(self) -> Token:
# return self.token
#
# def next_token(self, skip_whitespace=True):
# if self.token and self.token.type == TokenKind.EOF:
# return False
#
# self.pos += 1
# self.token = self.tokens[self.pos]
#
# if skip_whitespace:
# while self.token.type == TokenKind.WHITESPACE or self.token.type == TokenKind.NEWLINE:
# self.pos += 1
# self.token = self.tokens[self.pos]
#
# return self.token.type != TokenKind.EOF
# def initialize(self, context, concepts=None, sya_definitions=None):
# self.context = context
# self.sheerka = context.sheerka
#
# if sya_definitions:
# self.sya_definitions = sya_definitions
#
# if concepts:
# for concept in concepts:
# keywords = concept.key.split()
# for keyword in keywords:
# if keyword.startswith(VARIABLE_PREFIX):
# continue
#
# self.concepts_by_first_keyword.setdefault(keyword, []).append(concept.id)
# break
#
# return self.sheerka.ret(self.name, True, self.concepts_by_first_keyword)
#
# def get_concepts(self, token):
# """
# Tries to find if there are concepts that match the value of the token
# :param token:
# :return:
# """
#
# if token.type == TokenKind.STRING:
# name = token.value[1:-1]
# elif token.type == TokenKind.KEYWORD:
# name = token.value.value
# else:
# name = token.value
#
# result = []
# if name in self.concepts_by_first_keyword:
# for concept_id in self.concepts_by_first_keyword[name]:
#
# concept = self.sheerka.get_by_id(concept_id)
#
# if len(concept.metadata.props) == 0:
# # only concepts that has parameter (refuse atoms)
# # Note that this test is needed if the definition of the concept has changed
# continue
#
# if concept.metadata.definition_type == DEFINITION_TYPE_BNF:
# # bnf definitions are not supposed to be managed by this parser
# continue
#
# sya_concept_def = SyaConceptDef(concept)
# if concept.id in self.sya_definitions:
# sya_def = self.sya_definitions[concept.id]
# if sya_def[0] is not None:
# sya_concept_def.precedence = sya_def[0]
# if sya_def[1] is not None:
# sya_concept_def.associativity = sya_def[1]
#
# result.append(sya_concept_def)
# return result
#
# return None
def infix_to_postfix(self, context, text): def infix_to_postfix(self, context, text):
""" """
Implementing Shunting Yard Algorithm Implementing Shunting Yard Algorithm
+1 -1
View File
@@ -13,7 +13,7 @@
- C : concept (with history management) - C : concept (with history management)
- D : concept definitions (no history management) - D : concept definitions (no history management)
- R : executionContext ('R' stands for Result or ReturnValue, no history management) - R : executionContext ('R' stands for Result or ReturnValue, no history management)
- V : variable (from pickle) - O : ServiceObj (from pickle)
## How concepts are serialized ? ## How concepts are serialized ?
- get the id of the concept - get the id of the concept
+6 -4
View File
@@ -60,7 +60,7 @@ class Serializer:
self.register(ConceptSerializer()) self.register(ConceptSerializer())
self.register(DictionarySerializer()) self.register(DictionarySerializer())
self.register(ExecutionContextSerializer()) self.register(ExecutionContextSerializer())
self.register(VariableSerializer()) self.register(ServiceObjSerializer())
def register(self, serializer): def register(self, serializer):
""" """
@@ -288,11 +288,13 @@ class ExecutionContextSerializer(BaseSerializer):
return obj return obj
class VariableSerializer(PickleSerializer): class ServiceObjSerializer(PickleSerializer):
base_class = get_class("core.sheerka.services.sheerka_service.ServiceObj")
def __init__(self): def __init__(self):
super().__init__( super().__init__(
lambda obj: get_full_qualified_name(obj) == "core.sheerka.Services.SheerkaVariableManager.Variable", lambda obj: isinstance(obj, self.base_class),
"V", "O",
1) 1)
# #
+149
View File
@@ -0,0 +1,149 @@
import pytest
from core.builtin_concepts import BuiltinConcepts
from core.sheerka.services.SheerkaComparisonManager import SheerkaComparisonManager, ComparisonObj
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
class TestSheerkaGreaterThanManager(TestUsingMemoryBasedSheerka):
def test_i_can_add_a_is_greater_than(self):
sheerka, context, one, two = self.init_concepts("one", "two", cache_only=False)
service = sheerka.services[SheerkaComparisonManager.NAME]
res = service.is_greater_than(context, "prop_name", two, one)
assert res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS)
in_cache = sheerka.cache_manager.get(SheerkaComparisonManager.COMPARISON_ENTRY, "prop_name|#")
assert in_cache == [ComparisonObj(context.event.get_digest(), "prop_name", two.id, one.id, ">", "#")]
weighted = sheerka.cache_manager.get(SheerkaComparisonManager.RESOLVED_COMPARISON_ENTRY, "prop_name|#")
assert weighted == {"1001": 1, "1002": 2}
# I can commit
sheerka.cache_manager.commit(context)
in_db = sheerka.sdp.get(SheerkaComparisonManager.COMPARISON_ENTRY, "prop_name|#")
assert in_db == [ComparisonObj(context.event.get_digest(), "prop_name", two.id, one.id, ">", "#")]
def test_i_can_add_a_is_less_than(self):
sheerka, context, one, two = self.init_concepts("one", "two", cache_only=False)
service = sheerka.services[SheerkaComparisonManager.NAME]
res = service.is_less_than(context, "prop_name", one, two)
assert res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS)
in_cache = sheerka.cache_manager.get(SheerkaComparisonManager.COMPARISON_ENTRY, "prop_name|#")
assert in_cache == [ComparisonObj(context.event.get_digest(), "prop_name", one.id, two.id, "<", "#")]
weighted = sheerka.cache_manager.get(SheerkaComparisonManager.RESOLVED_COMPARISON_ENTRY, "prop_name|#")
assert weighted == {"1001": 1, "1002": 2}
# I can commit
sheerka.cache_manager.commit(context)
in_db = sheerka.sdp.get(SheerkaComparisonManager.COMPARISON_ENTRY, "prop_name|#")
assert in_db == [ComparisonObj(context.event.get_digest(), "prop_name", one.id, two.id, "<", "#")]
def test_i_can_add_multiples_constraints(self):
sheerka, context, one, two, three, four = self.init_concepts("one", "two", "three", "four", cache_only=False)
service = sheerka.services[SheerkaComparisonManager.NAME]
service.is_greater_than(context, "prop_name", two, one)
service.is_greater_than(context, "prop_name", three, two)
in_cache = sheerka.cache_manager.get(SheerkaComparisonManager.COMPARISON_ENTRY, "prop_name|#")
assert in_cache == [
ComparisonObj(context.event.get_digest(), "prop_name", two.id, one.id, ">", "#"),
ComparisonObj(context.event.get_digest(), "prop_name", three.id, two.id, ">", "#")
]
# I can commit
sheerka.cache_manager.commit(context)
in_db = sheerka.sdp.get(SheerkaComparisonManager.COMPARISON_ENTRY, "prop_name|#")
assert in_db == [
ComparisonObj(context.event.get_digest(), "prop_name", two.id, one.id, ">", "#"),
ComparisonObj(context.event.get_digest(), "prop_name", three.id, two.id, ">", "#")
]
sheerka.cache_manager.clear(SheerkaComparisonManager.COMPARISON_ENTRY) # reset the cache
service.is_greater_than(context, "prop_name", four, three)
in_cache = sheerka.cache_manager.get(SheerkaComparisonManager.COMPARISON_ENTRY, "prop_name|#")
assert in_cache == [
ComparisonObj(context.event.get_digest(), "prop_name", two.id, one.id, ">", "#"),
ComparisonObj(context.event.get_digest(), "prop_name", three.id, two.id, ">", "#"),
ComparisonObj(context.event.get_digest(), "prop_name", four.id, three.id, ">", "#"),
]
weighted = sheerka.cache_manager.get(SheerkaComparisonManager.RESOLVED_COMPARISON_ENTRY, "prop_name|#")
assert weighted == {"1001": 1, "1002": 2, "1003": 3, "1004": 4}
@pytest.mark.parametrize("entries, expected", [
(["two > one"], {'1001': 1, '1002': 2}),
(["one < two"], {'1001': 1, '1002': 2}),
(["three > two", "one < two"], {'1001': 1, '1002': 2, '1003': 3}),
(["three > one", "one < two"], {'1001': 1, '1002': 2, '1003': 2}),
])
def test_i_can_get_concept_weight(self, entries, expected):
sheerka, context, *concepts = self.init_concepts("one", "two", "three")
service = sheerka.services[SheerkaComparisonManager.NAME]
concepts_map = dict(zip(["one", "two", "three", "four", "five", "six"], concepts))
for entry in entries:
if ">" in entry:
a, b = [concepts_map[e.strip()] for e in entry.split(">")]
service.is_greater_than(context, "prop_name", a, b)
else:
a, b = [concepts_map[e.strip()] for e in entry.split("<")]
service.is_less_than(context, "prop_name", a, b)
assert service.get_concepts_weights("prop_name") == expected
def test_i_can_get_partition(self):
sheerka, context, one, two, three = self.init_concepts("one", "two", "three")
service = sheerka.services[SheerkaComparisonManager.NAME]
service.is_greater_than(context, "prop_name", two, one)
service.is_less_than(context, "prop_name", two, three)
res = service.get_partition("prop_name")
assert res == {
1: ["1001"],
2: ["1002"],
3: ["1003"],
}
def test_i_can_detect_chicken_and_egg(self):
sheerka, context, one, two = self.init_concepts("one", "two")
service = sheerka.services[SheerkaComparisonManager.NAME]
service.is_greater_than(context, "prop_name", two, one)
res = service.is_greater_than(context, "prop_name", one, two)
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.CHICKEN_AND_EGG)
assert set(res.body.body) == {one, two}
def test_i_can_detect_more_complex_chicken_and_egg(self):
sheerka, context, one, two, three, four, five = self.init_concepts("one", "two", "three", "four", "five")
service = sheerka.services[SheerkaComparisonManager.NAME]
service.is_greater_than(context, "prop_name", two, one)
service.is_greater_than(context, "prop_name", five, four)
service.is_greater_than(context, "prop_name", four, three)
service.is_greater_than(context, "prop_name", five, two)
res = service.is_greater_than(context, "prop_name", two, one)
assert res.status
res = service.is_greater_than(context, "prop_name", one, five)
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.CHICKEN_AND_EGG)
assert set(res.body.body) == {one, two, five}
def test_methods_are_correctly_bound(self):
sheerka, context, one, two = self.init_concepts("one", "two")
res = sheerka.is_greater_than(context, "prop_name", two, one)
assert res.status
+1 -1
View File
@@ -1,4 +1,4 @@
from core.sheerka.Services.SheerkaHistoryManager import hist from core.sheerka.services.SheerkaHistoryManager import hist
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
+2 -2
View File
@@ -16,8 +16,8 @@ class TestSheerkaVariable(TestUsingMemoryBasedSheerka):
# I can persist in db # I can persist in db
sheerka.cache_manager.commit(context) sheerka.cache_manager.commit(context)
assert sheerka.sdp.exists(Sheerka.VARIABLES_ENTRY, "TestSheerkaVariable.my_variable") assert sheerka.sdp.exists(Sheerka.VARIABLES_ENTRY, "TestSheerkaVariable|my_variable")
loaded = sheerka.sdp.get(Sheerka.VARIABLES_ENTRY, "TestSheerkaVariable.my_variable") loaded = sheerka.sdp.get(Sheerka.VARIABLES_ENTRY, "TestSheerkaVariable|my_variable")
assert loaded.event_id == context.event.get_digest() assert loaded.event_id == context.event.get_digest()
assert loaded.key == "my_variable" assert loaded.key == "my_variable"
assert loaded.value == 1 assert loaded.value == 1
+7
View File
@@ -16,6 +16,13 @@ class ConceptWithGetObjValue(Concept):
class TestSheerkaUsingMemoryBasedSheerka(TestUsingMemoryBasedSheerka): class TestSheerkaUsingMemoryBasedSheerka(TestUsingMemoryBasedSheerka):
def test_i_can_initialize_services(self):
sheerka = self.get_sheerka()
assert len(sheerka.services) > 0
assert None not in sheerka.services
assert "ComparisonManager" in sheerka.services # test at least one service
def test_i_can_initialize_builtin_parsers(self): def test_i_can_initialize_builtin_parsers(self):
sheerka = self.get_sheerka() sheerka = self.get_sheerka()
+18
View File
@@ -855,6 +855,24 @@ as:
assert res[0].status assert res[0].status
assert res[0].body == 64 assert res[0].body == 64
def test_concepts_parsed_by_atom_parser_must_not_be_evaluated(self):
definitions = [
"def concept mult from a mult b as a * b",
"def concept a mult b as a * b",
]
sheerka = self.init_scenario(definitions)
res = sheerka.evaluate_user_input("eval mult")
assert res[0].status
assert isinstance(res[0].body, Concept)
# res = sheerka.evaluate_user_input("eval a mult b")
#
# assert res[0].status
# assert isinstance(res[0].body, Concept)
class TestSheerkaNonRegFile(TestUsingFileBasedSheerka): class TestSheerkaNonRegFile(TestUsingFileBasedSheerka):
def test_i_can_def_several_concepts(self): def test_i_can_def_several_concepts(self):
+38 -6
View File
@@ -1,22 +1,25 @@
import pytest import pytest
from core.builtin_concepts import BuiltinConcepts from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept from core.concept import Concept, DEFINITION_TYPE_DEF
from parsers.AtomNodeParser import AtomNodeParser from parsers.AtomNodeParser import AtomNodeParser
from parsers.BaseNodeParser import cnode, utnode, CNC, scnode, SCN from parsers.BaseNodeParser import cnode, utnode, CNC, SCN
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
from tests.parsers.parsers_utils import compute_expected_array from tests.parsers.parsers_utils import compute_expected_array
class TestAtomsParser(TestUsingMemoryBasedSheerka): class TestAtomsParser(TestUsingMemoryBasedSheerka):
def init_parser(self, my_map, create_new=False, singleton=True): def init_parser(self, my_map, create_new=False, singleton=True, use_sheerka=False):
sheerka, context, *updated_concepts = self.init_concepts( sheerka, context, *updated_concepts = self.init_concepts(
*my_map.values(), *my_map.values(),
create_new=create_new, create_new=create_new,
singleton=singleton) singleton=singleton)
parser = AtomNodeParser() if use_sheerka:
parser.init_from_concepts(context, updated_concepts) parser = AtomNodeParser(sheerka=sheerka)
else:
parser = AtomNodeParser()
parser.init_from_concepts(context, updated_concepts)
return sheerka, context, parser return sheerka, context, parser
@@ -32,15 +35,20 @@ class TestAtomsParser(TestUsingMemoryBasedSheerka):
("foo", ["foo"]), ("foo", ["foo"]),
("foo bar", ["foo", "bar"]), ("foo bar", ["foo", "bar"]),
("foo bar twenties", ["foo", "bar", "twenties"]), ("foo bar twenties", ["foo", "bar", "twenties"]),
# ("plus", ["plus"]),
# ("++", ["++"]),
# ("a++ foo", ["++", "foo"]),
]) ])
def test_i_can_parse_simple_sequences(self, text, expected): def test_i_can_parse_simple_sequences(self, text, expected):
concepts_map = { concepts_map = {
"foo": Concept("foo"), "foo": Concept("foo"),
"bar": Concept("bar"), "bar": Concept("bar"),
"plus": Concept("a plus b").def_var("a").def_var("b"),
"++": Concept("++", definition="a++", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
"twenties": Concept("twenties", definition="'twenty' ('one'|'two')=unit").def_var("unit"), "twenties": Concept("twenties", definition="'twenty' ('one'|'two')=unit").def_var("unit"),
} }
sheerka, context, parser = self.init_parser(concepts_map) sheerka, context, parser = self.init_parser(concepts_map, create_new=True, use_sheerka=True)
res = parser.parse(context, text) res = parser.parse(context, text)
wrapper = res.body wrapper = res.body
lexer_nodes = res.body.body lexer_nodes = res.body.body
@@ -276,3 +284,27 @@ class TestAtomsParser(TestUsingMemoryBasedSheerka):
expected_array = compute_expected_array(concepts_map, text, expected) expected_array = compute_expected_array(concepts_map, text, expected)
assert sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT) assert sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
assert lexer_nodes == expected_array assert lexer_nodes == expected_array
@pytest.mark.parametrize("text, expected_is_evaluated", [
("foo", False),
("bar", False ),
("twenties", True),
("plus", True),
# ("plus", ["plus"]),
# ("++", ["++"]),
# ("a++ foo", ["++", "foo"]),
])
def test_concepts_with_variables_must_not_be_evaluated(self, text, expected_is_evaluated):
concepts_map = {
"foo": Concept("foo"),
"bar": Concept("bar", body="'bar'"),
"plus": Concept("plus", definition="a plus b", definition_type=DEFINITION_TYPE_DEF).def_var("a").def_var("b"),
"twenties": Concept("twenties", definition="'twenty' ('one'|'two')=unit").def_var("unit"),
}
sheerka, context, parser = self.init_parser(concepts_map, create_new=True, use_sheerka=True)
res = parser.parse(context, text)
lexer_nodes = res.body.body
assert res.status
assert lexer_nodes[0].concept.metadata.is_evaluated == expected_is_evaluated
+15 -4
View File
@@ -1,6 +1,7 @@
import pytest import pytest
from core.builtin_concepts import BuiltinConcepts from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept, CC from core.concept import Concept, CC
from core.sheerka.services.SheerkaComparisonManager import SheerkaComparisonManager
from core.tokenizer import Tokenizer from core.tokenizer import Tokenizer
from parsers.BaseNodeParser import utnode, ConceptNode, cnode, short_cnode, UnrecognizedTokensNode, \ from parsers.BaseNodeParser import utnode, ConceptNode, cnode, short_cnode, UnrecognizedTokensNode, \
SCWC, CNC, UTN SCWC, CNC, UTN
@@ -48,10 +49,20 @@ class TestSyaNodeParser(TestUsingMemoryBasedSheerka):
create_new=True, create_new=True,
init_from_sheerka=True) init_from_sheerka=True)
TestSyaNodeParser.sheerka.force_sya_def(context, [ cmap["plus"].set_prop(BuiltinConcepts.ASSOCIATIVITY, "right")
(cmap["plus"].id, 5, SyaAssociativity.Right), cmap["mult"].set_prop(BuiltinConcepts.ASSOCIATIVITY, "right")
(cmap["mult"].id, 10, SyaAssociativity.Right), cmap["minus"].set_prop(BuiltinConcepts.ASSOCIATIVITY, "right")
(cmap["minus"].id, 10, SyaAssociativity.Right)]) TestSyaNodeParser.sheerka.services[SheerkaComparisonManager.NAME].is_greater_than(context,
BuiltinConcepts.PRECEDENCE,
cmap["mult"], cmap["plus"])
TestSyaNodeParser.sheerka.services[SheerkaComparisonManager.NAME].is_greater_than(context,
BuiltinConcepts.PRECEDENCE,
cmap["mult"], cmap["minus"])
# TestSyaNodeParser.sheerka.force_sya_def(context, [
# (cmap["plus"].id, 5, SyaAssociativity.Right),
# (cmap["mult"].id, 10, SyaAssociativity.Right),
# (cmap["minus"].id, 5, SyaAssociativity.Right)])
def init_parser(self, def init_parser(self,
my_concepts_map=None, my_concepts_map=None,