Refactored sheerka class: splitted to use sub handlers. Refactored unit tests to use classes.
This commit is contained in:
-1329
File diff suppressed because it is too large
Load Diff
@@ -2,8 +2,8 @@ import getopt
|
||||
import sys
|
||||
import logging
|
||||
|
||||
from core.sheerka import Sheerka
|
||||
import core.utils
|
||||
from core.sheerka.Sheerka import Sheerka
|
||||
|
||||
|
||||
def usage():
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- encoding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
|
||||
from glob import glob
|
||||
from os.path import basename
|
||||
from os.path import splitext
|
||||
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name='sheerka',
|
||||
version='0.1',
|
||||
license='',
|
||||
description='A human/computer communication interface',
|
||||
long_description='',
|
||||
author='Kodjo Sossouvi',
|
||||
author_email='kodjo.sossouvi@gmail.com',
|
||||
url='',
|
||||
packages=find_packages('src'),
|
||||
package_dir={'': 'src'},
|
||||
py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')],
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
classifiers=[
|
||||
# complete classifier list: http://pypi.python.org/pypi?%3Aaction=list_classifiers
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Intended Audience :: Developers',
|
||||
'Operating System :: Unix',
|
||||
'Operating System :: POSIX',
|
||||
'Operating System :: Microsoft :: Windows',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
'Programming Language :: Python :: Implementation :: CPython',
|
||||
'Programming Language :: Python :: Implementation :: PyPy',
|
||||
'Topic :: Utilities',
|
||||
], install_requires=['pytest']
|
||||
)
|
||||
@@ -0,0 +1,203 @@
|
||||
import logging
|
||||
import time
|
||||
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from sdp.sheerkaDataProvider import Event
|
||||
|
||||
DEBUG_TAB_SIZE = 4
|
||||
|
||||
|
||||
class ExecutionContext:
|
||||
"""
|
||||
To keep track of the execution of a request
|
||||
"""
|
||||
|
||||
ids = {}
|
||||
|
||||
@staticmethod
|
||||
def get_id(event_digest):
|
||||
if event_digest in ExecutionContext.ids:
|
||||
ExecutionContext.ids[event_digest] += 1
|
||||
else:
|
||||
ExecutionContext.ids[event_digest] = 0
|
||||
return ExecutionContext.ids[event_digest]
|
||||
|
||||
def __init__(self,
|
||||
who,
|
||||
event: Event,
|
||||
sheerka,
|
||||
desc: str = None,
|
||||
**kwargs):
|
||||
|
||||
self._parent = None
|
||||
self._id = ExecutionContext.get_id(event.get_digest())
|
||||
self._tab = ""
|
||||
self._bag = {} # other variables
|
||||
self._start = 0
|
||||
self._stop = 0
|
||||
|
||||
self.who = who # who is asking
|
||||
self.event = event # what was the (original) trigger
|
||||
self.sheerka = sheerka # sheerka
|
||||
self.desc = desc # human description of what is going on
|
||||
self.children = []
|
||||
self.preprocess = None
|
||||
|
||||
self.inputs = {} # what was the parameters of the execution context
|
||||
self.values = {} # what was produced by the execution context
|
||||
|
||||
self.obj = kwargs.pop("obj", None)
|
||||
self.concepts = kwargs.pop("concepts", {})
|
||||
# update the other elements
|
||||
for k, v in kwargs.items():
|
||||
self._bag[k] = v
|
||||
|
||||
@property
|
||||
def elapsed(self):
|
||||
if self._start == 0:
|
||||
return 0
|
||||
|
||||
return (self._stop if self._stop > 0 else time.time_ns()) - self._start
|
||||
|
||||
@property
|
||||
def elapsed_str(self):
|
||||
nano_sec = self.elapsed
|
||||
dt = nano_sec / 1e6
|
||||
return f"{dt} ms" if dt < 1000 else f"{dt / 1000} s"
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
def __getattr__(self, item):
|
||||
if item in self._bag:
|
||||
return self._bag[item]
|
||||
|
||||
raise AttributeError(f"'ExecutionContext' object has no attribute '{item}'")
|
||||
|
||||
def __enter__(self):
|
||||
self._start = time.time_ns()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self._stop = time.time_ns()
|
||||
|
||||
def __repr__(self):
|
||||
msg = f"ExecutionContext(who={self.who}, id={self._id}"
|
||||
if self.desc:
|
||||
msg += f", desc='{self.desc}'"
|
||||
msg += ")"
|
||||
return msg
|
||||
|
||||
def add_preprocess(self, name, **kwargs):
|
||||
preprocess = self.sheerka.new(BuiltinConcepts.EVALUATOR_PRE_PROCESS)
|
||||
preprocess.set_prop("name", name)
|
||||
for k, v in kwargs.items():
|
||||
preprocess.set_prop(k, v)
|
||||
|
||||
if not self.preprocess:
|
||||
self.preprocess = set()
|
||||
self.preprocess.add(preprocess)
|
||||
return self
|
||||
|
||||
def add_inputs(self, **kwargs):
|
||||
for k, v in kwargs.items():
|
||||
self.inputs[k] = v
|
||||
return self
|
||||
|
||||
def add_values(self, **kwargs):
|
||||
for k, v in kwargs.items():
|
||||
self.values[k] = v
|
||||
return self
|
||||
|
||||
def get_concept(self, key):
|
||||
# search in obj
|
||||
if isinstance(self.obj, Concept):
|
||||
if self.obj.key == key:
|
||||
return self.obj
|
||||
for prop in self.obj.props:
|
||||
if prop == key:
|
||||
value = self.obj.props[prop].value
|
||||
if isinstance(value, Concept):
|
||||
return value
|
||||
|
||||
# search in concepts
|
||||
if self.concepts:
|
||||
for k, c in self.concepts.items():
|
||||
if k == key:
|
||||
return c
|
||||
|
||||
return self.sheerka.get(key)
|
||||
|
||||
def new_concept(self, key, **kwargs):
|
||||
# search in obj
|
||||
if self.obj:
|
||||
if self.obj.key == key:
|
||||
return self.sheerka.new_from_template(self.obj, key, **kwargs)
|
||||
for prop in self.obj.props:
|
||||
if prop == key:
|
||||
value = self.obj.props[prop].value
|
||||
if isinstance(value, Concept):
|
||||
return self.sheerka.new_from_template(value, key, **kwargs)
|
||||
else:
|
||||
return value
|
||||
|
||||
if self.concepts:
|
||||
for k, c in self.concepts.items():
|
||||
if k == key:
|
||||
return self.sheerka.new_from_template(c, key, **kwargs)
|
||||
|
||||
return self.sheerka.new(key, **kwargs)
|
||||
|
||||
def push(self, who=None, desc=None, **kwargs):
|
||||
who = who or self.who
|
||||
_kwargs = {"obj": self.obj, "concepts": self.concepts}
|
||||
_kwargs.update(self._bag)
|
||||
_kwargs.update(kwargs)
|
||||
new = ExecutionContext(
|
||||
who,
|
||||
self.event,
|
||||
self.sheerka,
|
||||
desc,
|
||||
**_kwargs,
|
||||
)
|
||||
new._parent = self
|
||||
new._tab = self._tab + " " * DEBUG_TAB_SIZE
|
||||
new.preprocess = self.preprocess
|
||||
|
||||
self.children.append(new)
|
||||
return new
|
||||
|
||||
def log_new(self, logger):
|
||||
logger.debug(f"[{self._id:2}]" + self._tab + str(self))
|
||||
|
||||
def log(self, logger, message, who=None):
|
||||
logger.debug(f"[{self._id:2}]" + self._tab + (f"[{who}] " if who else "") + str(message))
|
||||
|
||||
def log_error(self, logger, message, who=None):
|
||||
logger.exception(f"[{self._id:2}]" + self._tab + (f"[{who}] " if who else "") + str(message))
|
||||
|
||||
def log_result(self, logger, return_values):
|
||||
if not logger.isEnabledFor(logging.DEBUG):
|
||||
return
|
||||
|
||||
if len(return_values) == 0:
|
||||
logger.debug(self._tab + "No return value")
|
||||
|
||||
for r in return_values:
|
||||
to_str = self.return_value_to_str(r)
|
||||
logger.debug(f"[{self._id:2}]" + self._tab + "-> " + to_str)
|
||||
|
||||
def to_dict(self):
|
||||
from core.sheerka_transform import SheerkaTransform
|
||||
st = SheerkaTransform(self.sheerka)
|
||||
return st.to_dict(self)
|
||||
|
||||
@staticmethod
|
||||
def return_value_to_str(r):
|
||||
value = str(r.value)
|
||||
if len(value) > 50:
|
||||
value = value[:47] + "..."
|
||||
to_str = f"ReturnValue(who={r.who}, status={r.status}, value={value})"
|
||||
return to_str
|
||||
@@ -0,0 +1,600 @@
|
||||
from core.builtin_concepts import BuiltinConcepts, ErrorConcept, ReturnValueConcept, BuiltinErrors, BuiltinUnique, \
|
||||
UnknownConcept
|
||||
from core.concept import Concept, ConceptParts, PROPERTIES_FOR_NEW
|
||||
from core.sheerka.ExecutionContext import ExecutionContext
|
||||
from core.sheerka.SheerkaCreateNewConcept import SheerkaCreateNewConcept
|
||||
from core.sheerka.SheerkaDump import SheerkaDump
|
||||
from core.sheerka.SheerkaEvaluateConcept import SheerkaEvaluateConcept
|
||||
from core.sheerka.SheerkaExecute import SheerkaExecute
|
||||
from core.sheerka.SheerkaSetsManager import SheerkaSetsManager
|
||||
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event
|
||||
import core.utils
|
||||
import core.builtin_helpers
|
||||
|
||||
from core.sheerka_logger import console_handler
|
||||
|
||||
import logging
|
||||
|
||||
# CONCEPT_EVALUATION_STEPS = [
|
||||
# BuiltinConcepts.BEFORE_EVALUATION,
|
||||
# BuiltinConcepts.EVALUATION,
|
||||
# BuiltinConcepts.AFTER_EVALUATION]
|
||||
|
||||
CONCEPT_LEXER_PARSER_CLASS = "parsers.ConceptLexerParser.ConceptLexerParser"
|
||||
|
||||
|
||||
class Sheerka(Concept):
|
||||
"""
|
||||
Main controller for the project
|
||||
"""
|
||||
|
||||
CONCEPTS_ENTRY = "All_Concepts" # to store all the concepts
|
||||
CONCEPTS_BY_ID_ENTRY = "Concepts_By_ID"
|
||||
CONCEPTS_DEFINITIONS_ENTRY = "Concepts_Definitions" # to store definitions (bnf) of concepts
|
||||
BUILTIN_CONCEPTS_KEYS = "Builtins_Concepts" # sequential key for builtin concepts
|
||||
USER_CONCEPTS_KEYS = "User_Concepts" # sequential key for user defined concepts
|
||||
|
||||
def __init__(self, skip_builtins_in_db=False, debug=False, loggers=None):
|
||||
self.init_logging(debug, loggers)
|
||||
|
||||
super().__init__(BuiltinConcepts.SHEERKA, True, True, BuiltinConcepts.SHEERKA)
|
||||
self.log.debug("Starting Sheerka.")
|
||||
|
||||
# cache of the most used concepts
|
||||
# Note that these are only templates
|
||||
# They are used as a footprint for instantiation
|
||||
# Except of source when the concept is supposed to be unique
|
||||
# key is the key of the concept (not the name or the id)
|
||||
self.cache_by_key = {}
|
||||
self.cache_by_id = {}
|
||||
|
||||
# cache for concept definitions,
|
||||
# Primarily used for unit test that does not have access to sdp
|
||||
self.concepts_definition_cache = {}
|
||||
|
||||
#
|
||||
# cache for concepts grammars
|
||||
# a grammar is a resolved BNF
|
||||
self.concepts_grammars = {}
|
||||
|
||||
# a concept can be instantiated
|
||||
# ex: File is a concept, but File('foo.txt') is an instance
|
||||
# TODO: manage contexts
|
||||
self.instances = []
|
||||
|
||||
# List of the known rules by the system
|
||||
# ex: hello => say('hello')
|
||||
self.rules = []
|
||||
|
||||
self.sdp: SheerkaDataProvider = None # SheerkaDataProvider
|
||||
self.builtin_cache = {} # cache for builtin concepts
|
||||
self.parsers = {} # cache for builtin parsers
|
||||
self.evaluators = [] # cache for builtin evaluators
|
||||
|
||||
self.evaluators_prefix: str = None
|
||||
self.parsers_prefix: str = None
|
||||
|
||||
self.skip_builtins_in_db = skip_builtins_in_db
|
||||
|
||||
self.execute_handler = SheerkaExecute(self)
|
||||
self.create_new_concept_handler = SheerkaCreateNewConcept(self)
|
||||
self.dump_handler = SheerkaDump(self)
|
||||
self.sets_handler = SheerkaSetsManager(self)
|
||||
self.evaluate_concept_handler = SheerkaEvaluateConcept(self)
|
||||
|
||||
def initialize(self, root_folder: str = None):
|
||||
"""
|
||||
Starting Sheerka
|
||||
Loads the current configuration
|
||||
Notes that when it's the first time, it also create the needed working folders
|
||||
:param root_folder: root configuration folder
|
||||
:return: ReturnValue(Success or Error)
|
||||
"""
|
||||
|
||||
try:
|
||||
self.sdp = SheerkaDataProvider(root_folder)
|
||||
if self.sdp.first_time:
|
||||
self.sdp.set_key(self.USER_CONCEPTS_KEYS, 1000)
|
||||
|
||||
event = Event("Initializing Sheerka.")
|
||||
self.sdp.save_event(event)
|
||||
exec_context = ExecutionContext(self.key, event, self)
|
||||
|
||||
self.initialize_builtin_concepts()
|
||||
self.initialize_builtin_parsers()
|
||||
self.initialize_builtin_evaluators()
|
||||
self.initialize_concepts_definitions(exec_context)
|
||||
|
||||
except IOError as e:
|
||||
return ReturnValueConcept(self, False, self.get(BuiltinConcepts.ERROR), e)
|
||||
|
||||
return ReturnValueConcept(self, True, self)
|
||||
|
||||
def initialize_builtin_concepts(self):
|
||||
"""
|
||||
Initializes the builtin concepts
|
||||
:return: None
|
||||
"""
|
||||
self.init_log.debug("Initializing builtin concepts")
|
||||
builtins_classes = self.get_builtins_classes_as_dict()
|
||||
|
||||
# this all initialization of the builtins seems to be little bit complicated
|
||||
# why do we need to update it from DB ?
|
||||
for key in BuiltinConcepts:
|
||||
concept = self if key == BuiltinConcepts.SHEERKA \
|
||||
else builtins_classes[str(key)]() if str(key) in builtins_classes \
|
||||
else Concept(key, True, False, key)
|
||||
|
||||
if key in BuiltinUnique:
|
||||
concept.metadata.is_unique = True
|
||||
concept.metadata.is_evaluated = True
|
||||
|
||||
if not concept.metadata.is_unique and str(key) in builtins_classes:
|
||||
self.builtin_cache[key] = builtins_classes[str(key)]
|
||||
|
||||
if not self.skip_builtins_in_db:
|
||||
from_db = self.sdp.get_safe(self.CONCEPTS_ENTRY, concept.metadata.key)
|
||||
if from_db is None:
|
||||
self.init_log.debug(f"'{concept.name}' concept is not found in db. Adding.")
|
||||
self.set_id_if_needed(concept, True)
|
||||
self.sdp.add("init", self.CONCEPTS_ENTRY, concept, use_ref=True)
|
||||
else:
|
||||
self.init_log.debug(f"Found concept '{from_db}' in db. Updating.")
|
||||
concept.update_from(from_db)
|
||||
|
||||
self.add_in_cache(concept)
|
||||
|
||||
def initialize_builtin_parsers(self):
|
||||
"""
|
||||
Init the parsers
|
||||
:return:
|
||||
"""
|
||||
core.utils.init_package_import("parsers")
|
||||
base_class = core.utils.get_class("parsers.BaseParser.BaseParser")
|
||||
for parser in core.utils.get_sub_classes("parsers", base_class):
|
||||
if parser.__module__ == base_class.__module__:
|
||||
continue
|
||||
|
||||
self.init_log.debug(f"Adding builtin parser '{parser.__name__}'")
|
||||
self.parsers[core.utils.get_full_qualified_name(parser)] = parser
|
||||
|
||||
def initialize_builtin_evaluators(self):
|
||||
"""
|
||||
Init the evaluators
|
||||
:return:
|
||||
"""
|
||||
core.utils.init_package_import("evaluators")
|
||||
for evaluator in core.utils.get_sub_classes("evaluators", "evaluators.BaseEvaluator.OneReturnValueEvaluator"):
|
||||
self.init_log.debug(f"Adding builtin evaluator '{evaluator.__name__}'")
|
||||
self.evaluators.append(evaluator)
|
||||
|
||||
for evaluator in core.utils.get_sub_classes("evaluators", "evaluators.BaseEvaluator.AllReturnValuesEvaluator"):
|
||||
self.init_log.debug(f"Adding builtin evaluator '{evaluator.__name__}'")
|
||||
self.evaluators.append(evaluator)
|
||||
|
||||
def initialize_concepts_definitions(self, execution_context):
|
||||
self.init_log.debug("Initializing concepts definitions")
|
||||
definitions = self.sdp.get_safe(self.CONCEPTS_DEFINITIONS_ENTRY, load_origin=False)
|
||||
|
||||
if definitions is None:
|
||||
self.init_log.debug("No BNF defined")
|
||||
return
|
||||
|
||||
lexer_parser = self.parsers[CONCEPT_LEXER_PARSER_CLASS]()
|
||||
ret_val = lexer_parser.initialize(execution_context, definitions)
|
||||
if not ret_val.status:
|
||||
self.init_log.error("Failed to initialize concepts definitions " + str(ret_val.body))
|
||||
return
|
||||
|
||||
self.concepts_grammars = lexer_parser.concepts_grammars
|
||||
|
||||
def reset_cache(self, filter_to_use=None):
|
||||
"""
|
||||
reset the different cache that exists
|
||||
:param filter_to_use:
|
||||
:return:
|
||||
"""
|
||||
if filter_to_use is None:
|
||||
self.cache_by_key = {}
|
||||
self.cache_by_id = {}
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
return self
|
||||
|
||||
def evaluate_user_input(self, text: str, user_name="kodjo"):
|
||||
"""
|
||||
Note to KSI: If you try to add execution context to this function,
|
||||
You may end in an infinite loop
|
||||
:param text:
|
||||
:param user_name:
|
||||
:return:
|
||||
"""
|
||||
self.log.debug(f"Processing user input '{text}', {user_name=}.")
|
||||
event = Event(text, user_name)
|
||||
evt_digest = self.sdp.save_event(event)
|
||||
self.log.debug(f"{evt_digest=}")
|
||||
|
||||
with ExecutionContext(self.key, event, self, f"Evaluating '{text}'") as execution_context:
|
||||
user_input = self.ret(self.name, True, self.new(BuiltinConcepts.USER_INPUT, body=text, user_name=user_name))
|
||||
reduce_requested = self.ret(self.name, True, self.new(BuiltinConcepts.REDUCE_REQUESTED))
|
||||
|
||||
steps = [
|
||||
BuiltinConcepts.BEFORE_PARSING,
|
||||
BuiltinConcepts.PARSING,
|
||||
BuiltinConcepts.AFTER_PARSING,
|
||||
BuiltinConcepts.BEFORE_EVALUATION,
|
||||
BuiltinConcepts.EVALUATION,
|
||||
BuiltinConcepts.AFTER_EVALUATION
|
||||
]
|
||||
|
||||
ret = self.execute(execution_context, [user_input, reduce_requested], steps)
|
||||
execution_context.add_values(return_values=ret)
|
||||
|
||||
if not self.skip_builtins_in_db:
|
||||
self.sdp.save_result(execution_context)
|
||||
return ret
|
||||
|
||||
def execute(self, execution_context, return_values, execution_steps, logger=None):
|
||||
"""
|
||||
Executes process for all initial contexts
|
||||
:param execution_context:
|
||||
:param return_values:
|
||||
:param execution_steps:
|
||||
:param logger: logger to use (if not directly called by sheerka)
|
||||
:return:
|
||||
"""
|
||||
return self.execute_handler.execute(execution_context, return_values, execution_steps, logger)
|
||||
|
||||
def set_id_if_needed(self, obj: Concept, is_builtin: bool):
|
||||
"""
|
||||
Set the key for the concept if needed
|
||||
For test purpose only !!!!!
|
||||
:param obj:
|
||||
:param is_builtin:
|
||||
:return:
|
||||
"""
|
||||
if obj.metadata.id is not None:
|
||||
return
|
||||
|
||||
entry = self.BUILTIN_CONCEPTS_KEYS if is_builtin else self.USER_CONCEPTS_KEYS
|
||||
obj.metadata.id = self.sdp.get_next_key(entry)
|
||||
self.log.debug(f"Setting id '{obj.metadata.id}' to concept '{obj.metadata.name}'.")
|
||||
|
||||
def create_new_concept(self, context, concept: Concept, logger=None):
|
||||
"""
|
||||
Adds a new concept to the system
|
||||
:param context:
|
||||
:param concept: DefConceptNode
|
||||
:param logger
|
||||
:return: digest of the new concept
|
||||
"""
|
||||
|
||||
return self.create_new_concept_handler.create_new_concept(context, concept, 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
|
||||
:param context:
|
||||
:param concept:
|
||||
:param concept_set:
|
||||
:param logger:
|
||||
:return:
|
||||
"""
|
||||
return self.sets_handler.add_concept_to_set(context, concept, concept_set, logger)
|
||||
|
||||
def get_set_elements(self, concept):
|
||||
"""
|
||||
Concept is supposed to be a set
|
||||
Returns all elements if the set
|
||||
:param concept:
|
||||
:return:
|
||||
"""
|
||||
|
||||
return self.sets_handler.get_set_elements(concept)
|
||||
|
||||
def evaluate_concept(self, context, concept: Concept, logger=None):
|
||||
"""
|
||||
Evaluation a concept
|
||||
It means that if the where clause is True, will evaluate the body
|
||||
:param context:
|
||||
:param concept:
|
||||
:param logger:
|
||||
:return: value of the evaluation or error
|
||||
"""
|
||||
return self.evaluate_concept_handler.evaluate_concept(context, concept, logger)
|
||||
|
||||
def add_in_cache(self, concept: Concept):
|
||||
"""
|
||||
Adds a concept template in cache.
|
||||
The cache is used as a proxy before looking at sdp
|
||||
:param concept:
|
||||
:return:
|
||||
"""
|
||||
|
||||
# sanity check
|
||||
if concept.key is None:
|
||||
concept.init_key()
|
||||
|
||||
if concept.key is None:
|
||||
raise KeyError()
|
||||
|
||||
self.cache_by_key[concept.key] = concept
|
||||
|
||||
if concept.id:
|
||||
self.cache_by_id[concept.id] = concept
|
||||
|
||||
return concept
|
||||
|
||||
def get(self, concept_key, concept_id=None):
|
||||
"""
|
||||
Tries to find a concept
|
||||
What is return must be used a template for another concept.
|
||||
You must not modify the returned concept
|
||||
:param concept_key: key of the concept
|
||||
:param concept_id: when multiple concepts with the same key, use the id
|
||||
:return:
|
||||
"""
|
||||
|
||||
if concept_key is None:
|
||||
return ErrorConcept("Concept key is undefined.")
|
||||
|
||||
if isinstance(concept_key, BuiltinConcepts):
|
||||
concept_key = str(concept_key)
|
||||
|
||||
# first search in cache
|
||||
result = self.cache_by_key[concept_key] if concept_key in self.cache_by_key else \
|
||||
self.sdp.get_safe(self.CONCEPTS_ENTRY, concept_key)
|
||||
|
||||
if result and (concept_id is None or not isinstance(result, list)):
|
||||
return result
|
||||
|
||||
if isinstance(result, list):
|
||||
if concept_id:
|
||||
for c in result:
|
||||
if c.id == concept_id:
|
||||
return c
|
||||
else:
|
||||
return result
|
||||
|
||||
metadata = [("key", concept_key), ("id", concept_id)] if concept_id else ("key", concept_key)
|
||||
return self._get_unknown(metadata)
|
||||
|
||||
def get_by_id(self, concept_id):
|
||||
if concept_id is None:
|
||||
return ErrorConcept("Concept id is undefined.")
|
||||
|
||||
# first search in cache
|
||||
result = self.cache_by_id[concept_id] if concept_id in self.cache_by_id else \
|
||||
self.sdp.get_safe(self.CONCEPTS_BY_ID_ENTRY, concept_id)
|
||||
|
||||
return result or self._get_unknown(('id', concept_id))
|
||||
|
||||
def get_concept_definition(self):
|
||||
if self.concepts_definition_cache:
|
||||
return self.concepts_definition_cache
|
||||
|
||||
self.concepts_definition_cache = self.sdp.get_safe(
|
||||
self.CONCEPTS_DEFINITIONS_ENTRY,
|
||||
load_origin=False) or {}
|
||||
return self.concepts_definition_cache
|
||||
|
||||
def new(self, concept_key, **kwargs):
|
||||
"""
|
||||
Returns an instance of a new concept
|
||||
When the concept is supposed to be unique, returns the same instance
|
||||
:param concept_key:
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
if isinstance(concept_key, tuple):
|
||||
concept_key, concept_id = concept_key[0], concept_key[1]
|
||||
else:
|
||||
concept_id = None
|
||||
|
||||
template = self.get(concept_key, concept_id)
|
||||
|
||||
# manage concept not found
|
||||
if self.isinstance(template, BuiltinConcepts.UNKNOWN_CONCEPT) and \
|
||||
concept_key != BuiltinConcepts.UNKNOWN_CONCEPT:
|
||||
return template
|
||||
|
||||
if isinstance(template, list):
|
||||
# if template is a list, it means that there a multiple concepts under the same key
|
||||
concepts = [self.new_from_template(t, concept_key, **kwargs) for t in template]
|
||||
return concepts
|
||||
else:
|
||||
return self.new_from_template(template, concept_key, **kwargs)
|
||||
|
||||
def new_from_template(self, template, key, **kwargs):
|
||||
# manage singleton
|
||||
if template.metadata.is_unique:
|
||||
return template
|
||||
|
||||
# otherwise, create another instance
|
||||
concept = self.builtin_cache[key]() if key in self.builtin_cache else Concept()
|
||||
concept.update_from(template)
|
||||
|
||||
if len(kwargs) == 0:
|
||||
return concept
|
||||
|
||||
# update the properties, values, attributes
|
||||
# Not quite sure that this is the correct process order
|
||||
for k, v in kwargs.items():
|
||||
if k in concept.props:
|
||||
concept.set_prop(k, v)
|
||||
elif k in PROPERTIES_FOR_NEW:
|
||||
concept.values[ConceptParts(k)] = v
|
||||
elif hasattr(concept, k):
|
||||
setattr(concept, k, v)
|
||||
else:
|
||||
return self.new(BuiltinConcepts.UNKNOWN_PROPERTY, body=k, concept=concept)
|
||||
|
||||
# TODO : add the concept to the list of known concepts (self.instances)
|
||||
concept.metadata.is_evaluated = True
|
||||
return concept
|
||||
|
||||
def ret(self, who: str, status: bool, value, message=None, parents=None):
|
||||
"""
|
||||
Creates and returns a ReturnValue concept
|
||||
:param who:
|
||||
:param status:
|
||||
:param value:
|
||||
:param message:
|
||||
:param parents:
|
||||
:return:
|
||||
"""
|
||||
return self.new(
|
||||
BuiltinConcepts.RETURN_VALUE,
|
||||
who=who,
|
||||
status=status,
|
||||
value=value,
|
||||
message=message,
|
||||
parents=parents)
|
||||
|
||||
def value(self, obj, reduce_simple_list=False):
|
||||
if obj is None:
|
||||
return None
|
||||
|
||||
if hasattr(obj, "get_value"):
|
||||
return obj.get_value()
|
||||
|
||||
if not isinstance(obj, Concept):
|
||||
return obj
|
||||
|
||||
if obj.body is None:
|
||||
return obj
|
||||
|
||||
if reduce_simple_list and (isinstance(obj.body, list) or isinstance(obj.body, set)) and len(obj.body) == 1:
|
||||
body_to_use = obj.body[0]
|
||||
else:
|
||||
body_to_use = obj.body
|
||||
|
||||
return self.value(body_to_use)
|
||||
|
||||
def get_values(self, objs):
|
||||
if not (isinstance(objs, list) or
|
||||
self.isinstance(objs, BuiltinConcepts.LIST) or
|
||||
self.isinstance(objs, BuiltinConcepts.ENUMERATION)):
|
||||
objs = [objs]
|
||||
|
||||
return (self.value(obj) for obj in objs)
|
||||
|
||||
def is_success(self, obj):
|
||||
if isinstance(obj, bool): # quick win
|
||||
return obj
|
||||
|
||||
if isinstance(obj, ReturnValueConcept):
|
||||
return obj.status
|
||||
|
||||
if isinstance(obj, Concept) and obj.metadata.is_builtin and obj.key in BuiltinErrors:
|
||||
return False
|
||||
|
||||
return obj
|
||||
|
||||
def is_known(self, obj):
|
||||
if not isinstance(obj, Concept):
|
||||
return True
|
||||
|
||||
return obj.key != str(BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
|
||||
def isinstance(self, a, b):
|
||||
"""
|
||||
return true if the concept a is an instance of the concept b
|
||||
:param a:
|
||||
:param b:
|
||||
:return:
|
||||
"""
|
||||
|
||||
if isinstance(a, BuiltinConcepts): # common KSI error ;-)
|
||||
raise SyntaxError("Remember that the first parameter of isinstance MUST be a concept")
|
||||
|
||||
if not isinstance(a, Concept):
|
||||
return False
|
||||
|
||||
b_key = b.key if isinstance(b, Concept) else str(b)
|
||||
|
||||
return a.key == b_key
|
||||
|
||||
def isa(self, a, b):
|
||||
return self.sets_handler.isa(a, b)
|
||||
|
||||
def isagroup(self, concept):
|
||||
return self.sets_handler.isagroup(concept)
|
||||
|
||||
def get_evaluator_name(self, name):
|
||||
if self.evaluators_prefix is None:
|
||||
base_evaluator_class = core.utils.get_class("evaluators.BaseEvaluator.BaseEvaluator")
|
||||
self.evaluators_prefix = base_evaluator_class.PREFIX
|
||||
|
||||
return self.evaluators_prefix + name
|
||||
|
||||
def get_parser_name(self, name):
|
||||
if self.parsers_prefix is None:
|
||||
base_parser_class = core.utils.get_class("parsers.BaseParser.BaseParser")
|
||||
self.parsers_prefix = base_parser_class.PREFIX
|
||||
|
||||
return self.parsers_prefix + name
|
||||
|
||||
def concepts(self):
|
||||
res = []
|
||||
lst = self.sdp.list(self.CONCEPTS_ENTRY)
|
||||
for item in lst:
|
||||
if isinstance(item, list):
|
||||
res.extend(item)
|
||||
else:
|
||||
res.append(item)
|
||||
|
||||
return sorted(res, key=lambda i: int(i.id))
|
||||
|
||||
def test(self):
|
||||
return f"I have access to Sheerka !"
|
||||
|
||||
def test_error(self):
|
||||
raise Exception("I can raise an error")
|
||||
|
||||
@staticmethod
|
||||
def _get_unknown(metadata):
|
||||
"""
|
||||
Returns the concept 'UnknownConcept' for a requested id or key
|
||||
Note that I don't call the new() method to prevent cyclic call
|
||||
:param metadata:
|
||||
:return:
|
||||
"""
|
||||
|
||||
# metadata is a list of tuple that contains the known metadata for this concept
|
||||
# ex : (key, 'not_found)
|
||||
# or
|
||||
# (id, invalid_id)
|
||||
#
|
||||
# the metadata can be a list, if several attributes where given
|
||||
# (key, 'not_found), (id, invalid_id)
|
||||
|
||||
unknown_concept = UnknownConcept()
|
||||
unknown_concept.set_metadata_value(ConceptParts.BODY, metadata)
|
||||
for meta in (metadata if isinstance(metadata, list) else [metadata]):
|
||||
unknown_concept.set_prop(meta[0], meta[1])
|
||||
unknown_concept.metadata.is_evaluated = True
|
||||
return unknown_concept
|
||||
|
||||
@staticmethod
|
||||
def get_builtins_classes_as_dict():
|
||||
res = {}
|
||||
for c in core.utils.get_classes("core.builtin_concepts"):
|
||||
if issubclass(c, Concept) and c != Concept:
|
||||
res[c().metadata.key] = c
|
||||
|
||||
return res
|
||||
|
||||
@staticmethod
|
||||
def init_logging(debug, loggers):
|
||||
core.sheerka_logger.set_enabled(loggers)
|
||||
if debug:
|
||||
# log_format = "%(asctime)s %(name)s [%(levelname)s] %(message)s"
|
||||
log_format = "%(asctime)s [%(levelname)s] %(message)s"
|
||||
log_level = logging.DEBUG
|
||||
else:
|
||||
log_format = "%(message)s"
|
||||
log_level = logging.INFO
|
||||
|
||||
logging.basicConfig(format=log_format, level=log_level, handlers=[console_handler])
|
||||
@@ -0,0 +1,99 @@
|
||||
from core.builtin_concepts import BuiltinConcepts, ErrorConcept
|
||||
from core.concept import Concept
|
||||
from sdp.sheerkaDataProvider import SheerkaDataProviderDuplicateKeyError
|
||||
|
||||
CONCEPT_LEXER_PARSER_CLASS = "parsers.ConceptLexerParser.ConceptLexerParser"
|
||||
|
||||
|
||||
class SheerkaCreateNewConcept:
|
||||
"""
|
||||
Manage the creation of a new concept
|
||||
"""
|
||||
|
||||
def __init__(self, sheerka):
|
||||
self.sheerka = sheerka
|
||||
self.logger_name = self.create_new_concept.__name__
|
||||
|
||||
def create_new_concept(self, context, concept: Concept, logger=None):
|
||||
"""
|
||||
Adds a new concept to the system
|
||||
:param context:
|
||||
:param concept: DefConceptNode
|
||||
:param logger
|
||||
:return: digest of the new concept
|
||||
"""
|
||||
|
||||
logger = logger or self.sheerka.log
|
||||
|
||||
concept.init_key()
|
||||
concepts_definitions = None
|
||||
init_ret_value = None
|
||||
|
||||
# checks for duplicate concepts
|
||||
# TODO checks if it exists in cache first
|
||||
|
||||
if self.sheerka.sdp.exists(self.sheerka.CONCEPTS_ENTRY, concept.key, concept.get_digest()):
|
||||
error = SheerkaDataProviderDuplicateKeyError(self.sheerka.CONCEPTS_ENTRY + "." + concept.key, concept)
|
||||
return self.sheerka.ret(
|
||||
self.logger_name,
|
||||
False,
|
||||
self.sheerka.new(BuiltinConcepts.CONCEPT_ALREADY_DEFINED, body=concept),
|
||||
error.args[0])
|
||||
|
||||
# set id before saving in db
|
||||
self.sheerka.set_id_if_needed(concept, False)
|
||||
|
||||
# add the BNF if known
|
||||
if concept.bnf:
|
||||
concepts_definitions = self.sheerka.get_concept_definition()
|
||||
concepts_definitions[concept] = concept.bnf
|
||||
|
||||
# check if it's a valid BNF or whether it breaks the known rules
|
||||
concept_lexer_parser = self.sheerka.parsers[CONCEPT_LEXER_PARSER_CLASS]()
|
||||
with context.push(self.sheerka.name, desc=f"Initializing concept definition for {concept}") as sub_context:
|
||||
sub_context.concepts[concept.key] = concept # the concept is not in the real cache yet
|
||||
sub_context.log_new(logger)
|
||||
init_ret_value = concept_lexer_parser.initialize(sub_context, concepts_definitions)
|
||||
sub_context.add_values(return_values=init_ret_value)
|
||||
if not init_ret_value.status:
|
||||
return self.sheerka.ret(self.logger_name, False, ErrorConcept(init_ret_value.value))
|
||||
|
||||
# save the new concept in sdp
|
||||
try:
|
||||
# TODO : needs to make these calls atomic (or at least one single call)
|
||||
self.sheerka.sdp.add(
|
||||
context.event.get_digest(),
|
||||
self.sheerka.CONCEPTS_ENTRY,
|
||||
concept,
|
||||
use_ref=True)
|
||||
self.sheerka.sdp.add(
|
||||
context.event.get_digest(),
|
||||
self.sheerka.CONCEPTS_BY_ID_ENTRY,
|
||||
{concept.id: concept.get_digest()},
|
||||
is_ref=True)
|
||||
if concepts_definitions is not None:
|
||||
self.sheerka.sdp.set(
|
||||
context.event.get_digest(),
|
||||
self.sheerka.CONCEPTS_DEFINITIONS_ENTRY,
|
||||
concepts_definitions,
|
||||
use_ref=True)
|
||||
except SheerkaDataProviderDuplicateKeyError as error:
|
||||
context.log_error(logger, "Failed to create a new concept.", who=self.logger_name)
|
||||
return self.sheerka.ret(
|
||||
self.logger_name,
|
||||
False,
|
||||
self.sheerka.new(BuiltinConcepts.CONCEPT_ALREADY_DEFINED, body=concept),
|
||||
error.args[0])
|
||||
|
||||
# Updates the caches
|
||||
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:
|
||||
self.sheerka.concepts_grammars = init_ret_value.body
|
||||
|
||||
# process the return in needed
|
||||
ret = self.sheerka.ret(self.logger_name, True, self.sheerka.new(BuiltinConcepts.NEW_CONCEPT, body=concept))
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
|
||||
|
||||
class SheerkaDump:
|
||||
def __init__(self, sheerka):
|
||||
self.sheerka = sheerka
|
||||
|
||||
def dump_concepts(self):
|
||||
lst = self.sheerkasdp.list(self.sheerkaCONCEPTS_ENTRY)
|
||||
for item in lst:
|
||||
if hasattr(item, "__iter__"):
|
||||
for i in item:
|
||||
self.sheerkalog.info(i)
|
||||
else:
|
||||
self.sheerkalog.info(item)
|
||||
|
||||
def dump_definitions(self):
|
||||
defs = self.sheerkasdp.get(self.sheerkaCONCEPTS_DEFINITIONS_ENTRY)
|
||||
self.sheerkalog.info(defs)
|
||||
|
||||
def dump_desc(self, *concept_names):
|
||||
first = True
|
||||
for concept_name in concept_names:
|
||||
if isinstance(concept_name, Concept):
|
||||
concepts = concept_name
|
||||
else:
|
||||
concepts = self.sheerkaget(concept_name)
|
||||
if self.sheerkaisinstance(concepts, BuiltinConcepts.UNKNOWN_CONCEPT):
|
||||
self.sheerkalog.error(f"Concept '{concept_name}' is unknown")
|
||||
return False
|
||||
|
||||
if not hasattr(concepts, "__iter__"):
|
||||
concepts = [concepts]
|
||||
|
||||
for c in concepts:
|
||||
if not first:
|
||||
self.sheerkalog.info("")
|
||||
self.sheerkalog.info(f"name : {c.name}")
|
||||
self.sheerkalog.info(f"bnf : {c.metadata.definition}")
|
||||
self.sheerkalog.info(f"key : {c.key}")
|
||||
self.sheerkalog.info(f"body : {c.body}")
|
||||
self.sheerkalog.info(f"digest : {c.get_digest()}")
|
||||
first = False
|
||||
@@ -0,0 +1,195 @@
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import Concept, DoNotResolve, ConceptParts
|
||||
import core.builtin_helpers
|
||||
|
||||
CONCEPT_EVALUATION_STEPS = [
|
||||
BuiltinConcepts.BEFORE_EVALUATION,
|
||||
BuiltinConcepts.EVALUATION,
|
||||
BuiltinConcepts.AFTER_EVALUATION]
|
||||
|
||||
|
||||
class SheerkaEvaluateConcept:
|
||||
def __init__(self, sheerka):
|
||||
self.sheerka = sheerka
|
||||
self.logger_name = self.evaluate_concept.__name__
|
||||
|
||||
def initialize_concept_asts(self, context, concept: Concept, logger=None):
|
||||
"""
|
||||
Updates the codes of the newly created concept
|
||||
Basically, it runs the parsers on all parts
|
||||
:param concept:
|
||||
:param context:
|
||||
:param logger:
|
||||
:return:
|
||||
"""
|
||||
steps = [BuiltinConcepts.BEFORE_PARSING, BuiltinConcepts.PARSING, BuiltinConcepts.AFTER_PARSING]
|
||||
for part_key in ConceptParts:
|
||||
if part_key in concept.compiled:
|
||||
continue
|
||||
|
||||
source = getattr(concept.metadata, part_key.value)
|
||||
if source is None or not isinstance(source, str):
|
||||
continue
|
||||
|
||||
if source.strip() == "":
|
||||
concept.compiled[part_key] = DoNotResolve(source)
|
||||
else:
|
||||
with context.push(desc=f"Initializing compiled for {part_key}") as sub_context:
|
||||
sub_context.log_new(logger)
|
||||
sub_context.add_inputs(source=source)
|
||||
to_parse = self.sheerka.ret(context.who, True,
|
||||
self.sheerka.new(BuiltinConcepts.USER_INPUT, body=source))
|
||||
res = self.sheerka.execute(sub_context, to_parse, steps, logger)
|
||||
concept.compiled[part_key] = res
|
||||
sub_context.add_values(return_values=res)
|
||||
|
||||
for prop, default_value in concept.metadata.props:
|
||||
if prop in concept.compiled:
|
||||
continue
|
||||
|
||||
if default_value is None or not isinstance(default_value, str):
|
||||
continue
|
||||
|
||||
if default_value.strip() == "":
|
||||
concept.compiled[prop] = DoNotResolve(default_value)
|
||||
else:
|
||||
with context.push(desc=f"Initializing AST for property {prop}") as sub_context:
|
||||
sub_context.log_new(logger)
|
||||
sub_context.add_inputs(source=default_value)
|
||||
to_parse = self.sheerka.ret(context.who, True,
|
||||
self.sheerka.new(BuiltinConcepts.USER_INPUT, body=default_value))
|
||||
res = self.sheerka.execute(context, to_parse, steps)
|
||||
concept.compiled[prop] = res
|
||||
sub_context.add_values(return_values=res)
|
||||
|
||||
# Updates the cache of concepts when possible
|
||||
if concept.key in self.sheerka.cache_by_key:
|
||||
entry = self.sheerka.cache_by_key[concept.key]
|
||||
if isinstance(entry, list):
|
||||
# TODO : manage when there are multiple entries
|
||||
pass
|
||||
else:
|
||||
self.sheerka.cache_by_key[concept.key].compiled = concept.compiled
|
||||
|
||||
def resolve(self, context, to_resolve, current_prop, current_concept, logger):
|
||||
if isinstance(to_resolve, DoNotResolve):
|
||||
return to_resolve.value
|
||||
|
||||
desc = f"Evaluating {current_prop} (concept={current_concept})"
|
||||
context.log(logger, desc, self.logger_name)
|
||||
with context.push(desc=desc, obj=current_concept) as sub_context:
|
||||
sub_context.log_new(logger)
|
||||
|
||||
# when it's a concept, evaluate it
|
||||
if isinstance(to_resolve, Concept) and \
|
||||
not context.sheerka.isinstance(to_resolve, BuiltinConcepts.RETURN_VALUE):
|
||||
evaluated = self.evaluate_concept(sub_context, to_resolve, logger)
|
||||
sub_context.add_values(return_values=evaluated)
|
||||
if evaluated.key == to_resolve.key:
|
||||
return evaluated
|
||||
else:
|
||||
error = evaluated
|
||||
|
||||
# otherwise, execute all return values to find out what is the value
|
||||
else:
|
||||
r = self.sheerka.execute(sub_context, to_resolve, CONCEPT_EVALUATION_STEPS, logger)
|
||||
one_r = core.builtin_helpers.expect_one(context, r)
|
||||
sub_context.add_values(return_values=one_r)
|
||||
if one_r.status:
|
||||
return one_r.value
|
||||
else:
|
||||
error = one_r.value
|
||||
|
||||
return self.sheerka.new(BuiltinConcepts.CONCEPT_EVAL_ERROR,
|
||||
body=error,
|
||||
concept=current_concept,
|
||||
property_name=current_prop)
|
||||
|
||||
def resolve_list(self, context, list_to_resolve, current_prop, current_concept, logger):
|
||||
"""When dealing with a list, there are two possibilities"""
|
||||
# It may be a list of ReturnValueConcept to execute (always the case for metadata)
|
||||
# or a list of single values (may be the case for properties)
|
||||
# in this latter case, all values are to be processed one by one and a list should be returned
|
||||
if len(list_to_resolve) == 0:
|
||||
return []
|
||||
|
||||
if self.sheerka.isinstance(list_to_resolve[0], BuiltinConcepts.RETURN_VALUE):
|
||||
return self.resolve(context, list_to_resolve, current_prop, current_concept, logger)
|
||||
|
||||
res = []
|
||||
for to_resolve in list_to_resolve:
|
||||
# sanity check
|
||||
if self.sheerka.isinstance(to_resolve, BuiltinConcepts.RETURN_VALUE):
|
||||
return self.sheerka.new(BuiltinConcepts.CONCEPT_EVAL_ERROR,
|
||||
body="Mix between real values and return values",
|
||||
concept=current_concept,
|
||||
property_name=current_prop)
|
||||
|
||||
r = self.resolve(context, to_resolve, current_prop, current_concept, logger)
|
||||
if self.sheerka.isinstance(r, BuiltinConcepts.CONCEPT_EVAL_ERROR):
|
||||
return r
|
||||
res.append(r)
|
||||
|
||||
return res
|
||||
|
||||
def evaluate_concept(self, context, concept: Concept, logger=None):
|
||||
"""
|
||||
Evaluation a concept
|
||||
It means that if the where clause is True, will evaluate the body
|
||||
:param context:
|
||||
:param concept:
|
||||
:param logger:
|
||||
:return: value of the evaluation or error
|
||||
"""
|
||||
|
||||
logger = logger or self.sheerka.log
|
||||
|
||||
if concept.metadata.is_evaluated:
|
||||
return concept
|
||||
|
||||
# WHERE condition should already be validated by the parser.
|
||||
# It's a mandatory condition for the concept before it can be recognized
|
||||
|
||||
#
|
||||
# TODO : Validate the PRE condition
|
||||
#
|
||||
|
||||
self.initialize_concept_asts(context, concept, logger)
|
||||
|
||||
# to make sure of the order, it don't use ConceptParts.get_parts()
|
||||
# props must be evaluated first
|
||||
all_metadata_to_eval = ["props", "where", "pre", "post", "body"]
|
||||
|
||||
for metadata_to_eval in all_metadata_to_eval:
|
||||
if metadata_to_eval == "props":
|
||||
for prop_name in (p for p in concept.props if p in concept.compiled):
|
||||
prop_ast = concept.compiled[prop_name]
|
||||
|
||||
if isinstance(prop_ast, list):
|
||||
# Do not send the current concept for the properties
|
||||
resolved = self.resolve_list(context, prop_ast, prop_name, None, logger)
|
||||
else:
|
||||
# Do not send the current concept for the properties
|
||||
resolved = self.resolve(context, prop_ast, prop_name, None, logger)
|
||||
if context.sheerka.isinstance(resolved, BuiltinConcepts.CONCEPT_EVAL_ERROR):
|
||||
resolved.set_prop("concept", concept) # since current concept was not sent
|
||||
return resolved
|
||||
else:
|
||||
concept.set_prop(prop_name, resolved)
|
||||
else:
|
||||
part_key = ConceptParts(metadata_to_eval)
|
||||
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)
|
||||
if context.sheerka.isinstance(resolved, BuiltinConcepts.CONCEPT_EVAL_ERROR):
|
||||
return resolved
|
||||
else:
|
||||
concept.values[part_key] = resolved
|
||||
|
||||
#
|
||||
# TODO : Validate the POST condition
|
||||
#
|
||||
|
||||
concept.init_key() # only does it if needed
|
||||
concept.metadata.is_evaluated = True
|
||||
return concept
|
||||
@@ -0,0 +1,254 @@
|
||||
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept
|
||||
import core.utils
|
||||
|
||||
|
||||
class SheerkaExecute:
|
||||
"""
|
||||
Manage the execution of a process flow
|
||||
"""
|
||||
|
||||
def __init__(self, sheerka):
|
||||
self.sheerka = sheerka
|
||||
|
||||
def call_parsers(self, execution_context, return_values, logger=None):
|
||||
|
||||
# return_values must be a list
|
||||
if not isinstance(return_values, list):
|
||||
return_values = [return_values]
|
||||
|
||||
# first make the distinguish between what is for the parsers and what is not
|
||||
result = []
|
||||
to_process = []
|
||||
for r in return_values:
|
||||
if not r.status or not self.sheerka.isinstance(r.body, BuiltinConcepts.USER_INPUT):
|
||||
result.append(r)
|
||||
else:
|
||||
to_process.append(r)
|
||||
|
||||
if not to_process:
|
||||
return result
|
||||
|
||||
# keep track of the originals user inputs, as they need to be removed at the end
|
||||
user_inputs = to_process[:]
|
||||
|
||||
# group the parsers by priorities
|
||||
instantiated_parsers = [parser(sheerka=self.sheerka) for parser in self.sheerka.parsers.values()]
|
||||
grouped_parsers = {}
|
||||
for parser in [p for p in instantiated_parsers if p.enabled]:
|
||||
if logger:
|
||||
parser.log = logger
|
||||
grouped_parsers.setdefault(parser.priority, []).append(parser)
|
||||
sorted_priorities = sorted(grouped_parsers.keys(), reverse=True)
|
||||
|
||||
stop_processing = False
|
||||
for priority in sorted_priorities:
|
||||
inputs_for_this_group = to_process[:]
|
||||
|
||||
for parser in grouped_parsers[priority]:
|
||||
|
||||
return_value_success_found = False
|
||||
for return_value in inputs_for_this_group:
|
||||
|
||||
to_parse = return_value.body.body \
|
||||
if self.sheerka.isinstance(return_value.body, BuiltinConcepts.USER_INPUT) \
|
||||
else return_value.body
|
||||
|
||||
# if self.sheerka.log.isEnabledFor(logging.DEBUG):
|
||||
# debug_text = "'" + to_parse + "'" if isinstance(to_parse, str) \
|
||||
# else "'" + BaseParser.get_text_from_tokens(to_parse) + "' as tokens"
|
||||
# execution_context.log(logger or self.sheerka.log, f"Parsing {debug_text}")
|
||||
|
||||
with execution_context.push(desc=f"Parsing using {parser.name}") as sub_context:
|
||||
sub_context.add_inputs(to_parse=to_parse)
|
||||
res = parser.parse(sub_context, to_parse)
|
||||
if res is not None:
|
||||
if hasattr(res, "__iter__"):
|
||||
for r in res:
|
||||
if r is None:
|
||||
continue
|
||||
r.parents = [return_value]
|
||||
result.append(r)
|
||||
if self.sheerka.isinstance(r.body, BuiltinConcepts.PARSER_RESULT):
|
||||
to_process.append(r)
|
||||
if r.status:
|
||||
return_value_success_found = True
|
||||
|
||||
else:
|
||||
res.parents = [return_value]
|
||||
result.append(res)
|
||||
if self.sheerka.isinstance(res.body, BuiltinConcepts.PARSER_RESULT):
|
||||
to_process.append(res)
|
||||
if res.status:
|
||||
return_value_success_found = True
|
||||
sub_context.add_values(return_values=res)
|
||||
|
||||
if return_value_success_found:
|
||||
stop_processing = True
|
||||
break # Stop the other return_values (but not the other parsers with the same priority)
|
||||
|
||||
if stop_processing:
|
||||
break # Do not try the other priorities if a match is found
|
||||
|
||||
result = core.utils.remove_list_from_list(result, user_inputs)
|
||||
return result
|
||||
|
||||
def call_evaluators(self, execution_context, return_values, process_step, evaluation_context=None, logger=None):
|
||||
|
||||
# return_values must be a list
|
||||
if not isinstance(return_values, list):
|
||||
return_values = [return_values]
|
||||
|
||||
# Evaluation context are contexts that may modify the behaviour of the execution
|
||||
# For example, a concept to indicate that the value is not wanted
|
||||
# Or a concept to indicate that we want the letter form of the response
|
||||
# But first, they need to be transformed into return values
|
||||
if evaluation_context is None:
|
||||
evaluation_return_values = []
|
||||
else:
|
||||
evaluation_return_values = [self.sheerka.ret(execution_context.who, True, c) for c in evaluation_context]
|
||||
|
||||
# add the current step as part as the evaluation context
|
||||
evaluation_return_values.append(self.sheerka.ret(execution_context.who, True, self.sheerka.new(process_step)))
|
||||
|
||||
# the pool of return values are the mix
|
||||
return_values.extend(evaluation_return_values)
|
||||
|
||||
# group the evaluators by priority and sort them
|
||||
# The first one to be applied will be the one with the highest priority
|
||||
grouped_evaluators = {}
|
||||
instantiated_evaluators = [e_class() for e_class in self.sheerka.evaluators]
|
||||
|
||||
# pre-process evaluators if needed
|
||||
instantiated_evaluators = self._preprocess_evaluators(execution_context, instantiated_evaluators)
|
||||
|
||||
for evaluator in [e for e in instantiated_evaluators if e.enabled and process_step in e.steps]:
|
||||
if logger:
|
||||
evaluator.log = logger
|
||||
grouped_evaluators.setdefault(evaluator.priority, []).append(evaluator)
|
||||
|
||||
# order the groups by priority, the higher first
|
||||
sorted_priorities = sorted(grouped_evaluators.keys(), reverse=True)
|
||||
|
||||
# process
|
||||
iteration = 0
|
||||
while True:
|
||||
with execution_context.push(desc=f"iteration #{iteration}", iteration=iteration) as iteration_context:
|
||||
simple_digest = return_values[:]
|
||||
iteration_context.add_inputs(return_values=simple_digest)
|
||||
|
||||
for priority in sorted_priorities:
|
||||
|
||||
original_items = return_values[:]
|
||||
evaluated_items = []
|
||||
to_delete = []
|
||||
for evaluator in grouped_evaluators[priority]:
|
||||
evaluator = self._preprocess_evaluators(execution_context, evaluator.__class__()) # fresh copy
|
||||
|
||||
sub_context_desc = f"Evaluating using {evaluator.name} ({priority=})"
|
||||
with iteration_context.push(desc=sub_context_desc) as sub_context:
|
||||
sub_context.add_inputs(return_values=original_items)
|
||||
|
||||
# process evaluators that work on one simple return value at the time
|
||||
from evaluators.BaseEvaluator import OneReturnValueEvaluator
|
||||
if isinstance(evaluator, OneReturnValueEvaluator):
|
||||
debug_result = []
|
||||
for item in original_items:
|
||||
if evaluator.matches(sub_context, item):
|
||||
result = evaluator.eval(sub_context, item)
|
||||
if result is None:
|
||||
debug_result.append({"input": item, "return_value": None})
|
||||
continue
|
||||
|
||||
to_delete.append(item)
|
||||
if isinstance(result, list):
|
||||
evaluated_items.extend(result)
|
||||
elif isinstance(result, ReturnValueConcept):
|
||||
evaluated_items.append(result)
|
||||
else:
|
||||
error = self.sheerka.new(BuiltinConcepts.INVALID_RETURN_VALUE, body=result,
|
||||
evaluator=evaluator)
|
||||
result = self.sheerka.ret("sheerka.process", False, error, parents=[item])
|
||||
evaluated_items.append(result)
|
||||
debug_result.append({"input": item, "return_value": result})
|
||||
else:
|
||||
debug_result.append({"input": item, "return_value": "** No Match **"})
|
||||
sub_context.add_values(return_values=debug_result)
|
||||
|
||||
# process evaluators that work on all return values
|
||||
else:
|
||||
if evaluator.matches(sub_context, original_items):
|
||||
results = evaluator.eval(sub_context, original_items)
|
||||
if results is None:
|
||||
continue
|
||||
if not isinstance(results, list):
|
||||
results = [results]
|
||||
for result in results:
|
||||
evaluated_items.append(result)
|
||||
to_delete.extend(result.parents)
|
||||
sub_context.add_values(return_values=results)
|
||||
else:
|
||||
sub_context.add_values(return_values="** No Match **")
|
||||
|
||||
return_values = evaluated_items
|
||||
return_values.extend([item for item in original_items if item not in to_delete])
|
||||
|
||||
iteration_context.add_values(return_values=return_values[:])
|
||||
|
||||
# have we done something ?
|
||||
to_compare = return_values[:]
|
||||
if simple_digest == to_compare:
|
||||
break
|
||||
|
||||
# inc the iteration and continue
|
||||
iteration += 1
|
||||
|
||||
# remove all evaluation context that are not reduced
|
||||
return_values = core.utils.remove_list_from_list(return_values, evaluation_return_values)
|
||||
return return_values
|
||||
|
||||
def execute(self, execution_context, return_values, execution_steps, logger=None):
|
||||
"""
|
||||
Executes process for all initial contexts
|
||||
:param execution_context:
|
||||
:param return_values:
|
||||
:param execution_steps:
|
||||
:param logger: logger to use (if not directly called by sheerka)
|
||||
:return:
|
||||
"""
|
||||
|
||||
for step in execution_steps:
|
||||
copy = return_values[:] if hasattr(return_values, "__iter__") else [return_values]
|
||||
with execution_context.push(step=step, iteration=0, desc=f"{step=}", return_values=copy) as sub_context:
|
||||
sub_context.log(logger or self.sheerka.log, f"{step=}, context='{sub_context}'")
|
||||
|
||||
if step == BuiltinConcepts.PARSING:
|
||||
return_values = self.call_parsers(sub_context, return_values, logger)
|
||||
else:
|
||||
return_values = self.call_evaluators(sub_context, return_values, step, None, logger)
|
||||
|
||||
if copy != return_values:
|
||||
sub_context.log_result(logger or self.sheerka.log, return_values)
|
||||
|
||||
sub_context.add_values(return_values=return_values)
|
||||
|
||||
return return_values
|
||||
|
||||
def _preprocess_evaluators(self, context, evaluators):
|
||||
if not context.preprocess:
|
||||
return evaluators
|
||||
|
||||
if not hasattr(evaluators, "__iter__"):
|
||||
single_one = True
|
||||
evaluators = [evaluators]
|
||||
else:
|
||||
single_one = False
|
||||
|
||||
for preprocess in context.preprocess:
|
||||
for e in evaluators:
|
||||
if preprocess.props["name"].value == e.name:
|
||||
for prop, value in preprocess.props.items():
|
||||
if prop == "name":
|
||||
continue
|
||||
if hasattr(e, prop):
|
||||
setattr(e, prop, value.value)
|
||||
return evaluators[0] if single_one else evaluators
|
||||
@@ -0,0 +1,83 @@
|
||||
from core.builtin_concepts import BuiltinConcepts, ErrorConcept
|
||||
from core.concept import Concept
|
||||
|
||||
GROUP_PREFIX = 'All_'
|
||||
|
||||
|
||||
class SheerkaSetsManager:
|
||||
def __init__(self, sheerka):
|
||||
self.sheerka = sheerka
|
||||
self.logger_name = self.add_concept_to_set.__name__
|
||||
|
||||
def add_concept_to_set(self, context, concept, concept_set, logger=None):
|
||||
"""
|
||||
Add an entry in sdp to tell that concept isa concept_set
|
||||
:param context:
|
||||
:param concept:
|
||||
:param concept_set:
|
||||
:param logger:
|
||||
:return:
|
||||
"""
|
||||
logger = logger or self.sheerka.log
|
||||
|
||||
context.log(logger, f"Adding concept {concept} to set {concept_set}", who=self.logger_name)
|
||||
|
||||
assert concept.id
|
||||
assert concept_set.id
|
||||
|
||||
try:
|
||||
ret = self.sheerka.sdp.add_unique(context.event.get_digest(), GROUP_PREFIX + concept_set.id, concept.id)
|
||||
if ret == (None, None): # concept already in set
|
||||
return self.sheerka.ret(
|
||||
self.logger_name,
|
||||
False,
|
||||
self.sheerka.new(BuiltinConcepts.CONCEPT_ALREADY_IN_SET, body=concept, concept_set=concept_set))
|
||||
else:
|
||||
return self.sheerka.ret(self.logger_name, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
|
||||
except Exception as error:
|
||||
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):
|
||||
"""
|
||||
Concept is supposed to be a set
|
||||
Returns all elements if the set
|
||||
:param concept:
|
||||
:return:
|
||||
"""
|
||||
|
||||
assert concept.id
|
||||
|
||||
ids = self.sheerka.sdp.get_safe(GROUP_PREFIX + concept.id)
|
||||
if ids is None:
|
||||
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
|
||||
|
||||
def isa(self, a, b):
|
||||
"""
|
||||
return true if the concept a is a b
|
||||
Will handle when the keyword isa will be implemented
|
||||
:param a:
|
||||
:param b:
|
||||
:return:
|
||||
"""
|
||||
|
||||
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)
|
||||
|
||||
# TODO, first check the 'isa' property of a
|
||||
|
||||
return self.sheerka.sdp.exists(GROUP_PREFIX + b.id, a.id)
|
||||
|
||||
def isagroup(self, concept):
|
||||
"""True if exists All_<concept_id> in sdp"""
|
||||
if not concept.id:
|
||||
return None
|
||||
|
||||
res = self.sheerka.sdp.get_safe(GROUP_PREFIX + concept.id)
|
||||
return res is not None
|
||||
@@ -2,7 +2,7 @@ import dataclasses
|
||||
from enum import Enum
|
||||
|
||||
from core.concept import Concept, PROPERTIES_TO_SERIALIZE
|
||||
from core.sheerka import ExecutionContext
|
||||
from core.sheerka.Sheerka import ExecutionContext
|
||||
from core.tokenizer import Token
|
||||
from evaluators.BaseEvaluator import BaseEvaluator
|
||||
from parsers.BaseParser import BaseParser, Node
|
||||
@@ -1,4 +1,4 @@
|
||||
from core.sheerka import ExecutionContext
|
||||
from core.sheerka.Sheerka import ExecutionContext
|
||||
from core.sheerka_logger import get_logger
|
||||
|
||||
|
||||
@@ -61,9 +61,9 @@ class PythonEvaluator(OneReturnValueEvaluator):
|
||||
def get_locals(self, context, node):
|
||||
my_locals = {
|
||||
"sheerka": context.sheerka,
|
||||
"desc": context.sheerka.dump_desc,
|
||||
"concepts": context.sheerka.dump_concepts,
|
||||
"definitions": context.sheerka.dump_definitions,
|
||||
"desc": context.sheerka.dump_handler.dump_desc,
|
||||
"concepts": context.sheerka.dump_handler.dump_concepts,
|
||||
"definitions": context.sheerka.dump_handler.dump_definitions,
|
||||
}
|
||||
if context.obj:
|
||||
context.log(self.verbose_log,
|
||||
@@ -2,7 +2,7 @@ from dataclasses import dataclass
|
||||
|
||||
import core.utils
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.sheerka import ExecutionContext
|
||||
from core.sheerka.Sheerka import ExecutionContext
|
||||
from core.tokenizer import Tokenizer, Token, TokenKind, LexerError
|
||||
from parsers.BaseParser import BaseParser, ErrorNode, UnexpectedTokenErrorNode
|
||||
from parsers.ConceptLexerParser import OrderedChoice, Sequence, Optional, ZeroOrMore, OneOrMore, ConceptExpression, StrMatch
|
||||
@@ -6,7 +6,7 @@ from parsers.BaseParser import BaseParser, Node, ErrorNode, NotInitializedNode
|
||||
from core.tokenizer import Tokenizer, TokenKind, Token, Keywords
|
||||
from dataclasses import dataclass, field
|
||||
from parsers.BnfParser import BnfParser
|
||||
from core.sheerka import ExecutionContext
|
||||
from core.sheerka.Sheerka import ExecutionContext
|
||||
|
||||
|
||||
@dataclass()
|
||||
@@ -252,7 +252,7 @@ class ExecutionContextSerializer(BaseSerializer):
|
||||
BaseSerializer.__init__(self, "R", 1)
|
||||
|
||||
def matches(self, obj):
|
||||
return core.utils.get_full_qualified_name(obj) == "core.sheerka.ExecutionContext"
|
||||
return core.utils.get_full_qualified_name(obj) == "core.sheerka.ExecutionContext.ExecutionContext"
|
||||
|
||||
def dump(self, stream, obj, context):
|
||||
as_json = obj.to_dict()
|
||||
@@ -0,0 +1,58 @@
|
||||
import ast
|
||||
|
||||
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept
|
||||
from core.concept import Concept
|
||||
from core.sheerka.ExecutionContext import ExecutionContext
|
||||
from sdp.sheerkaDataProvider import Event
|
||||
|
||||
|
||||
class BaseTest:
|
||||
def get_sheerka(self):
|
||||
pass
|
||||
|
||||
def get_context(self, sheerka=None):
|
||||
return ExecutionContext("test", Event(), sheerka or self.get_sheerka())
|
||||
|
||||
def get_default_concept(self):
|
||||
concept = Concept(
|
||||
name="a + b",
|
||||
where="isinstance(a, int) and isinstance(b, int)",
|
||||
pre="isinstance(a, int) and isinstance(b, int)",
|
||||
post="isinstance(res, int)",
|
||||
body="def func(x,y):\n return x+y\nfunc(a,b)",
|
||||
desc="specific description")
|
||||
concept.def_prop("a", "value1")
|
||||
concept.def_prop("b", "value2")
|
||||
|
||||
return concept
|
||||
|
||||
def dump_ast(self, node):
|
||||
dump = ast.dump(node)
|
||||
for to_remove in [", ctx=Load()", ", kind=None", ", type_ignores=[]"]:
|
||||
dump = dump.replace(to_remove, "")
|
||||
return dump
|
||||
|
||||
@staticmethod
|
||||
def retval(obj, who="who", status=True):
|
||||
"""ret_val"""
|
||||
return ReturnValueConcept.ret(who, status, obj)
|
||||
|
||||
@staticmethod
|
||||
def tretval(sheerka, obj, who="who"):
|
||||
"""True ret_val + add concept in cache"""
|
||||
if isinstance(obj, Concept):
|
||||
obj.init_key()
|
||||
if obj.key not in sheerka.cache_by_key:
|
||||
sheerka.cache_by_key[obj.key] = obj
|
||||
return sheerka.ret(who, True, obj)
|
||||
|
||||
@staticmethod
|
||||
def pretval(concept, source=None, parser="parsers.name"):
|
||||
"""ParserResult ret_val"""
|
||||
return ReturnValueConcept(
|
||||
"some_name",
|
||||
True,
|
||||
ParserResultConcept(parser=parser,
|
||||
source=source or concept.name,
|
||||
value=concept,
|
||||
try_parsed=concept))
|
||||
@@ -0,0 +1,34 @@
|
||||
import os
|
||||
import shutil
|
||||
from os import path
|
||||
|
||||
import pytest
|
||||
from core.sheerka.Sheerka import Sheerka
|
||||
from tests.BaseTest import BaseTest
|
||||
|
||||
|
||||
class TestUsingFileBasedSheerka(BaseTest):
|
||||
|
||||
tests_root = path.abspath("../../build/tests")
|
||||
root_folder = "init_folder"
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def init_test(self):
|
||||
if path.exists(self.tests_root):
|
||||
shutil.rmtree(self.tests_root)
|
||||
|
||||
if not path.exists(self.tests_root):
|
||||
os.makedirs(self.tests_root)
|
||||
current_pwd = os.getcwd()
|
||||
os.chdir(self.tests_root)
|
||||
|
||||
yield None
|
||||
|
||||
os.chdir(current_pwd)
|
||||
|
||||
def get_sheerka(self, use_dict=True, skip_builtins_in_db=True):
|
||||
root = "mem://" if use_dict else self.root_folder
|
||||
sheerka = Sheerka(skip_builtins_in_db=skip_builtins_in_db)
|
||||
sheerka.initialize(root)
|
||||
|
||||
return sheerka
|
||||
@@ -0,0 +1,10 @@
|
||||
from core.sheerka.Sheerka import Sheerka
|
||||
from tests.BaseTest import BaseTest
|
||||
|
||||
|
||||
class TestUsingMemoryBasedSheerka(BaseTest):
|
||||
|
||||
def get_sheerka(self, skip_builtins_in_db=True):
|
||||
sheerka = Sheerka(skip_builtins_in_db=skip_builtins_in_db)
|
||||
sheerka.initialize("mem://")
|
||||
return sheerka
|
||||
@@ -2,7 +2,7 @@ import pytest
|
||||
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from core.sheerka import ExecutionContext
|
||||
from core.sheerka.Sheerka import ExecutionContext
|
||||
from sdp.sheerkaDataProvider import Event
|
||||
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import PROPERTIES_TO_SERIALIZE
|
||||
from sdp.sheerkaDataProvider import SheerkaDataProvider
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
class TestSheerkaCreateNewConcept(TestUsingMemoryBasedSheerka):
|
||||
def test_i_can_add_a_concept(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept = self.get_default_concept()
|
||||
|
||||
res = sheerka.create_new_concept(self.get_context(sheerka), concept)
|
||||
|
||||
assert res.status
|
||||
assert sheerka.isinstance(res.value, BuiltinConcepts.NEW_CONCEPT)
|
||||
|
||||
concept_found = res.value.body
|
||||
for prop in PROPERTIES_TO_SERIALIZE:
|
||||
assert getattr(concept_found.metadata, prop) == getattr(concept.metadata, prop)
|
||||
|
||||
assert concept_found.key == "__var__0 + __var__1"
|
||||
assert concept_found.id == "1001"
|
||||
|
||||
assert concept.key in sheerka.cache_by_key
|
||||
assert concept.id in sheerka.cache_by_id
|
||||
assert sheerka.sdp.io.exists(
|
||||
sheerka.sdp.io.get_obj_path(SheerkaDataProvider.ObjectsFolder, concept_found.get_digest()))
|
||||
|
||||
def test_i_cannot_add_the_same_concept_twice(self):
|
||||
"""
|
||||
Checks that duplicated concepts are managed by sheerka, not by sheerka.sdp
|
||||
:return:
|
||||
"""
|
||||
sheerka = self.get_sheerka()
|
||||
concept = self.get_default_concept()
|
||||
|
||||
sheerka.create_new_concept(self.get_context(sheerka), concept)
|
||||
res = sheerka.create_new_concept(self.get_context(sheerka), concept)
|
||||
|
||||
assert not res.status
|
||||
assert sheerka.isinstance(res.value, BuiltinConcepts.CONCEPT_ALREADY_DEFINED)
|
||||
assert res.value.body == concept
|
||||
|
||||
def test_i_can_get_a_newly_created_concept(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept = self.get_default_concept()
|
||||
|
||||
sheerka.create_new_concept(self.get_context(sheerka), concept)
|
||||
|
||||
from_cache = sheerka.get(concept.key)
|
||||
assert from_cache is not None
|
||||
assert from_cache == concept
|
||||
|
||||
from_cache = sheerka.get_by_id(concept.id)
|
||||
assert from_cache is not None
|
||||
assert from_cache == concept
|
||||
|
||||
def test_i_first_look_in_local_cache(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept = self.get_default_concept()
|
||||
|
||||
sheerka.create_new_concept(self.get_context(sheerka), concept)
|
||||
sheerka.cache_by_key[concept.key].pre = "I have modified the concept in cache"
|
||||
|
||||
from_cache = sheerka.get(concept.key)
|
||||
assert from_cache is not None
|
||||
assert from_cache.key == concept.key
|
||||
assert from_cache.pre == "I have modified the concept in cache"
|
||||
|
||||
def test_i_can_get_a_known_concept_when_not_in_cache(self):
|
||||
"""
|
||||
When not in cache, uses sdp
|
||||
:return:
|
||||
"""
|
||||
sheerka = self.get_sheerka()
|
||||
concept = self.get_default_concept()
|
||||
sheerka.create_new_concept(self.get_context(sheerka), concept)
|
||||
|
||||
sheerka.cache_by_key = {} # reset the cache
|
||||
loaded = sheerka.get(concept.key)
|
||||
|
||||
assert loaded is not None
|
||||
assert loaded == concept
|
||||
|
||||
# I can also get it by its id
|
||||
loaded = sheerka.sdp.get(sheerka.CONCEPTS_BY_ID_ENTRY, concept.id)
|
||||
assert loaded is not None
|
||||
assert loaded == concept
|
||||
|
||||
def test_i_can_get_a_concept_by_its_id(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept = self.get_default_concept()
|
||||
sheerka.create_new_concept(self.get_context(sheerka), concept)
|
||||
|
||||
sheerka.cache_by_key = {} # reset the cache
|
||||
loaded = sheerka.get_by_id(concept.id)
|
||||
|
||||
assert loaded is not None
|
||||
assert loaded == concept
|
||||
|
||||
def test_i_can_get_list_of_concept_when_same_key_when_no_cache(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept1 = self.get_default_concept()
|
||||
concept2 = self.get_default_concept()
|
||||
concept2.metadata.body = "a+b"
|
||||
|
||||
res1 = sheerka.create_new_concept(self.get_context(sheerka), concept1)
|
||||
res2 = sheerka.create_new_concept(self.get_context(sheerka), concept2)
|
||||
|
||||
assert res1.value.body.key == res2.value.body.key # same key
|
||||
|
||||
sheerka.cache_by_key = {} # reset the cache
|
||||
|
||||
result = sheerka.get(concept1.key)
|
||||
assert len(result) == 2
|
||||
assert result[0] == concept1
|
||||
assert result[1] == concept2
|
||||
|
||||
def test_i_can_get_list_of_concept_when_same_key_when_cache(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept1 = self.get_default_concept()
|
||||
concept2 = self.get_default_concept()
|
||||
concept2.metadata.body = "a+b"
|
||||
|
||||
res1 = sheerka.create_new_concept(self.get_context(sheerka), concept1)
|
||||
res2 = sheerka.create_new_concept(self.get_context(sheerka), concept2)
|
||||
|
||||
assert res1.value.body.key == res2.value.body.key # same key
|
||||
|
||||
# sheerka.cache_by_key = {} # Do not reset the cache
|
||||
|
||||
result = sheerka.get(concept1.key)
|
||||
assert len(result) == 2
|
||||
assert result[0] == concept1
|
||||
assert result[1] == concept2
|
||||
|
||||
def test_i_can_get_the_correct_concept_using_the_id_when_same_key_when_no_cache(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept1 = self.get_default_concept()
|
||||
concept2 = self.get_default_concept()
|
||||
concept2.metadata.body = "a+b"
|
||||
|
||||
res1 = sheerka.create_new_concept(self.get_context(sheerka), concept1)
|
||||
res2 = sheerka.create_new_concept(self.get_context(sheerka), concept2)
|
||||
|
||||
assert res1.value.body.key == res2.value.body.key # same key
|
||||
|
||||
result = sheerka.get(concept1.key, res2.body.body.id)
|
||||
assert result.name == "a + b"
|
||||
assert result.metadata.body == "a+b"
|
||||
|
||||
def test_i_can_get_the_correct_concept_using_the_id__when_same_key_when_cache(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept1 = self.get_default_concept()
|
||||
concept2 = self.get_default_concept()
|
||||
concept2.metadata.body = "a+b"
|
||||
|
||||
res1 = sheerka.create_new_concept(self.get_context(sheerka), concept1)
|
||||
res2 = sheerka.create_new_concept(self.get_context(sheerka), concept2)
|
||||
|
||||
assert res1.value.body.key == res2.value.body.key # same key
|
||||
|
||||
result = sheerka.get(concept1.key, res2.body.body.id)
|
||||
assert result.name == "a + b"
|
||||
assert result.metadata.body == "a+b"
|
||||
|
||||
def test_i_cannot_get_the_correct_concept_id_the_id_is_wrong(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept1 = self.get_default_concept()
|
||||
concept2 = self.get_default_concept()
|
||||
concept2.metadata.body = "a+b"
|
||||
|
||||
res1 = sheerka.create_new_concept(self.get_context(sheerka), concept1)
|
||||
res2 = sheerka.create_new_concept(self.get_context(sheerka), concept2)
|
||||
|
||||
assert res1.value.body.key == res2.value.body.key # same key
|
||||
|
||||
result = sheerka.get(concept1.key, "wrong id")
|
||||
assert sheerka.isinstance(result, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
@@ -0,0 +1,332 @@
|
||||
import pytest
|
||||
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept, ParserResultConcept
|
||||
from core.concept import Concept, simplec, DoNotResolve, ConceptParts, Property
|
||||
from parsers.PythonParser import PythonNode
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
||||
|
||||
@pytest.mark.parametrize("body, expected", [
|
||||
# (None, None),
|
||||
# ("", ""),
|
||||
("1", 1),
|
||||
("1+1", 2),
|
||||
("'one'", "one"),
|
||||
("'one' + 'two'", "onetwo"),
|
||||
("True", True),
|
||||
("1 > 2", False),
|
||||
])
|
||||
def test_i_can_evaluate_a_concept_with_simple_body(self,body, expected):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
concept = Concept("foo", body=body)
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
|
||||
assert evaluated.key == concept.key
|
||||
assert evaluated.body == expected
|
||||
assert evaluated.metadata.body == body
|
||||
assert evaluated.metadata.pre is None
|
||||
assert evaluated.metadata.post is None
|
||||
assert evaluated.metadata.where is None
|
||||
assert evaluated.props == {}
|
||||
assert evaluated.metadata.is_evaluated
|
||||
assert len(evaluated.values) == 0 if body is None else 1
|
||||
|
||||
@pytest.mark.parametrize("expr, expected", [
|
||||
("", ""),
|
||||
("1", 1),
|
||||
("1+1", 2),
|
||||
("'one'", "one"),
|
||||
("'one' + 'two'", "onetwo"),
|
||||
("True", True),
|
||||
("1 > 2", False),
|
||||
])
|
||||
def test_i_can_evaluate_the_other_metadata(self,expr, expected):
|
||||
"""
|
||||
I only test WHERE, it's the same for the others
|
||||
:param expr:
|
||||
:param expected:
|
||||
:return:
|
||||
"""
|
||||
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
concept = Concept("foo", where=expr)
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
|
||||
assert evaluated.key == concept.key
|
||||
assert evaluated.metadata.body is None
|
||||
assert evaluated.metadata.pre is None
|
||||
assert evaluated.metadata.post is None
|
||||
assert evaluated.metadata.where == expr
|
||||
assert evaluated.get_metadata_value(ConceptParts.WHERE) == expected
|
||||
assert evaluated.props == {}
|
||||
assert evaluated.metadata.is_evaluated
|
||||
assert len(evaluated.values) == 0 if expr is None else 1
|
||||
|
||||
@pytest.mark.parametrize("expr, expected", [
|
||||
(None, None),
|
||||
("", ""),
|
||||
("1", 1),
|
||||
("1+1", 2),
|
||||
("'one'", "one"),
|
||||
("'one' + 'two'", "onetwo"),
|
||||
("True", True),
|
||||
("1 > 2", False),
|
||||
])
|
||||
def test_i_can_evaluate_a_concept_with_prop(self, expr, expected):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
concept = Concept("foo").def_prop("a", expr)
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
|
||||
assert evaluated.key == concept.key
|
||||
assert evaluated.metadata.pre is None
|
||||
assert evaluated.metadata.pre is None
|
||||
assert evaluated.metadata.post is None
|
||||
assert evaluated.metadata.where is None
|
||||
assert evaluated.props == {"a": Property("a", expected)}
|
||||
assert evaluated.metadata.is_evaluated
|
||||
|
||||
def test_i_can_evaluate_metadata_using_do_not_resolve(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept = Concept("foo")
|
||||
concept.compiled[ConceptParts.BODY] = DoNotResolve("do not resolve")
|
||||
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
|
||||
assert evaluated.body == "do not resolve"
|
||||
assert evaluated.metadata.is_evaluated
|
||||
|
||||
def test_i_can_evaluate_property_using_do_not_resolve(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept = Concept("foo").def_prop("a")
|
||||
concept.compiled["a"] = DoNotResolve("do not resolve")
|
||||
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
|
||||
assert evaluated.get_prop("a") == "do not resolve"
|
||||
assert evaluated.metadata.is_evaluated
|
||||
|
||||
def test_original_value_is_overridden_when_using_do_no_resolve(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept = Concept("foo", body="original value").set_prop("a", "original value")
|
||||
concept.compiled["a"] = DoNotResolve("do not resolve")
|
||||
concept.compiled[ConceptParts.BODY] = DoNotResolve("do not resolve")
|
||||
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
|
||||
assert evaluated.body == "do not resolve"
|
||||
assert evaluated.get_prop("a") == "do not resolve"
|
||||
assert evaluated.metadata.is_evaluated
|
||||
|
||||
def test_props_are_evaluated_before_body(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
concept = Concept("foo", body="a+1").def_prop("a", "10").init_key()
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
|
||||
assert evaluated.key == concept.key
|
||||
assert evaluated.body == 11
|
||||
|
||||
def test_i_can_evaluate_when_another_concept_is_referenced(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept_a = Concept("a")
|
||||
sheerka.add_in_cache(concept_a)
|
||||
|
||||
concept = Concept("foo", body="a").init_key()
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
|
||||
assert evaluated == simplec("foo", simplec("a", None))
|
||||
assert id(evaluated.body) != id(concept_a)
|
||||
assert evaluated.metadata.is_evaluated
|
||||
assert evaluated.body.metadata.is_evaluated
|
||||
|
||||
def test_i_can_evaluate_when_the_referenced_concept_has_a_body(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept_a = Concept("a", body="1")
|
||||
sheerka.add_in_cache(concept_a)
|
||||
|
||||
concept = Concept("foo", body="a")
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
|
||||
assert evaluated.key == concept.key
|
||||
assert evaluated.body == simplec("a", 1)
|
||||
assert not concept_a.metadata.is_evaluated
|
||||
assert evaluated.metadata.is_evaluated
|
||||
|
||||
def test_i_can_evaluate_concept_of_concept_when_the_leaf_has_a_body(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.add_in_cache(Concept(name="a", body="'a'"))
|
||||
sheerka.add_in_cache(Concept(name="b", body="a"))
|
||||
sheerka.add_in_cache(Concept(name="c", body="b"))
|
||||
concept_d = sheerka.add_in_cache(Concept(name="d", body="c"))
|
||||
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept_d)
|
||||
|
||||
assert evaluated.key == concept_d.key
|
||||
expected = simplec("c", simplec("b", simplec("a", "a")))
|
||||
assert evaluated.body == expected
|
||||
assert sheerka.value(evaluated) == 'a'
|
||||
assert evaluated.metadata.is_evaluated
|
||||
|
||||
def test_i_can_evaluate_concept_of_concept_does_not_have_a_body(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.add_in_cache(Concept(name="a"))
|
||||
sheerka.add_in_cache(Concept(name="b", body="a"))
|
||||
sheerka.add_in_cache(Concept(name="c", body="b"))
|
||||
concept_d = sheerka.add_in_cache(Concept(name="d", body="c"))
|
||||
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept_d)
|
||||
|
||||
assert evaluated.key == concept_d.key
|
||||
expected = simplec("c", simplec("b", simplec("a", None)))
|
||||
assert evaluated.body == expected
|
||||
assert sheerka.value(evaluated) == Concept(name="a").init_key()
|
||||
assert evaluated.metadata.is_evaluated
|
||||
|
||||
def test_i_can_evaluate_concept_when_properties_reference_others_concepts(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept_a = sheerka.add_in_cache(Concept(name="a").init_key())
|
||||
|
||||
concept = Concept("foo", body="a").def_prop("a", "a").init_key()
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
|
||||
# first prop a is evaluated to concept_a
|
||||
# then body is evaluated to prop a -> concept_a
|
||||
assert evaluated.key == concept.key
|
||||
assert evaluated.body == concept_a
|
||||
|
||||
def test_i_can_evaluate_concept_when_properties_reference_others_concepts_2(self):
|
||||
"""
|
||||
Same test,
|
||||
but the name of the property and the name of the concept are different
|
||||
:return:
|
||||
"""
|
||||
sheerka = self.get_sheerka()
|
||||
concept_a = sheerka.add_in_cache(Concept(name="a"))
|
||||
|
||||
concept = Concept("foo", body="concept_a").def_prop("concept_a", "a")
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
|
||||
assert evaluated.key == concept.key
|
||||
assert evaluated.body == concept_a
|
||||
|
||||
def test_i_can_evaluate_concept_when_properties_reference_others_concepts_with_body(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.add_in_cache(Concept(name="a", body="1"))
|
||||
sheerka.add_in_cache(Concept(name="b", body="2"))
|
||||
|
||||
concept = Concept("foo", body="propA + propB").def_prop("propA", "a").def_prop("propB", "b")
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
|
||||
assert evaluated.key == concept.key
|
||||
assert evaluated.body == 3
|
||||
|
||||
def test_i_can_evaluate_concept_when_properties_is_a_concept(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept_a = sheerka.add_in_cache(Concept(name="a", body="'a'").init_key())
|
||||
|
||||
concept = Concept("foo").def_prop("a")
|
||||
concept.compiled["a"] = concept_a
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
|
||||
assert evaluated.key == concept.key
|
||||
assert evaluated.get_prop("a") == simplec("a", "a")
|
||||
|
||||
def test_i_can_evaluate_when_property_asts_is_a_list(self):
|
||||
sheerka = self.get_sheerka()
|
||||
foo = Concept("foo", body="1")
|
||||
|
||||
concept = Concept("to_eval").def_prop("prop")
|
||||
concept.compiled["prop"] = [foo, DoNotResolve("1")]
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
|
||||
props = evaluated.get_prop("prop")
|
||||
assert len(props) == 2
|
||||
assert props[0] == simplec("foo", 1)
|
||||
assert props[1] == "1"
|
||||
|
||||
def test_i_can_evaluate_when_compiled_is_set_up_with_return_value(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
python_node = PythonNode("1 +1 ")
|
||||
parser_result = ParserResultConcept(parser="who", value=python_node)
|
||||
|
||||
concept = Concept("to_eval").def_prop("prop")
|
||||
concept.compiled["prop"] = [ReturnValueConcept("who", True, parser_result)]
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
assert evaluated.get_prop("prop") == 2
|
||||
|
||||
# also works when only one return value
|
||||
concept = Concept("to_eval").def_prop("prop")
|
||||
concept.compiled["prop"] = ReturnValueConcept("who", True, parser_result)
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
assert evaluated.get_prop("prop") == 2
|
||||
|
||||
def test_i_can_reference_sheerka(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
concept = Concept("foo", body="sheerka.test()").init_key()
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
|
||||
assert evaluated.key == concept.key
|
||||
assert evaluated.body == sheerka.test()
|
||||
|
||||
def test_properties_values_takes_precedence_over_the_outside_world(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.add_in_cache(Concept(name="a", body="'concept_a'"))
|
||||
sheerka.add_in_cache(Concept(name="b", body="'concept_b'"))
|
||||
|
||||
concept = Concept("foo", body="a")
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
assert evaluated.key == concept.key
|
||||
assert evaluated.body == simplec("a", "concept_a") # this test was already done
|
||||
|
||||
# so check this one.
|
||||
concept = Concept("foo", body="a").def_prop("a", "'property_a'")
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
assert evaluated.key == concept.key
|
||||
assert evaluated.body == 'property_a'
|
||||
|
||||
# or this one.
|
||||
concept = Concept("foo", body="a").def_prop("a", "b")
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
assert evaluated.key == concept.key
|
||||
assert evaluated.body == simplec(name="b", body="concept_b")
|
||||
|
||||
def test_properties_values_takes_precedence(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.add_in_cache(Concept(name="a", body="'concept_a'"))
|
||||
sheerka.add_in_cache(Concept(name="b", body="'concept_b'"))
|
||||
|
||||
concept = Concept("foo", body="a + b").def_prop("a", "'prop_a'")
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
assert evaluated.key == concept.key
|
||||
assert evaluated.body == 'prop_aconcept_b'
|
||||
|
||||
def test_i_can_reference_sub_property_of_a_property(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.add_in_cache(Concept(name="concept_a").def_prop("subProp", "'sub_a'"))
|
||||
|
||||
concept = Concept("foo", body="a.props['subProp'].value").def_prop("a", "concept_a")
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
assert evaluated == simplec(concept.key, "sub_a")
|
||||
|
||||
def test_i_cannot_evaluate_concept_if_property_is_in_error(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
concept = Concept(name="concept_a").def_prop("subProp", "undef_concept")
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
assert sheerka.isinstance(evaluated, BuiltinConcepts.CONCEPT_EVAL_ERROR)
|
||||
|
||||
def test_key_is_initialized_by_evaluation(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
concept = Concept("foo")
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka), concept)
|
||||
|
||||
assert evaluated.key == concept.init_key().key
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
import os
|
||||
import shutil
|
||||
from os import path
|
||||
|
||||
import pytest
|
||||
from core.builtin_concepts import ConceptAlreadyInSet, BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
|
||||
from tests.TestUsingFileBasedSheerka import TestUsingFileBasedSheerka
|
||||
|
||||
|
||||
class TestSheerkaSetsManager(TestUsingFileBasedSheerka):
|
||||
|
||||
|
||||
|
||||
def test_i_can_add_concept_to_set(self):
|
||||
sheerka = self.get_sheerka(False, False)
|
||||
|
||||
foo = Concept("foo")
|
||||
sheerka.set_id_if_needed(foo, False)
|
||||
|
||||
all_foos = Concept("all_foos")
|
||||
sheerka.set_id_if_needed(all_foos, False)
|
||||
|
||||
context = self.get_context(sheerka)
|
||||
res = sheerka.add_concept_to_set(context, foo, all_foos)
|
||||
|
||||
assert res.status
|
||||
assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS)
|
||||
|
||||
all_entries = self.get_sheerka(False, False).sdp.get("All_" + all_foos.id, None, False)
|
||||
assert len(all_entries) == 1
|
||||
assert foo.id in all_entries
|
||||
|
||||
def test_i_can_add_several_concepts_to_set(self):
|
||||
sheerka = self.get_sheerka(False, False)
|
||||
|
||||
foo1 = Concept("foo1")
|
||||
sheerka.set_id_if_needed(foo1, False)
|
||||
|
||||
foo2 = Concept("foo1")
|
||||
sheerka.set_id_if_needed(foo2, False)
|
||||
|
||||
all_foos = Concept("all_foos")
|
||||
sheerka.set_id_if_needed(all_foos, False)
|
||||
|
||||
context = self.get_context(sheerka)
|
||||
sheerka.add_concept_to_set(context, foo1, all_foos)
|
||||
res = sheerka.add_concept_to_set(context, foo2, all_foos)
|
||||
|
||||
assert res.status
|
||||
assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS)
|
||||
|
||||
all_entries = self.get_sheerka(False, False).sdp.get("All_" + all_foos.id, None, False)
|
||||
assert len(all_entries) == 2
|
||||
assert foo1.id in all_entries
|
||||
assert foo2.id in all_entries
|
||||
|
||||
def test_i_cannot_add_the_same_concept_twice_in_a_set(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
foo = Concept("foo")
|
||||
sheerka.set_id_if_needed(foo, False)
|
||||
|
||||
all_foos = Concept("all_foos")
|
||||
sheerka.set_id_if_needed(all_foos, False)
|
||||
|
||||
context = self.get_context(sheerka)
|
||||
sheerka.add_concept_to_set(context, foo, all_foos)
|
||||
res = sheerka.add_concept_to_set(context, foo, all_foos)
|
||||
|
||||
assert not res.status
|
||||
assert res.body == ConceptAlreadyInSet(foo, all_foos)
|
||||
|
||||
all_entries = sheerka.sdp.get("All_" + all_foos.id, None, False)
|
||||
assert len(all_entries) == 1
|
||||
assert foo.id in all_entries
|
||||
|
||||
def test_i_get_elements_from_a_set(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
one = Concept("one")
|
||||
two = Concept("two")
|
||||
three = Concept("three")
|
||||
number = Concept("number")
|
||||
|
||||
for c in [one, two, three, number]:
|
||||
sheerka.set_id_if_needed(c, False)
|
||||
sheerka.add_in_cache(c)
|
||||
|
||||
context = self.get_context(sheerka)
|
||||
for c in [one, two, three]:
|
||||
sheerka.add_concept_to_set(context, c, number)
|
||||
|
||||
elements = sheerka.get_set_elements(number)
|
||||
|
||||
assert set(elements) == {one, two, three}
|
||||
|
||||
def test_i_cannot_get_elements_if_not_a_set(self):
|
||||
sheerka = self.get_sheerka()
|
||||
one = Concept("one")
|
||||
sheerka.set_id_if_needed(one, False)
|
||||
sheerka.add_in_cache(one)
|
||||
|
||||
error = sheerka.get_set_elements(one)
|
||||
|
||||
assert sheerka.isinstance(error, BuiltinConcepts.NOT_A_SET)
|
||||
assert error.body == one
|
||||
|
||||
def test_isa_and_isa_group(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
group = Concept("group").init_key()
|
||||
group.metadata.id = "1001"
|
||||
assert not sheerka.isagroup(group)
|
||||
|
||||
foo = Concept("foo").init_key()
|
||||
foo.metadata.id = "1002"
|
||||
assert not sheerka.isa(foo, group)
|
||||
|
||||
context = self.get_context(sheerka)
|
||||
sheerka.add_concept_to_set(context, foo, group)
|
||||
assert sheerka.isagroup(group)
|
||||
assert sheerka.isa(foo, group)
|
||||
@@ -6,7 +6,7 @@ from core.ast.nodes import NodeParent, GenericNodeConcept
|
||||
import core.ast.nodes
|
||||
from core.ast.visitors import ConceptNodeVisitor, UnreferencedNamesVisitor
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.sheerka import Sheerka
|
||||
from core.sheerka.Sheerka import Sheerka
|
||||
|
||||
|
||||
def get_sheerka():
|
||||
@@ -0,0 +1,145 @@
|
||||
import ast
|
||||
import pytest
|
||||
import core.builtin_helpers
|
||||
|
||||
from core.builtin_concepts import ReturnValueConcept, BuiltinConcepts
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
class TestBuiltinHelpers(TestUsingMemoryBasedSheerka):
|
||||
|
||||
def test_i_can_use_expect_one_when_empty(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
res = core.builtin_helpers.expect_one(self.get_context(sheerka), [])
|
||||
assert not res.status
|
||||
assert sheerka.isinstance(res.value, BuiltinConcepts.IS_EMPTY)
|
||||
|
||||
def test_i_can_use_expect_one_when_too_many_success(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
items = [
|
||||
ReturnValueConcept("who", True, "value1"),
|
||||
ReturnValueConcept("who", True, "value2"),
|
||||
]
|
||||
res = core.builtin_helpers.expect_one(self.get_context(sheerka), items)
|
||||
assert not res.status
|
||||
assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_SUCCESS)
|
||||
assert res.value.body == items
|
||||
assert res.parents == items
|
||||
|
||||
def test_i_can_use_expect_one_when_same_success(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
items = [
|
||||
ReturnValueConcept("who", True, "value"),
|
||||
ReturnValueConcept("who", True, "value"),
|
||||
]
|
||||
res = core.builtin_helpers.expect_one(self.get_context(sheerka), items)
|
||||
assert res.status
|
||||
assert res.value == items[0].value
|
||||
assert res.parents == items
|
||||
|
||||
def test_i_can_use_expect_when_only_errors_1(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
items = [
|
||||
ReturnValueConcept("who", False, sheerka.new(BuiltinConcepts.ERROR)),
|
||||
]
|
||||
res = core.builtin_helpers.expect_one(self.get_context(sheerka), items)
|
||||
assert not res.status
|
||||
assert res.value, items[0]
|
||||
assert res.parents == items
|
||||
|
||||
def test_i_can_use_expect_when_only_errors_2(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
items = [
|
||||
ReturnValueConcept("who", False, None),
|
||||
ReturnValueConcept("who", False, None),
|
||||
]
|
||||
res = core.builtin_helpers.expect_one(self.get_context(sheerka), items)
|
||||
assert not res.status
|
||||
assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_ERRORS)
|
||||
assert res.value.body == items
|
||||
assert res.parents == items
|
||||
|
||||
def test_i_can_use_expect_one_when_one_success_1(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
items = [
|
||||
ReturnValueConcept("who", True, None),
|
||||
]
|
||||
res = core.builtin_helpers.expect_one(self.get_context(sheerka), items)
|
||||
assert res.status
|
||||
assert res.body == items[0].body
|
||||
assert res.parents == items
|
||||
|
||||
def test_i_can_use_expect_one_when_one_success_2(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
items = [
|
||||
ReturnValueConcept("who", False, None),
|
||||
ReturnValueConcept("who", True, None),
|
||||
ReturnValueConcept("who", False, None),
|
||||
]
|
||||
res = core.builtin_helpers.expect_one(self.get_context(sheerka), items)
|
||||
assert res.status
|
||||
assert res.body == items[1].body
|
||||
assert res.parents == items
|
||||
|
||||
def test_i_can_use_expect_one_when_not_a_list_true(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
item = ReturnValueConcept("who", True, None)
|
||||
res = core.builtin_helpers.expect_one(self.get_context(sheerka), item)
|
||||
assert res.status
|
||||
assert res == item
|
||||
|
||||
def test_i_can_use_expect_one_when_not_a_list_false(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
item = ReturnValueConcept("who", False, None)
|
||||
res = core.builtin_helpers.expect_one(self.get_context(sheerka), item)
|
||||
|
||||
assert not res.status
|
||||
assert res == item
|
||||
|
||||
@pytest.mark.parametrize("expression, vars_to_include, vars_to_exclude, expected_expr", [
|
||||
("a == 1", [], [], []),
|
||||
("a == 1", ["a"], [], ["a == 1"]),
|
||||
("a == 1", [], ["a"], []),
|
||||
("predicate(a)", [], [], []),
|
||||
("predicate(a)", ["a"], [], ["predicate(a)"]),
|
||||
("predicate(a, b)", ["a"], [], ["predicate(a, b)"]),
|
||||
("predicate(a, b)", ["b"], [], ["predicate(a, b)"]),
|
||||
("predicate(a, b)", ["a", "b"], [], ["predicate(a, b)"]),
|
||||
("predicate(a, b)", ["a"], ["b"], []),
|
||||
("a + b == 1", [], [], []),
|
||||
("a + b == 1", ["a"], [], ["a + b == 1"]),
|
||||
("a + b == 1", ["a"], ["b"], []),
|
||||
("a + b == 1", ["b"], [], ["a + b == 1"]),
|
||||
("a + b == 1", ["a", "b"], [], ["a + b == 1"]),
|
||||
("a == 1 and b == 2", [], [], []),
|
||||
("a == 1 and b == 2", ["a"], [], ["a == 1"]),
|
||||
("a == 1 and b == 2", ["b"], [], ["b == 2"]),
|
||||
("a == 1 and b == 2", ["a"], ["b"], ["a == 1"]),
|
||||
("a == 1 and b == 2", ["a", "b"], [], ["a == 1 and b == 2"]),
|
||||
("predicate(a,c) and predicate(b,c)", ["a", "b"], [], ["predicate(a,c) and predicate(b,c)"]),
|
||||
("not(a == 1)", [], [], []),
|
||||
("not(a == 1)", ["a"], [], ["not(a==1)"]),
|
||||
("a == 1 or b == 2", [], [], []),
|
||||
("a == 1 or b == 2", ["a"], [], ["a == 1"]),
|
||||
("a == 1 or b == 2", ["b"], [], ["b == 2"]),
|
||||
("a == 1 or b == 2", ["a", "b"], [], ["a == 1 or b == 2"]),
|
||||
("predicate(a,c) or predicate(b,c)", ["a", "b"], [], ["predicate(a,c) or predicate(b,c)"]),
|
||||
])
|
||||
def test_i_can_extract_predicates(self, expression, vars_to_include, vars_to_exclude, expected_expr):
|
||||
sheerka = self.get_sheerka()
|
||||
expected = [ast.parse(expr, mode="eval") for expr in expected_expr]
|
||||
|
||||
actual = core.builtin_helpers.extract_predicates(sheerka, expression, vars_to_include, vars_to_exclude)
|
||||
assert len(actual) == len(expected)
|
||||
for i in range(len(actual)):
|
||||
assert self.dump_ast(actual[i]) == self.dump_ast(expected[i])
|
||||
@@ -0,0 +1,244 @@
|
||||
import pytest
|
||||
import os
|
||||
|
||||
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept, UserInputConcept
|
||||
from core.concept import Concept, PROPERTIES_TO_SERIALIZE
|
||||
from core.sheerka.Sheerka import Sheerka
|
||||
|
||||
from tests.TestUsingFileBasedSheerka import TestUsingFileBasedSheerka
|
||||
|
||||
|
||||
class ConceptWithGetValue(Concept):
|
||||
def get_value(self):
|
||||
return self.get_prop("my_prop")
|
||||
|
||||
|
||||
class TestSheerka(TestUsingFileBasedSheerka):
|
||||
|
||||
def test_root_folder_is_created_after_initialization(self):
|
||||
return_value = Sheerka().initialize(self.root_folder)
|
||||
assert return_value.status, "initialisation should be successful"
|
||||
assert os.path.exists(self.root_folder), "init folder should be created"
|
||||
|
||||
def test_i_can_list_builtin_concepts(self):
|
||||
sheerka = self.get_sheerka()
|
||||
builtins = list(sheerka.get_builtins_classes_as_dict())
|
||||
|
||||
assert str(BuiltinConcepts.ERROR) in builtins
|
||||
assert str(BuiltinConcepts.RETURN_VALUE) in builtins
|
||||
|
||||
def test_builtin_concepts_are_initialized(self):
|
||||
sheerka = self.get_sheerka(skip_builtins_in_db=False)
|
||||
assert len(sheerka.cache_by_key) == len(BuiltinConcepts)
|
||||
for concept_name in BuiltinConcepts:
|
||||
assert str(concept_name) in sheerka.cache_by_key
|
||||
assert sheerka.sdp.get_safe(sheerka.CONCEPTS_ENTRY, str(concept_name)) is not None
|
||||
|
||||
for key, concept_class in sheerka.get_builtins_classes_as_dict().items():
|
||||
assert isinstance(sheerka.cache_by_key[key], concept_class)
|
||||
|
||||
def test_builtin_concepts_can_be_updated(self):
|
||||
sheerka = self.get_sheerka(False, False)
|
||||
loaded_sheerka = sheerka.get(BuiltinConcepts.SHEERKA)
|
||||
loaded_sheerka.metadata.desc = "I have a description"
|
||||
sheerka.sdp.modify("Test", sheerka.CONCEPTS_ENTRY, loaded_sheerka.key, loaded_sheerka)
|
||||
|
||||
sheerka = self.get_sheerka(False, False)
|
||||
loaded_sheerka = sheerka.get(BuiltinConcepts.SHEERKA)
|
||||
|
||||
assert loaded_sheerka.metadata.desc == "I have a description"
|
||||
|
||||
def test_i_can_get_a_builtin_concept_by_their_enum_or_the_string(self):
|
||||
"""
|
||||
Checks that a concept can be found its name
|
||||
even when there are variables in the name (ex 'hello + a' or 'a + b' )
|
||||
:return:
|
||||
"""
|
||||
sheerka = self.get_sheerka()
|
||||
for key in sheerka.get_builtins_classes_as_dict():
|
||||
assert sheerka.get(key) is not None
|
||||
assert sheerka.get(str(key)) is not None
|
||||
|
||||
def test_i_cannot_get_when_key_is_none(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
res = sheerka.get(None)
|
||||
assert sheerka.isinstance(res, BuiltinConcepts.ERROR)
|
||||
assert res.body == "Concept key is undefined."
|
||||
|
||||
def test_unknown_concept_is_return_when_the_concept_key_is_not_found(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
loaded = sheerka.get("key_that_does_not_exist")
|
||||
|
||||
assert loaded is not None
|
||||
assert sheerka.isinstance(loaded, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
assert loaded.body == ("key", "key_that_does_not_exist")
|
||||
assert loaded.metadata.is_evaluated
|
||||
|
||||
def test_unknown_concept_is_return_when_the_concept_id_is_not_found(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
loaded = sheerka.get_by_id("id_that_does_not_exist")
|
||||
|
||||
assert loaded is not None
|
||||
assert sheerka.isinstance(loaded, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
assert loaded.body == ("id", "id_that_does_not_exist")
|
||||
assert loaded.metadata.is_evaluated
|
||||
|
||||
def test_i_can_instantiate_a_builtin_concept_when_it_has_its_own_class(self):
|
||||
sheerka = self.get_sheerka()
|
||||
ret = sheerka.new(BuiltinConcepts.RETURN_VALUE, who="who", status="status", value="value", message="message")
|
||||
|
||||
assert isinstance(ret, ReturnValueConcept)
|
||||
assert ret.key == str(BuiltinConcepts.RETURN_VALUE)
|
||||
assert ret.who == "who"
|
||||
assert ret.status == "status"
|
||||
assert ret.value == "value"
|
||||
assert ret.message == "message"
|
||||
|
||||
def test_i_can_instantiate_a_builtin_concept_when_no_specific_class(self):
|
||||
sheerka = self.get_sheerka()
|
||||
ret = sheerka.new(BuiltinConcepts.UNKNOWN_CONCEPT, body="fake_concept")
|
||||
|
||||
assert isinstance(ret, Concept)
|
||||
assert ret.key == str(BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
assert ret.body == "fake_concept"
|
||||
|
||||
def test_i_can_instantiate_a_concept(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept = self.get_default_concept()
|
||||
sheerka.create_new_concept(self.get_context(sheerka), concept)
|
||||
|
||||
new = sheerka.new(concept.key, a=10, b="value")
|
||||
|
||||
assert sheerka.isinstance(new, concept)
|
||||
for prop in PROPERTIES_TO_SERIALIZE:
|
||||
assert getattr(new.metadata, prop) == getattr(concept.metadata, prop)
|
||||
|
||||
assert new.props["a"].value == 10
|
||||
assert new.props["b"].value == "value"
|
||||
|
||||
def test_i_can_instantiate_with_the_name_and_the_id(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.create_new_concept(self.get_context(sheerka), Concept("foo", body="foo1"))
|
||||
sheerka.create_new_concept(self.get_context(sheerka), Concept("foo", body="foo2"))
|
||||
|
||||
concepts = sheerka.new("foo")
|
||||
assert len(concepts) == 2
|
||||
|
||||
foo1 = sheerka.new(("foo", "1001"))
|
||||
assert foo1.metadata.body == "foo1"
|
||||
|
||||
foo2 = sheerka.new(("foo", "1002"))
|
||||
assert foo2.metadata.body == "foo2"
|
||||
|
||||
def test_instances_are_different_when_asking_for_new(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept = self.get_default_concept()
|
||||
sheerka.create_new_concept(self.get_context(sheerka), concept)
|
||||
|
||||
new1 = sheerka.new(concept.key, a=10, b="value")
|
||||
new2 = sheerka.new(concept.key, a=10, b="value")
|
||||
|
||||
assert new1 == new2
|
||||
assert id(new1) != id(new2)
|
||||
|
||||
def test_i_get_the_same_instance_when_is_unique_is_true(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept = Concept(name="unique", is_unique=True)
|
||||
sheerka.create_new_concept(self.get_context(sheerka), concept)
|
||||
|
||||
new1 = sheerka.new(concept.key, a=10, b="value")
|
||||
new2 = sheerka.new(concept.key, a=10, b="value")
|
||||
|
||||
assert new1 == new2
|
||||
assert id(new1) == id(new2)
|
||||
|
||||
def test_i_cannot_instantiate_an_unknown_concept(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
new = sheerka.new("fake_concept")
|
||||
|
||||
assert sheerka.isinstance(new, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
assert new.body == ('key', 'fake_concept')
|
||||
|
||||
def test_i_cannot_instantiate_with_invalid_id(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.create_new_concept(self.get_context(sheerka), Concept("foo", body="foo1"))
|
||||
sheerka.create_new_concept(self.get_context(sheerka), Concept("foo", body="foo2"))
|
||||
|
||||
new = sheerka.new(("foo", "invalid_id"))
|
||||
|
||||
assert sheerka.isinstance(new, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
assert new.body == [('key', 'foo'), ('id', 'invalid_id')]
|
||||
|
||||
def test_i_cannot_instantiate_with_invalid_key(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.create_new_concept(self.get_context(sheerka), Concept("foo", body="foo1"))
|
||||
sheerka.create_new_concept(self.get_context(sheerka), Concept("foo", body="foo2"))
|
||||
|
||||
new = sheerka.new(("invalid_key", "1001"))
|
||||
|
||||
assert sheerka.isinstance(new, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
assert new.body == [('key', 'invalid_key'), ('id', '1001')]
|
||||
|
||||
def test_concept_id_is_irrelevant_when_only_one_concept(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.create_new_concept(self.get_context(sheerka), Concept("foo", body="foo1"))
|
||||
|
||||
new = sheerka.new(("foo", "invalid_id"))
|
||||
|
||||
assert sheerka.isinstance(new, "foo")
|
||||
assert new.metadata.body == "foo1"
|
||||
|
||||
def test_i_cannot_instantiate_when_properties_are_not_recognized(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept = self.get_default_concept()
|
||||
sheerka.create_new_concept(self.get_context(sheerka), concept)
|
||||
|
||||
new = sheerka.new(concept.key, a=10, c="value")
|
||||
|
||||
assert sheerka.isinstance(new, BuiltinConcepts.UNKNOWN_PROPERTY)
|
||||
assert new.property_name == "c"
|
||||
assert sheerka.isinstance(new.concept, concept)
|
||||
|
||||
@pytest.mark.parametrize("concept, reduce_simple_list, expected", [
|
||||
(None, False, None),
|
||||
(3.14, False, 3.14),
|
||||
("foo", False, "foo"),
|
||||
(True, False, True),
|
||||
(Concept("name", body="foo"), False, "foo"),
|
||||
(Concept("name"), False, Concept("name")),
|
||||
(ConceptWithGetValue("name").set_prop("my_prop", "my_value"), False, "my_value"),
|
||||
(ReturnValueConcept(value="return_value"), False, "return_value"),
|
||||
(ReturnValueConcept(value=Concept(key=BuiltinConcepts.USER_INPUT, body="text"), status=True), False, "text"),
|
||||
(ReturnValueConcept(value=UserInputConcept("text"), status=True), False, "text"),
|
||||
(Concept("name", body=["foo", "bar"]), False, ["foo", "bar"]),
|
||||
(Concept("name", body=["foo"]), True, "foo"),
|
||||
(Concept("name", body=Concept("foo")), False, Concept("foo")),
|
||||
(Concept("name", body=Concept("foo", body="value")), False, "value"),
|
||||
(Concept("name", body=Concept("foo", body=Concept("bar", body="value"))), False, "value"),
|
||||
(Concept("name", body=Concept("foo", body=ReturnValueConcept(value="return_value"))), False, "return_value"),
|
||||
])
|
||||
def test_i_can_get_value(self, concept, reduce_simple_list, expected):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
# I use auto_init() instead of evaluate_concept() to be quicker
|
||||
c = concept
|
||||
while isinstance(c, Concept):
|
||||
c.auto_init()
|
||||
c = c.body
|
||||
|
||||
assert sheerka.value(concept, reduce_simple_list) == expected
|
||||
|
||||
def test_list_of_concept_is_sorted_by_id(self):
|
||||
sheerka = self.get_sheerka(False, False)
|
||||
concepts = sheerka.concepts()
|
||||
|
||||
assert concepts[0].id < concepts[-1].id
|
||||
|
||||
def test_builtin_error_concept_are_errors(self):
|
||||
# only test a random one, it will be the same for the others
|
||||
sheerka = self.get_sheerka()
|
||||
assert not sheerka.is_success(sheerka.new(BuiltinConcepts.TOO_MANY_SUCCESS))
|
||||
@@ -0,0 +1,375 @@
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from evaluators.BaseEvaluator import OneReturnValueEvaluator, BaseEvaluator, AllReturnValuesEvaluator
|
||||
|
||||
from tests.BaseTest import BaseTest
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
class Out:
|
||||
debug_out = []
|
||||
|
||||
def out(self, method, name, context, return_value):
|
||||
name = name[len(BaseEvaluator.PREFIX):]
|
||||
if isinstance(return_value, list):
|
||||
target = [str(r.body.key) for r in return_value]
|
||||
else:
|
||||
target = str(return_value.body.key)
|
||||
step = str(context.step)
|
||||
text = f"{step} [{context.iteration}] "
|
||||
text += f"{name} - {method} - target={target}"
|
||||
self.debug_out.append(text)
|
||||
|
||||
def out_all(self, method, name, context, return_values):
|
||||
name = name[len(BaseEvaluator.PREFIX):]
|
||||
target = [str(r.body.key) for r in return_values]
|
||||
step = str(context.step)
|
||||
text = f"{step} [{context.iteration}] "
|
||||
text += f"{name} - {method} - target={target}"
|
||||
self.debug_out.append(text)
|
||||
|
||||
|
||||
class OneReturnValueEvaluatorForTestingPurpose(OneReturnValueEvaluator, Out):
|
||||
def __init__(self, name, steps, priority):
|
||||
super().__init__(name, steps, priority)
|
||||
|
||||
def matches(self, context, return_value):
|
||||
self.out("matches", self.name, context, return_value)
|
||||
return True
|
||||
|
||||
def eval(self, context, return_value):
|
||||
self.out("eval", self.name, context, return_value)
|
||||
|
||||
|
||||
class AllReturnValueEvaluatorForTestingPurpose(AllReturnValuesEvaluator, Out):
|
||||
def __init__(self, name, steps, priority):
|
||||
super().__init__(name, steps, priority)
|
||||
|
||||
def matches(self, context, return_values):
|
||||
self.out("matches", self.name, context, return_values)
|
||||
return True
|
||||
|
||||
def eval(self, context, return_values):
|
||||
self.out("eval", self.name, context, return_values)
|
||||
|
||||
|
||||
class EvaluatorOneWithPriority(OneReturnValueEvaluatorForTestingPurpose):
|
||||
def __init__(self, name, priority):
|
||||
super().__init__(name, [BuiltinConcepts.EVALUATION], priority)
|
||||
|
||||
|
||||
class EvaluatorOneWithPriority10(EvaluatorOneWithPriority):
|
||||
def __init__(self):
|
||||
super().__init__("priority10", 10)
|
||||
|
||||
|
||||
class EvaluatorOneWithPriority15(EvaluatorOneWithPriority):
|
||||
def __init__(self):
|
||||
super().__init__("priority15", 15)
|
||||
|
||||
|
||||
class EvaluatorOneWithPriority20(EvaluatorOneWithPriority):
|
||||
def __init__(self):
|
||||
super().__init__("priority20", 20)
|
||||
|
||||
|
||||
class EvaluatorAllWithPriority(AllReturnValueEvaluatorForTestingPurpose):
|
||||
def __init__(self, name, priority):
|
||||
super().__init__(name, [BuiltinConcepts.EVALUATION], priority)
|
||||
|
||||
|
||||
class EvaluatorAllWithPriority10(EvaluatorAllWithPriority):
|
||||
def __init__(self):
|
||||
super().__init__("all_priority10", 10)
|
||||
|
||||
|
||||
class EvaluatorAllWithPriority15(EvaluatorAllWithPriority):
|
||||
def __init__(self):
|
||||
super().__init__("all_priority15", 15)
|
||||
|
||||
|
||||
class EvaluatorAllWithPriority20(EvaluatorAllWithPriority):
|
||||
def __init__(self):
|
||||
super().__init__("all_priority20", 20)
|
||||
|
||||
|
||||
class EvaluatorOneModifyFoo(EvaluatorOneWithPriority):
|
||||
def __init__(self):
|
||||
super().__init__("modifyFoo", 10)
|
||||
|
||||
def matches(self, context, return_value):
|
||||
super().matches(context, return_value)
|
||||
return context.sheerka.isinstance(return_value.body, "foo")
|
||||
|
||||
def eval(self, context, return_value):
|
||||
super().eval(context, return_value)
|
||||
return BaseTest.tretval(context.sheerka, Concept("bar"))
|
||||
|
||||
|
||||
class EvaluatorOneModifyBar(EvaluatorOneWithPriority):
|
||||
def __init__(self):
|
||||
super().__init__("modifyBar", 10)
|
||||
|
||||
def matches(self, context, return_value):
|
||||
super().matches(context, return_value)
|
||||
return context.sheerka.isinstance(return_value.body, "bar")
|
||||
|
||||
def eval(self, context, return_value):
|
||||
super().eval(context, return_value)
|
||||
return BaseTest.tretval(context.sheerka, Concept("baz"))
|
||||
|
||||
|
||||
class EvaluatorOnePreEvaluation(OneReturnValueEvaluatorForTestingPurpose):
|
||||
def __init__(self):
|
||||
super().__init__("preEval", [BuiltinConcepts.BEFORE_EVALUATION], 10)
|
||||
|
||||
|
||||
class EvaluatorOneMultiSteps(OneReturnValueEvaluatorForTestingPurpose):
|
||||
def __init__(self):
|
||||
super().__init__("multiStep", [BuiltinConcepts.EVALUATION, BuiltinConcepts.BEFORE_EVALUATION], 10)
|
||||
|
||||
|
||||
class EvaluatorAllReduceFooBar(EvaluatorAllWithPriority):
|
||||
def __init__(self):
|
||||
super().__init__("all_reduce_foobar", 10)
|
||||
|
||||
def matches(self, context, return_values):
|
||||
super().matches(context, return_values)
|
||||
keys = [c.body.key for c in return_values]
|
||||
return "foo" in keys and "bar" in keys
|
||||
|
||||
def eval(self, context, return_values):
|
||||
super().eval(context, return_values)
|
||||
ret = BaseTest.tretval(context.sheerka, context.sheerka.new(BuiltinConcepts.SUCCESS))
|
||||
ret.parents = return_values
|
||||
return ret
|
||||
|
||||
|
||||
class EvaluatorAllSuppressFooEntry(EvaluatorAllWithPriority):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("suppress", 100)
|
||||
|
||||
def matches(self, context, return_values):
|
||||
super().matches(context, return_values)
|
||||
return True
|
||||
|
||||
def eval(self, context, return_values):
|
||||
super().eval(context, return_values)
|
||||
foo = None
|
||||
for ret in return_values:
|
||||
if ret.body.name == "foo":
|
||||
foo = ret
|
||||
|
||||
if foo:
|
||||
return context.sheerka.ret(self.name, False, Concept("does not matter"), parents=[foo])
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class TestSheerkaExecuteEvaluators(TestUsingMemoryBasedSheerka):
|
||||
|
||||
def test_that_return_values_is_unchanged_when_no_evaluator(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.evaluators = []
|
||||
|
||||
entries = self.tretval(sheerka, Concept("foo"))
|
||||
return_values = sheerka.execute(self.get_context(sheerka), entries, [BuiltinConcepts.EVALUATION])
|
||||
|
||||
assert return_values == [entries]
|
||||
|
||||
def test_i_can_use_a_list_as_input(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.evaluators = []
|
||||
|
||||
entries = [self.tretval(sheerka, Concept("foo"))]
|
||||
return_values = sheerka.execute(self.get_context(sheerka), entries, [BuiltinConcepts.EVALUATION])
|
||||
|
||||
assert return_values == entries
|
||||
|
||||
def test_step_concept_is_removed_after_processing_if_not_reduced(self):
|
||||
"""
|
||||
No evaluator
|
||||
"""
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.evaluators = []
|
||||
|
||||
entry = self.tretval(sheerka, Concept("foo"))
|
||||
return_values = sheerka.execute(self.get_context(sheerka), entry, [BuiltinConcepts.EVALUATION])
|
||||
|
||||
assert BuiltinConcepts.EVALUATION not in [r.body.key for r in return_values]
|
||||
|
||||
def test_step_concept_is_removed_after_processing_if_not_reduced_2(self):
|
||||
"""
|
||||
This time the entry is modified by an evaluator,
|
||||
nevertheless, step concept is removed
|
||||
"""
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.evaluators = [EvaluatorOneModifyFoo]
|
||||
|
||||
entry = self.tretval(sheerka, Concept("foo"))
|
||||
return_values = sheerka.execute(self.get_context(sheerka), entry, [BuiltinConcepts.EVALUATION])
|
||||
|
||||
assert BuiltinConcepts.EVALUATION not in [r.body.key for r in return_values]
|
||||
|
||||
def test_that_higher_priority_evaluators_are_evaluated_first(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.evaluators = [
|
||||
EvaluatorAllWithPriority10,
|
||||
EvaluatorOneWithPriority20,
|
||||
EvaluatorAllWithPriority15,
|
||||
EvaluatorOneWithPriority10,
|
||||
EvaluatorOneWithPriority15,
|
||||
EvaluatorAllWithPriority20, ]
|
||||
|
||||
entries = [self.tretval(sheerka, Concept("foo"))]
|
||||
Out.debug_out = []
|
||||
sheerka.execute(self.get_context(sheerka), entries, [BuiltinConcepts.EVALUATION])
|
||||
|
||||
assert Out.debug_out == [
|
||||
'__EVALUATION [0] priority20 - matches - target=foo',
|
||||
'__EVALUATION [0] priority20 - eval - target=foo',
|
||||
'__EVALUATION [0] priority20 - matches - target=__EVALUATION',
|
||||
'__EVALUATION [0] priority20 - eval - target=__EVALUATION',
|
||||
"__EVALUATION [0] all_priority20 - matches - target=['foo', '__EVALUATION']",
|
||||
"__EVALUATION [0] all_priority20 - eval - target=['foo', '__EVALUATION']",
|
||||
"__EVALUATION [0] all_priority15 - matches - target=['foo', '__EVALUATION']",
|
||||
"__EVALUATION [0] all_priority15 - eval - target=['foo', '__EVALUATION']",
|
||||
'__EVALUATION [0] priority15 - matches - target=foo',
|
||||
'__EVALUATION [0] priority15 - eval - target=foo',
|
||||
'__EVALUATION [0] priority15 - matches - target=__EVALUATION',
|
||||
'__EVALUATION [0] priority15 - eval - target=__EVALUATION',
|
||||
"__EVALUATION [0] all_priority10 - matches - target=['foo', '__EVALUATION']",
|
||||
"__EVALUATION [0] all_priority10 - eval - target=['foo', '__EVALUATION']",
|
||||
'__EVALUATION [0] priority10 - matches - target=foo',
|
||||
'__EVALUATION [0] priority10 - eval - target=foo',
|
||||
'__EVALUATION [0] priority10 - matches - target=__EVALUATION',
|
||||
'__EVALUATION [0] priority10 - eval - target=__EVALUATION'
|
||||
]
|
||||
|
||||
def test_that_predicate_is_checked_before_evaluation_for_one_return(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.evaluators = [EvaluatorOneModifyFoo]
|
||||
|
||||
entries = [self.tretval(sheerka, Concept("foo")), self.tretval(sheerka, Concept("baz"))]
|
||||
Out.debug_out = []
|
||||
sheerka.execute(self.get_context(sheerka), entries, [BuiltinConcepts.EVALUATION])
|
||||
|
||||
assert Out.debug_out == [
|
||||
'__EVALUATION [0] modifyFoo - matches - target=foo',
|
||||
'__EVALUATION [0] modifyFoo - eval - target=foo',
|
||||
'__EVALUATION [0] modifyFoo - matches - target=baz',
|
||||
'__EVALUATION [0] modifyFoo - matches - target=__EVALUATION',
|
||||
'__EVALUATION [1] modifyFoo - matches - target=bar',
|
||||
'__EVALUATION [1] modifyFoo - matches - target=baz',
|
||||
'__EVALUATION [1] modifyFoo - matches - target=__EVALUATION'
|
||||
]
|
||||
|
||||
def test_that_predicate_is_checked_before_evaluation_for_all_return(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.evaluators = [EvaluatorAllReduceFooBar]
|
||||
|
||||
entries = [self.tretval(sheerka, Concept("foo")), self.tretval(sheerka, Concept("bar"))]
|
||||
Out.debug_out = []
|
||||
sheerka.execute(self.get_context(sheerka), entries, [BuiltinConcepts.EVALUATION])
|
||||
assert Out.debug_out == [
|
||||
"__EVALUATION [0] all_reduce_foobar - matches - target=['foo', 'bar', '__EVALUATION']",
|
||||
"__EVALUATION [0] all_reduce_foobar - eval - target=['foo', 'bar', '__EVALUATION']",
|
||||
"__EVALUATION [1] all_reduce_foobar - matches - target=['__SUCCESS']"
|
||||
]
|
||||
|
||||
entries = [self.tretval(sheerka, Concept("foo"))]
|
||||
Out.debug_out = []
|
||||
sheerka.execute(self.get_context(sheerka), entries, [BuiltinConcepts.EVALUATION])
|
||||
assert Out.debug_out == [
|
||||
"__EVALUATION [0] all_reduce_foobar - matches - target=['foo', '__EVALUATION']"
|
||||
]
|
||||
|
||||
def test_evaluation_continue_until_no_more_modification(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.evaluators = [EvaluatorOneModifyFoo, EvaluatorOneModifyBar]
|
||||
|
||||
entries = [self.tretval(sheerka, Concept("foo")), self.tretval(sheerka, Concept("baz"))]
|
||||
Out.debug_out = []
|
||||
sheerka.execute(self.get_context(sheerka), entries, [BuiltinConcepts.EVALUATION])
|
||||
|
||||
assert Out.debug_out == [
|
||||
'__EVALUATION [0] modifyFoo - matches - target=foo',
|
||||
'__EVALUATION [0] modifyFoo - eval - target=foo',
|
||||
'__EVALUATION [0] modifyFoo - matches - target=baz',
|
||||
'__EVALUATION [0] modifyFoo - matches - target=__EVALUATION',
|
||||
'__EVALUATION [0] modifyBar - matches - target=foo',
|
||||
'__EVALUATION [0] modifyBar - matches - target=baz',
|
||||
'__EVALUATION [0] modifyBar - matches - target=__EVALUATION',
|
||||
'__EVALUATION [1] modifyFoo - matches - target=bar',
|
||||
'__EVALUATION [1] modifyFoo - matches - target=baz',
|
||||
'__EVALUATION [1] modifyFoo - matches - target=__EVALUATION',
|
||||
'__EVALUATION [1] modifyBar - matches - target=bar',
|
||||
'__EVALUATION [1] modifyBar - eval - target=bar',
|
||||
'__EVALUATION [1] modifyBar - matches - target=baz',
|
||||
'__EVALUATION [1] modifyBar - matches - target=__EVALUATION',
|
||||
'__EVALUATION [2] modifyFoo - matches - target=baz',
|
||||
'__EVALUATION [2] modifyFoo - matches - target=baz',
|
||||
'__EVALUATION [2] modifyFoo - matches - target=__EVALUATION',
|
||||
'__EVALUATION [2] modifyBar - matches - target=baz',
|
||||
'__EVALUATION [2] modifyBar - matches - target=baz',
|
||||
'__EVALUATION [2] modifyBar - matches - target=__EVALUATION'
|
||||
]
|
||||
|
||||
def test_evaluation_steps_are_respected(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.evaluators = [EvaluatorOneWithPriority10, EvaluatorOnePreEvaluation]
|
||||
|
||||
entries = [self.tretval(sheerka, Concept("foo"))]
|
||||
Out.debug_out = []
|
||||
sheerka.execute(self.get_context(sheerka), entries, [BuiltinConcepts.BEFORE_EVALUATION])
|
||||
|
||||
assert Out.debug_out == [
|
||||
'__BEFORE_EVALUATION [0] preEval - matches - target=foo',
|
||||
'__BEFORE_EVALUATION [0] preEval - eval - target=foo',
|
||||
'__BEFORE_EVALUATION [0] preEval - matches - target=__BEFORE_EVALUATION',
|
||||
'__BEFORE_EVALUATION [0] preEval - eval - target=__BEFORE_EVALUATION']
|
||||
|
||||
def test_evaluation_multi_steps_are_respected(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.evaluators = [EvaluatorOneMultiSteps]
|
||||
|
||||
entries = [self.tretval(sheerka, Concept("foo"))]
|
||||
Out.debug_out = []
|
||||
sheerka.execute(self.get_context(sheerka), entries,
|
||||
[BuiltinConcepts.BEFORE_EVALUATION, BuiltinConcepts.EVALUATION])
|
||||
|
||||
assert Out.debug_out == [
|
||||
'__BEFORE_EVALUATION [0] multiStep - matches - target=foo',
|
||||
'__BEFORE_EVALUATION [0] multiStep - eval - target=foo',
|
||||
'__BEFORE_EVALUATION [0] multiStep - matches - target=__BEFORE_EVALUATION',
|
||||
'__BEFORE_EVALUATION [0] multiStep - eval - target=__BEFORE_EVALUATION',
|
||||
'__EVALUATION [0] multiStep - matches - target=foo',
|
||||
'__EVALUATION [0] multiStep - eval - target=foo',
|
||||
'__EVALUATION [0] multiStep - matches - target=__EVALUATION',
|
||||
'__EVALUATION [0] multiStep - eval - target=__EVALUATION'
|
||||
]
|
||||
|
||||
def test_evaluators_can_be_pre_processed(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.evaluators = [EvaluatorOneModifyFoo]
|
||||
|
||||
entries = [self.tretval(sheerka, Concept("foo"))]
|
||||
|
||||
# disable evaluator
|
||||
context = self.get_context(sheerka)
|
||||
context.add_preprocess(EvaluatorOneModifyFoo().name, enabled=False) # disabled for this exec context
|
||||
Out.debug_out = []
|
||||
sheerka.execute(context, entries, [BuiltinConcepts.EVALUATION])
|
||||
assert Out.debug_out == []
|
||||
|
||||
# other contextes are not impacted
|
||||
Out.debug_out = []
|
||||
sheerka.execute(self.get_context(sheerka), entries, [BuiltinConcepts.EVALUATION])
|
||||
assert Out.debug_out == [
|
||||
'__EVALUATION [0] modifyFoo - matches - target=foo',
|
||||
'__EVALUATION [0] modifyFoo - eval - target=foo',
|
||||
'__EVALUATION [0] modifyFoo - matches - target=__EVALUATION',
|
||||
'__EVALUATION [1] modifyFoo - matches - target=bar',
|
||||
'__EVALUATION [1] modifyFoo - matches - target=__EVALUATION'
|
||||
]
|
||||
@@ -0,0 +1,363 @@
|
||||
from core.builtin_concepts import ReturnValueConcept, UserInputConcept, BuiltinConcepts, ParserResultConcept
|
||||
from parsers.BaseParser import BaseParser
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
def get_ret_val(text, who="who"):
|
||||
return ReturnValueConcept(who, True, UserInputConcept(text, "user_name"))
|
||||
|
||||
|
||||
class BaseTestParser(BaseParser):
|
||||
debug_out = []
|
||||
|
||||
def __init__(self, name, priority, status=None, parser_result=None):
|
||||
super().__init__(name, priority)
|
||||
self.status = status
|
||||
self.parser_result = parser_result
|
||||
|
||||
@staticmethod
|
||||
def _get_name(name):
|
||||
return name[8:] if name.startswith("parsers.") else name
|
||||
|
||||
@staticmethod
|
||||
def _get_source(text_):
|
||||
return text_ if isinstance(text_, str) else text_.body
|
||||
|
||||
def _out(self, name, priority, status, source):
|
||||
debug = f"name={name}"
|
||||
debug += f", priority={priority}"
|
||||
debug += f", status={status}"
|
||||
debug += f", source={source}"
|
||||
self.debug_out.append(debug)
|
||||
|
||||
def parse(self, context, text):
|
||||
self._out(self._get_name(self.name), self.priority, self.status, self._get_source(text))
|
||||
value = self._get_name(self.name) + ":" + (text if isinstance(text, str) else text.body)
|
||||
parser_result = ParserResultConcept(parser=self, value=value)
|
||||
return ReturnValueConcept(self, self.status, self.parser_result or parser_result)
|
||||
|
||||
|
||||
class Enabled90FalseParser(BaseTestParser):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__("Enabled90False", 90, False)
|
||||
|
||||
|
||||
class Enabled80FalseParser(BaseTestParser):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__("Enabled80False", 80, False)
|
||||
|
||||
|
||||
class Enabled80MultipleFalseParser(BaseTestParser):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__("Enabled80MultipleFalse", 80, False)
|
||||
|
||||
def parse(self, context, text):
|
||||
self._out(self._get_name(self.name), self.priority, self.status, self._get_source(text))
|
||||
value1 = self._get_name(self.name) + ":" + (text if isinstance(text, str) else text.body) + "_1"
|
||||
value2 = self._get_name(self.name) + ":" + (text if isinstance(text, str) else text.body) + "_2"
|
||||
return [
|
||||
ReturnValueConcept(self, self.status, ParserResultConcept(parser=self, value=value1)),
|
||||
ReturnValueConcept(self, self.status, ParserResultConcept(parser=self, value=value2)),
|
||||
]
|
||||
|
||||
|
||||
class Enabled80MultipleTrueParser(BaseTestParser):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__("Enabled80MultipleTrue", 80)
|
||||
|
||||
def parse(self, context, text):
|
||||
self._out(self._get_name(self.name), self.priority, self.status, self._get_source(text))
|
||||
value1 = self._get_name(self.name) + ":" + (text if isinstance(text, str) else text.body) + "_1"
|
||||
value2 = self._get_name(self.name) + ":" + (text if isinstance(text, str) else text.body) + "_2"
|
||||
return [
|
||||
ReturnValueConcept(self, True, ParserResultConcept(parser=self, value=value1)),
|
||||
ReturnValueConcept(self, False, ParserResultConcept(parser=self, value=value2)),
|
||||
]
|
||||
|
||||
|
||||
class Enabled70FalseParser(BaseTestParser):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__("Enabled70False", 70, False, "Not a ParserResult")
|
||||
|
||||
|
||||
class Enabled50TrueParser(BaseTestParser):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__("Enabled50True", 50, True)
|
||||
|
||||
def parse(self, context, text):
|
||||
source = self._get_source(text)
|
||||
status = isinstance(text, ParserResultConcept) and source == "Enabled80False:Enabled90False:hello world"
|
||||
self._out(self._get_name(self.name), self.priority, status, source)
|
||||
|
||||
value = self._get_name(self.name) + ":" + (text if isinstance(text, str) else text.body)
|
||||
return_value = ParserResultConcept(parser=self, value=value)
|
||||
return ReturnValueConcept(self, status, return_value)
|
||||
|
||||
|
||||
class Enabled50bisTrueParser(BaseTestParser):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__("Enabled50BisTrue", 50, True)
|
||||
|
||||
|
||||
class Enabled50FalseParser(BaseTestParser):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__("Enabled50False", 50, False)
|
||||
|
||||
|
||||
class Enabled10TrueParser(BaseTestParser):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__("Enabled10True", 10, True)
|
||||
|
||||
|
||||
class DisabledParser(BaseTestParser):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__("Disabled", 90, True)
|
||||
self.enabled = False
|
||||
|
||||
|
||||
class NoneParser(BaseTestParser):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__("None", 90, True, None)
|
||||
|
||||
def parse(self, context, text):
|
||||
self._out(self._get_name(self.name), self.priority, self.status, self._get_source(text))
|
||||
return None
|
||||
|
||||
|
||||
class ListOfNoneParser(BaseTestParser):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__("ListOfNone", 90, True, None)
|
||||
|
||||
def parse(self, context, text):
|
||||
self._out(self._get_name(self.name), self.priority, self.status, self._get_source(text))
|
||||
return [None, None]
|
||||
|
||||
|
||||
class TestSheerkaExecuteParsers(TestUsingMemoryBasedSheerka):
|
||||
|
||||
def test_disabled_parsers_are_not_executed(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.parsers = {
|
||||
"Enabled": Enabled10TrueParser,
|
||||
"Disabled": DisabledParser
|
||||
}
|
||||
|
||||
user_input = [get_ret_val("hello world")]
|
||||
BaseTestParser.debug_out = []
|
||||
sheerka.execute(self.get_context(sheerka), user_input, [BuiltinConcepts.PARSING])
|
||||
|
||||
assert BaseTestParser.debug_out == ['name=Enabled10True, priority=10, status=True, source=hello world']
|
||||
|
||||
def test_parser_are_executed_by_priority(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.parsers = {
|
||||
"Enabled90False": Enabled90FalseParser,
|
||||
"Enabled80False": Enabled80FalseParser,
|
||||
"Enabled50True": Enabled50TrueParser,
|
||||
}
|
||||
|
||||
user_input = [get_ret_val("hello world")]
|
||||
BaseTestParser.debug_out = []
|
||||
sheerka.execute(self.get_context(sheerka), user_input, [BuiltinConcepts.PARSING])
|
||||
|
||||
assert BaseTestParser.debug_out == [
|
||||
'name=Enabled90False, priority=90, status=False, source=hello world',
|
||||
'name=Enabled80False, priority=80, status=False, source=hello world',
|
||||
'name=Enabled80False, priority=80, status=False, source=Enabled90False:hello world',
|
||||
'name=Enabled50True, priority=50, status=False, source=hello world',
|
||||
'name=Enabled50True, priority=50, status=False, source=Enabled90False:hello world',
|
||||
'name=Enabled50True, priority=50, status=False, source=Enabled80False:hello world',
|
||||
'name=Enabled50True, priority=50, status=True, source=Enabled80False:Enabled90False:hello world',
|
||||
]
|
||||
|
||||
def test_parsing_stop_at_the_first_success(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.parsers = {
|
||||
"Enabled80False": Enabled80FalseParser,
|
||||
"Enabled50bisTrue": Enabled50bisTrueParser,
|
||||
"Enabled10True": Enabled10TrueParser,
|
||||
}
|
||||
|
||||
user_input = [get_ret_val("hello world")]
|
||||
BaseTestParser.debug_out = []
|
||||
sheerka.execute(self.get_context(sheerka), user_input, [BuiltinConcepts.PARSING])
|
||||
|
||||
assert BaseTestParser.debug_out == [
|
||||
'name=Enabled80False, priority=80, status=False, source=hello world',
|
||||
'name=Enabled50BisTrue, priority=50, status=True, source=hello world',
|
||||
]
|
||||
|
||||
def test_parsing_stop_at_the_first_success_2(self):
|
||||
"""
|
||||
Same test than before, but Enabled50True takes more time to find a match
|
||||
:return:
|
||||
"""
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.parsers = {
|
||||
"Enabled90False": Enabled90FalseParser,
|
||||
"Enabled80False": Enabled80FalseParser,
|
||||
"Enabled50True": Enabled50TrueParser,
|
||||
"Enabled10True": Enabled10TrueParser,
|
||||
}
|
||||
|
||||
user_input = [get_ret_val("hello world")]
|
||||
BaseTestParser.debug_out = []
|
||||
sheerka.execute(self.get_context(sheerka), user_input, [BuiltinConcepts.PARSING])
|
||||
|
||||
assert BaseTestParser.debug_out == [
|
||||
'name=Enabled90False, priority=90, status=False, source=hello world',
|
||||
'name=Enabled80False, priority=80, status=False, source=hello world',
|
||||
'name=Enabled80False, priority=80, status=False, source=Enabled90False:hello world',
|
||||
'name=Enabled50True, priority=50, status=False, source=hello world',
|
||||
'name=Enabled50True, priority=50, status=False, source=Enabled90False:hello world',
|
||||
'name=Enabled50True, priority=50, status=False, source=Enabled80False:hello world',
|
||||
'name=Enabled50True, priority=50, status=True, source=Enabled80False:Enabled90False:hello world',
|
||||
]
|
||||
|
||||
def test_all_parsers_of_a_given_priority_are_executed(self):
|
||||
"""
|
||||
Make sure that all parsers with priority 50 are executed
|
||||
:return:
|
||||
"""
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.parsers = {
|
||||
"Enabled90False": Enabled90FalseParser,
|
||||
"Enabled80False": Enabled80FalseParser,
|
||||
"Enabled50True": Enabled50TrueParser,
|
||||
"Enabled50bisTrue": Enabled50bisTrueParser,
|
||||
"Enabled50False": Enabled50FalseParser,
|
||||
"Enabled10True": Enabled10TrueParser,
|
||||
}
|
||||
|
||||
user_input = [get_ret_val("hello world")]
|
||||
BaseTestParser.debug_out = []
|
||||
sheerka.execute(self.get_context(sheerka), user_input, [BuiltinConcepts.PARSING])
|
||||
|
||||
assert BaseTestParser.debug_out == [
|
||||
'name=Enabled90False, priority=90, status=False, source=hello world',
|
||||
'name=Enabled80False, priority=80, status=False, source=hello world',
|
||||
'name=Enabled80False, priority=80, status=False, source=Enabled90False:hello world',
|
||||
'name=Enabled50True, priority=50, status=False, source=hello world',
|
||||
'name=Enabled50True, priority=50, status=False, source=Enabled90False:hello world',
|
||||
'name=Enabled50True, priority=50, status=False, source=Enabled80False:hello world',
|
||||
'name=Enabled50True, priority=50, status=True, source=Enabled80False:Enabled90False:hello world',
|
||||
'name=Enabled50BisTrue, priority=50, status=True, source=hello world',
|
||||
'name=Enabled50False, priority=50, status=False, source=hello world',
|
||||
'name=Enabled50False, priority=50, status=False, source=Enabled90False:hello world',
|
||||
'name=Enabled50False, priority=50, status=False, source=Enabled80False:hello world',
|
||||
'name=Enabled50False, priority=50, status=False, source=Enabled80False:Enabled90False:hello world',
|
||||
]
|
||||
|
||||
def test_a_parser_has_access_to_the_output_of_its_predecessors(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.parsers = {
|
||||
"Enabled90False": Enabled90FalseParser,
|
||||
"Enabled80False": Enabled80FalseParser,
|
||||
"Enabled50True": Enabled50TrueParser,
|
||||
}
|
||||
|
||||
user_input = [get_ret_val("hello world")]
|
||||
|
||||
res = sheerka.execute(self.get_context(sheerka), user_input, [BuiltinConcepts.PARSING])
|
||||
res_as_tuple = [(str(r.who)[8:], r.status, r.body.body) for r in res]
|
||||
assert res_as_tuple == [
|
||||
('Enabled90False', False, 'Enabled90False:hello world'),
|
||||
('Enabled80False', False, 'Enabled80False:hello world'),
|
||||
('Enabled80False', False, 'Enabled80False:Enabled90False:hello world'),
|
||||
('Enabled50True', False, 'Enabled50True:hello world'),
|
||||
('Enabled50True', False, 'Enabled50True:Enabled90False:hello world'),
|
||||
('Enabled50True', False, 'Enabled50True:Enabled80False:hello world'),
|
||||
('Enabled50True', True, 'Enabled50True:Enabled80False:Enabled90False:hello world'),
|
||||
]
|
||||
|
||||
def test_none_return_values_are_discarded(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.parsers = {
|
||||
"NoneParser": NoneParser,
|
||||
"ListOfNone": ListOfNoneParser,
|
||||
}
|
||||
|
||||
user_input = [get_ret_val("hello world")]
|
||||
|
||||
BaseTestParser.debug_out = []
|
||||
res = sheerka.execute(self.get_context(sheerka), user_input, [BuiltinConcepts.PARSING])
|
||||
assert res == []
|
||||
assert BaseTestParser.debug_out == [
|
||||
'name=None, priority=90, status=True, source=hello world',
|
||||
'name=ListOfNone, priority=90, status=True, source=hello world'
|
||||
]
|
||||
|
||||
def test_following_priorities_can_only_see_parser_result_return_values(self):
|
||||
"""
|
||||
Normally, lower priority parsers can see the result of the higher priority parsers
|
||||
This is true only if the higher priority parser return a ParserResultConcept
|
||||
:return:
|
||||
"""
|
||||
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.parsers = {
|
||||
"Enabled80False": Enabled80FalseParser,
|
||||
"Enabled70False": Enabled70FalseParser,
|
||||
"Enabled50True": Enabled50TrueParser,
|
||||
}
|
||||
|
||||
user_input = [get_ret_val("hello world")]
|
||||
BaseTestParser.debug_out = []
|
||||
sheerka.execute(self.get_context(sheerka), user_input, [BuiltinConcepts.PARSING])
|
||||
|
||||
assert BaseTestParser.debug_out == [
|
||||
'name=Enabled80False, priority=80, status=False, source=hello world',
|
||||
'name=Enabled70False, priority=70, status=False, source=hello world',
|
||||
'name=Enabled70False, priority=70, status=False, source=Enabled80False:hello world',
|
||||
'name=Enabled50True, priority=50, status=False, source=hello world',
|
||||
'name=Enabled50True, priority=50, status=False, source=Enabled80False:hello world',
|
||||
]
|
||||
|
||||
def test_i_can_manage_parser_with_multiple_results(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.parsers = {
|
||||
"Enabled80MultipleFalse": Enabled80MultipleFalseParser,
|
||||
"Enabled50True": Enabled50TrueParser,
|
||||
}
|
||||
|
||||
user_input = [get_ret_val("hello world")]
|
||||
BaseTestParser.debug_out = []
|
||||
res = sheerka.execute(self.get_context(sheerka), user_input, [BuiltinConcepts.PARSING])
|
||||
|
||||
assert BaseTestParser.debug_out == [
|
||||
'name=Enabled80MultipleFalse, priority=80, status=False, source=hello world',
|
||||
'name=Enabled50True, priority=50, status=False, source=hello world',
|
||||
'name=Enabled50True, priority=50, status=False, source=Enabled80MultipleFalse:hello world_1',
|
||||
'name=Enabled50True, priority=50, status=False, source=Enabled80MultipleFalse:hello world_2',
|
||||
]
|
||||
|
||||
res_as_tuple = [(str(r.who)[8:], r.status, r.body.body) for r in res]
|
||||
assert res_as_tuple == [
|
||||
('Enabled80MultipleFalse', False, 'Enabled80MultipleFalse:hello world_1'),
|
||||
('Enabled80MultipleFalse', False, 'Enabled80MultipleFalse:hello world_2'),
|
||||
('Enabled50True', False, 'Enabled50True:hello world'),
|
||||
('Enabled50True', False, 'Enabled50True:Enabled80MultipleFalse:hello world_1'),
|
||||
('Enabled50True', False, 'Enabled50True:Enabled80MultipleFalse:hello world_2'),
|
||||
]
|
||||
|
||||
def test_i_can_manage_parser_with_multiple_results_and_a_sucess(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.parsers = {
|
||||
"Enabled80MultipleTrue": Enabled80MultipleTrueParser,
|
||||
"Enabled50True": Enabled50TrueParser,
|
||||
}
|
||||
|
||||
user_input = [get_ret_val("hello world")]
|
||||
BaseTestParser.debug_out = []
|
||||
res = sheerka.execute(self.get_context(sheerka), user_input, [BuiltinConcepts.PARSING])
|
||||
|
||||
assert BaseTestParser.debug_out == [
|
||||
'name=Enabled80MultipleTrue, priority=80, status=None, source=hello world',
|
||||
]
|
||||
|
||||
res_as_tuple = [(str(r.who)[8:], r.status, r.body.body) for r in res]
|
||||
assert res_as_tuple == [
|
||||
('Enabled80MultipleTrue', True, 'Enabled80MultipleTrue:hello world_1'),
|
||||
('Enabled80MultipleTrue', False, 'Enabled80MultipleTrue:hello world_2'),
|
||||
]
|
||||
@@ -0,0 +1,318 @@
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import Concept, ConceptParts
|
||||
from core.sheerka.ExecutionContext import ExecutionContext
|
||||
from core.sheerka_transform import SheerkaTransform, OBJ_TYPE_KEY, SheerkaTransformType, OBJ_ID_KEY
|
||||
from sdp.sheerkaDataProvider import Event
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
class TestSheerkaTransform(TestUsingMemoryBasedSheerka):
|
||||
def test_i_can_transform_an_unknown_concept(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
foo = Concept("foo", body="body")
|
||||
concept_with_sub = Concept("concept_with_sub", body=foo)
|
||||
|
||||
concept = Concept(
|
||||
name="concept_name",
|
||||
is_builtin=True,
|
||||
is_unique=True,
|
||||
key="concept_key",
|
||||
body=concept_with_sub,
|
||||
where=[foo, 1, "1", True, 1.0],
|
||||
pre=foo,
|
||||
post=None, # will not appear
|
||||
definition="it is a definition",
|
||||
definition_type="def type",
|
||||
desc="this this the desc"
|
||||
).def_prop("a", "10").def_prop("b", "foo").def_prop("c", "concept_with_sub")
|
||||
|
||||
# add values and props
|
||||
concept.values[ConceptParts.BODY] = Concept().update_from(concept_with_sub).auto_init()
|
||||
concept.values[ConceptParts.WHERE] = [foo, 1, "1", True, 1.0]
|
||||
concept.values[ConceptParts.PRE] = Concept().update_from(foo).auto_init()
|
||||
concept.values[ConceptParts.POST] = "a value for POST"
|
||||
concept.set_prop("a", 10).set_prop("b", foo).set_prop("c", concept_with_sub)
|
||||
|
||||
st = SheerkaTransform(sheerka)
|
||||
to_dict = st.to_dict(concept)
|
||||
|
||||
assert to_dict == {
|
||||
OBJ_TYPE_KEY: SheerkaTransformType.Concept,
|
||||
OBJ_ID_KEY: 0,
|
||||
'meta.name': 'concept_name',
|
||||
'meta.key': 'concept_key',
|
||||
'meta.is_builtin': True,
|
||||
'meta.is_unique': True,
|
||||
'meta.definition': 'it is a definition',
|
||||
'meta.definition_type': 'def type',
|
||||
'meta.desc': 'this this the desc',
|
||||
'meta.where': [{OBJ_TYPE_KEY: SheerkaTransformType.Concept,
|
||||
OBJ_ID_KEY: 1,
|
||||
'meta.body': 'body',
|
||||
'meta.name': 'foo'}, 1, '1', True, 1.0],
|
||||
'meta.pre': {OBJ_TYPE_KEY: SheerkaTransformType.Reference, OBJ_ID_KEY: 1},
|
||||
'meta.body': {
|
||||
'__type__': SheerkaTransformType.Concept,
|
||||
'__id__': 2,
|
||||
'meta.name': 'concept_with_sub',
|
||||
'meta.body': {
|
||||
'__type__': SheerkaTransformType.Reference,
|
||||
'__id__': 1}},
|
||||
'meta.props': [['a', '10'], ['b', 'foo'], ['c', 'concept_with_sub']],
|
||||
'pre': {'__type__': SheerkaTransformType.Concept,
|
||||
'__id__': 4,
|
||||
'meta.body': 'body',
|
||||
'meta.name': 'foo',
|
||||
'body': 'body'},
|
||||
'post': "a value for POST",
|
||||
'body': {'__type__': SheerkaTransformType.Concept,
|
||||
'__id__': 3,
|
||||
'meta.body': {'__id__': 1, '__type__': SheerkaTransformType.Reference},
|
||||
'meta.name': 'concept_with_sub',
|
||||
'body': {'__id__': 1, '__type__': SheerkaTransformType.Reference}},
|
||||
'where': [{OBJ_TYPE_KEY: SheerkaTransformType.Reference, OBJ_ID_KEY: 1}, 1, '1', True, 1.0],
|
||||
'props': [('a', 10),
|
||||
('b', {OBJ_TYPE_KEY: SheerkaTransformType.Reference, OBJ_ID_KEY: 1}),
|
||||
('c', {OBJ_TYPE_KEY: SheerkaTransformType.Reference, OBJ_ID_KEY: 2})],
|
||||
}
|
||||
|
||||
def test_i_can_transform_unknown_concept_with_almost_same_value(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept = Concept("foo")
|
||||
|
||||
st = SheerkaTransform(sheerka)
|
||||
to_dict = st.to_dict(concept)
|
||||
|
||||
assert to_dict == {OBJ_TYPE_KEY: SheerkaTransformType.Concept, OBJ_ID_KEY: 0, 'meta.name': 'foo'}
|
||||
|
||||
def test_i_can_transform_known_concept_when_the_values_are_the_same(self):
|
||||
"""
|
||||
Values are the same means that we are serializing a concept which has kept all its default values
|
||||
There is not diff between the concept to serialize and the one which was registered with create_new_concept()
|
||||
We serialize only the id of the concept
|
||||
:return:
|
||||
"""
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
concept = Concept(
|
||||
name="concept_name",
|
||||
is_builtin=True,
|
||||
is_unique=False,
|
||||
key="concept_key",
|
||||
body="body definition",
|
||||
where="where definition",
|
||||
pre="pre definition",
|
||||
post="post definition",
|
||||
definition="it is a definition",
|
||||
definition_type="def type",
|
||||
desc="this this the desc"
|
||||
).def_prop("a").def_prop("b")
|
||||
sheerka.create_new_concept(self.get_context(sheerka), concept)
|
||||
|
||||
new_concept = sheerka.new(concept.key)
|
||||
st = SheerkaTransform(sheerka)
|
||||
to_dict = st.to_dict(new_concept)
|
||||
|
||||
assert to_dict == {OBJ_TYPE_KEY: SheerkaTransformType.Concept, OBJ_ID_KEY: 0, "id": "1001"}
|
||||
|
||||
def test_i_can_transform_known_concept_when_the_values_are_different(self):
|
||||
"""
|
||||
Values are the different means the concept was modified.
|
||||
It's different from the one which was registered with create_new_concept()
|
||||
We serialize only the differences
|
||||
:return:
|
||||
"""
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
concept = Concept(
|
||||
name="concept_name",
|
||||
is_builtin=True,
|
||||
is_unique=False,
|
||||
key="concept_key",
|
||||
body="body definition",
|
||||
where="where definition",
|
||||
pre="pre definition",
|
||||
post="post definition",
|
||||
definition="it is a definition",
|
||||
definition_type="def type",
|
||||
desc="this this the desc"
|
||||
).def_prop("a").def_prop("b")
|
||||
sheerka.create_new_concept(self.get_context(sheerka), concept)
|
||||
|
||||
new_concept = sheerka.new(concept.key, body="another", a=10, pre="another pre")
|
||||
st = SheerkaTransform(sheerka)
|
||||
to_dict = st.to_dict(new_concept)
|
||||
|
||||
assert to_dict == {
|
||||
OBJ_TYPE_KEY: SheerkaTransformType.Concept,
|
||||
OBJ_ID_KEY: 0,
|
||||
"id": "1001",
|
||||
'pre': 'another pre',
|
||||
"body": "another",
|
||||
'props': [('a', 10)]
|
||||
}
|
||||
|
||||
def test_i_can_transform_concept_with_circular_reference(self):
|
||||
sheerka = self.get_sheerka()
|
||||
foo = Concept("foo")
|
||||
bar = Concept("bar", body=foo)
|
||||
foo.metadata.body = bar
|
||||
|
||||
st = SheerkaTransform(sheerka)
|
||||
to_dict = st.to_dict(foo)
|
||||
|
||||
assert to_dict == {
|
||||
OBJ_TYPE_KEY: SheerkaTransformType.Concept,
|
||||
OBJ_ID_KEY: 0,
|
||||
'meta.name': 'foo',
|
||||
'meta.body': {OBJ_TYPE_KEY: SheerkaTransformType.Concept,
|
||||
OBJ_ID_KEY: 1,
|
||||
'meta.name': 'bar',
|
||||
'meta.body': {OBJ_TYPE_KEY: SheerkaTransformType.Reference,
|
||||
OBJ_ID_KEY: 0},
|
||||
},
|
||||
}
|
||||
|
||||
def test_i_can_transform_concept_with_circular_reference_2(self):
|
||||
sheerka = self.get_sheerka()
|
||||
foo = Concept("foo")
|
||||
bar = Concept("foo", body=foo)
|
||||
foo.metadata.body = bar
|
||||
|
||||
st = SheerkaTransform(sheerka)
|
||||
to_dict = st.to_dict(foo)
|
||||
|
||||
assert to_dict == {
|
||||
OBJ_TYPE_KEY: SheerkaTransformType.Concept,
|
||||
OBJ_ID_KEY: 0,
|
||||
'meta.name': 'foo',
|
||||
'meta.body': {OBJ_TYPE_KEY: SheerkaTransformType.Concept,
|
||||
OBJ_ID_KEY: 1,
|
||||
'meta.name': 'foo',
|
||||
'meta.body': {OBJ_TYPE_KEY: SheerkaTransformType.Reference,
|
||||
OBJ_ID_KEY: 0},
|
||||
},
|
||||
}
|
||||
|
||||
def test_i_can_transform_the_unknown_concept(self):
|
||||
sheerka = self.get_sheerka(False)
|
||||
|
||||
unknown = sheerka.new(BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
|
||||
st = SheerkaTransform(sheerka)
|
||||
to_dict = st.to_dict(unknown)
|
||||
|
||||
assert len(to_dict) == 3
|
||||
assert to_dict[OBJ_TYPE_KEY] == SheerkaTransformType.Concept
|
||||
assert to_dict[OBJ_ID_KEY] == 0
|
||||
assert "id" in to_dict
|
||||
|
||||
def test_i_can_transform_simple_execution_context(self):
|
||||
sheerka = self.get_sheerka()
|
||||
ExecutionContext.ids = {}
|
||||
context = ExecutionContext("requester", Event(), sheerka, 'this is the desc')
|
||||
|
||||
st = SheerkaTransform(sheerka)
|
||||
to_dict = st.to_dict(context)
|
||||
|
||||
assert to_dict == {
|
||||
OBJ_TYPE_KEY: SheerkaTransformType.ExecutionContext,
|
||||
OBJ_ID_KEY: 0,
|
||||
'_parent': None,
|
||||
'_id': 0,
|
||||
'_tab': '',
|
||||
'_bag': {},
|
||||
'_start': 0,
|
||||
'_stop': 0,
|
||||
'who': 'requester',
|
||||
'event': {OBJ_TYPE_KEY: SheerkaTransformType.Event, OBJ_ID_KEY: 1, 'digest': 'xxx'},
|
||||
'desc': 'this is the desc',
|
||||
'children': [],
|
||||
'preprocess': None,
|
||||
'inputs': {},
|
||||
'values': {},
|
||||
'obj': None,
|
||||
'concepts': {}
|
||||
}
|
||||
|
||||
def test_i_can_transform_list(self):
|
||||
sheerka = self.get_sheerka()
|
||||
ExecutionContext.ids = {}
|
||||
context = ExecutionContext("requester", Event(), sheerka, 'this is the desc')
|
||||
|
||||
st = SheerkaTransform(sheerka)
|
||||
to_dict = st.to_dict([context])
|
||||
|
||||
assert len(to_dict) == 1
|
||||
assert isinstance(to_dict, list)
|
||||
assert to_dict[0]["who"] == "requester"
|
||||
assert to_dict[0]["desc"] == "this is the desc"
|
||||
|
||||
def test_i_can_transform_set(self):
|
||||
sheerka = self.get_sheerka()
|
||||
ExecutionContext.ids = {}
|
||||
context = ExecutionContext("requester", Event(), sheerka, 'this is the desc')
|
||||
|
||||
st = SheerkaTransform(sheerka)
|
||||
to_dict = st.to_dict({context})
|
||||
|
||||
assert len(to_dict) == 1
|
||||
assert isinstance(to_dict, list)
|
||||
assert to_dict[0]["who"] == "requester"
|
||||
assert to_dict[0]["desc"] == "this is the desc"
|
||||
|
||||
def test_i_can_transform_dict(self):
|
||||
sheerka = self.get_sheerka()
|
||||
ExecutionContext.ids = {}
|
||||
context = ExecutionContext("requester", Event(), sheerka, 'this is the desc')
|
||||
known_concept = Concept("foo", body="foo").set_prop("a", "value_of_a").init_key()
|
||||
sheerka.create_new_concept(self.get_context(sheerka), known_concept)
|
||||
unknown_concept = Concept("bar")
|
||||
known = sheerka.new("foo")
|
||||
|
||||
bag = {
|
||||
"context": context,
|
||||
"known_concept": known_concept,
|
||||
"unknown_concept": unknown_concept,
|
||||
"True": True,
|
||||
"Number": 1.1,
|
||||
"String": "a string value",
|
||||
"None": None,
|
||||
unknown_concept: "hello",
|
||||
known: "world"
|
||||
}
|
||||
|
||||
st = SheerkaTransform(sheerka)
|
||||
to_dict = st.to_dict(bag)
|
||||
|
||||
assert isinstance(to_dict, dict)
|
||||
assert to_dict['Number'] == 1.1
|
||||
assert to_dict['String'] == 'a string value'
|
||||
assert to_dict['True']
|
||||
assert to_dict['None'] is None
|
||||
assert to_dict["context"][OBJ_TYPE_KEY] == SheerkaTransformType.ExecutionContext
|
||||
assert to_dict["known_concept"][OBJ_TYPE_KEY] == SheerkaTransformType.Concept
|
||||
assert to_dict["known_concept"]["id"] == '1001'
|
||||
assert to_dict["unknown_concept"][OBJ_TYPE_KEY] == SheerkaTransformType.Concept
|
||||
assert to_dict["(None)bar"] == "hello"
|
||||
assert to_dict["(1001)foo"] == "world"
|
||||
|
||||
def test_i_can_transform_when_circular_references(self):
|
||||
sheerka = self.get_sheerka()
|
||||
ExecutionContext.ids = {}
|
||||
context = ExecutionContext("requester", Event(), sheerka, 'this is the desc')
|
||||
context.push("another requester", "another desc")
|
||||
|
||||
st = SheerkaTransform(sheerka)
|
||||
to_dict = st.to_dict(context)
|
||||
|
||||
assert isinstance(to_dict, dict)
|
||||
assert to_dict[OBJ_TYPE_KEY] == SheerkaTransformType.ExecutionContext
|
||||
assert len(to_dict["children"]) == 1
|
||||
assert to_dict["children"][0][OBJ_TYPE_KEY] == SheerkaTransformType.ExecutionContext
|
||||
assert to_dict["children"][0]['_parent'][OBJ_TYPE_KEY] == SheerkaTransformType.Reference
|
||||
assert to_dict["children"][0]['_parent'][OBJ_ID_KEY] == 0
|
||||
assert to_dict["children"][0]['event'][OBJ_TYPE_KEY] == SheerkaTransformType.Reference
|
||||
assert to_dict["children"][0]['event'][OBJ_ID_KEY] == 1
|
||||
@@ -0,0 +1,155 @@
|
||||
import ast
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
|
||||
from core.concept import VARIABLE_PREFIX, Concept
|
||||
from core.tokenizer import Tokenizer
|
||||
from evaluators.AddConceptEvaluator import AddConceptEvaluator
|
||||
from parsers.BaseParser import BaseParser
|
||||
from parsers.ConceptLexerParser import Sequence, StrMatch, ZeroOrMore, ConceptExpression
|
||||
from parsers.BnfParser import BnfParser
|
||||
from parsers.DefaultParser import DefConceptNode, NameNode
|
||||
from parsers.PythonParser import PythonNode, PythonParser
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
class TestAddConceptEvaluator(TestUsingMemoryBasedSheerka):
|
||||
|
||||
@staticmethod
|
||||
def get_concept_part(part):
|
||||
if isinstance(part, str):
|
||||
node = PythonNode(part, ast.parse(part, mode="eval"))
|
||||
return ReturnValueConcept(
|
||||
who="parsers.Default",
|
||||
status=True,
|
||||
value=ParserResultConcept(
|
||||
source=part,
|
||||
parser=PythonParser(),
|
||||
value=node))
|
||||
|
||||
if isinstance(part, PythonNode):
|
||||
return ReturnValueConcept(
|
||||
who="parsers.Default",
|
||||
status=True,
|
||||
value=ParserResultConcept(
|
||||
source=part.source,
|
||||
parser=PythonParser(),
|
||||
value=part))
|
||||
|
||||
if isinstance(part, ReturnValueConcept):
|
||||
return part
|
||||
|
||||
@staticmethod
|
||||
def get_return_value(source, parsing_expression):
|
||||
return ReturnValueConcept(
|
||||
who="Parsers:RegexParser",
|
||||
status=True,
|
||||
value=ParserResultConcept(
|
||||
source=source,
|
||||
parser=BnfParser(),
|
||||
value=parsing_expression
|
||||
)
|
||||
)
|
||||
|
||||
def get_def_concept(self, name, where=None, pre=None, post=None, body=None, definition=None):
|
||||
concept = DefConceptNode([], name=NameNode(list(Tokenizer(name))))
|
||||
|
||||
if body:
|
||||
concept.body = self.get_concept_part(body)
|
||||
if where:
|
||||
concept.where = self.get_concept_part(where)
|
||||
if pre:
|
||||
concept.pre = self.get_concept_part(pre)
|
||||
if post:
|
||||
concept.post = self.get_concept_part(post)
|
||||
if definition:
|
||||
concept.definition = definition
|
||||
|
||||
return ReturnValueConcept(BaseParser.PREFIX + "some_name", True, ParserResultConcept(value=concept))
|
||||
|
||||
@pytest.mark.parametrize("ret_val, expected", [
|
||||
(
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name", True, ParserResultConcept(value=DefConceptNode([]))),
|
||||
True),
|
||||
(ReturnValueConcept(BaseParser.PREFIX + "some_name", False, ParserResultConcept(value=DefConceptNode([]))),
|
||||
False),
|
||||
(ReturnValueConcept(BaseParser.PREFIX + "some_name", True, "not a ParserResultConcept"), False),
|
||||
(ReturnValueConcept(BaseParser.PREFIX + "some_name", True, ParserResultConcept()), False),
|
||||
])
|
||||
def test_i_can_match(self, ret_val, expected):
|
||||
context = self.get_context()
|
||||
assert AddConceptEvaluator().matches(context, ret_val) == expected
|
||||
|
||||
def test_that_the_source_is_correctly_set(self):
|
||||
context = self.get_context()
|
||||
def_concept_return_value = self.get_def_concept(
|
||||
name="hello a",
|
||||
definition=self.get_return_value("hello a", Sequence(StrMatch("hello"), StrMatch("a"))),
|
||||
where="isinstance(a, str )",
|
||||
pre="a is not None",
|
||||
body="print('hello' + a)")
|
||||
|
||||
evaluated = AddConceptEvaluator().eval(context, def_concept_return_value)
|
||||
|
||||
assert evaluated.status
|
||||
assert context.sheerka.isinstance(evaluated.body, BuiltinConcepts.NEW_CONCEPT)
|
||||
|
||||
created_concept = evaluated.body.body
|
||||
assert created_concept.metadata.name == "hello a"
|
||||
assert created_concept.metadata.where == "isinstance(a, str )"
|
||||
assert created_concept.metadata.pre == "a is not None"
|
||||
assert created_concept.metadata.post is None
|
||||
assert created_concept.metadata.body == "print('hello' + a)"
|
||||
assert created_concept.metadata.definition == "hello a"
|
||||
|
||||
def test_that_the_new_concept_is_correctly_saved_in_db(self):
|
||||
context = self.get_context()
|
||||
def_concept_return_value = self.get_def_concept(
|
||||
name="hello a",
|
||||
definition=self.get_return_value("hello a", Sequence(StrMatch("hello"), StrMatch("a"))),
|
||||
where="isinstance(a, str )",
|
||||
pre="a is not None",
|
||||
body="print('hello' + a)")
|
||||
|
||||
# sanity. Make sure that the concept does not already exist
|
||||
from_db = context.sheerka.get("hello " + VARIABLE_PREFIX + "0")
|
||||
assert context.sheerka.isinstance(from_db, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
|
||||
AddConceptEvaluator().eval(context, def_concept_return_value)
|
||||
context.sheerka.concepts_cache = {} # reset cache
|
||||
from_db = context.sheerka.get("hello " + VARIABLE_PREFIX + "0")
|
||||
|
||||
assert from_db.metadata.key == f"hello {VARIABLE_PREFIX}0"
|
||||
assert from_db.metadata.name == "hello a"
|
||||
assert from_db.metadata.where == "isinstance(a, str )"
|
||||
assert from_db.metadata.pre == "a is not None"
|
||||
assert from_db.metadata.post is None
|
||||
assert from_db.metadata.body == "print('hello' + a)"
|
||||
assert from_db.metadata.definition == "hello a"
|
||||
assert len(from_db.metadata.props) == 1
|
||||
assert from_db.metadata.props[0] == ("a", None)
|
||||
assert "a" in from_db.props
|
||||
|
||||
assert from_db.compiled == {} # ast is not saved in db
|
||||
|
||||
def test_i_can_get_props_from_python_node(self):
|
||||
ret_val = self.get_concept_part("isinstance(a, str)")
|
||||
context = self.get_context()
|
||||
|
||||
assert AddConceptEvaluator.get_props(context.sheerka, ret_val, ["a"]) == ["a"]
|
||||
|
||||
def test_i_can_get_props_from_another_concept(self):
|
||||
concept = Concept("hello").def_prop("a").def_prop("b")
|
||||
ret_val = ReturnValueConcept(who="some_parser",
|
||||
status=True,
|
||||
value=ParserResultConcept(value=concept))
|
||||
|
||||
assert AddConceptEvaluator.get_props(self.get_context(), ret_val, []) == ["a", "b"]
|
||||
|
||||
def test_i_can_get_props_from_definition(self):
|
||||
parsing_expression = Sequence(ConceptExpression('mult'),
|
||||
ZeroOrMore(Sequence(StrMatch("+"), ConceptExpression("add"))))
|
||||
ret_val = self.get_return_value("mult (('+'|'-') add)?", parsing_expression)
|
||||
|
||||
assert AddConceptEvaluator.get_props(self.get_context(), ret_val, []) == ["add", "mult"]
|
||||
@@ -0,0 +1,102 @@
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from core.tokenizer import Tokenizer
|
||||
from evaluators.AddConceptInSetEvaluator import AddConceptInSetEvaluator
|
||||
from parsers.DefaultParser import IsaConceptNode, NameNode
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
def get_ret_val(concept_name, concept_set_name):
|
||||
n1 = NameNode(list(Tokenizer(concept_name)))
|
||||
n2 = NameNode(list(Tokenizer(concept_set_name)))
|
||||
|
||||
return ReturnValueConcept("some_name", True, ParserResultConcept(value=IsaConceptNode([], n1, n2)))
|
||||
|
||||
|
||||
class TestAssConceptInSetEvaluator(TestUsingMemoryBasedSheerka):
|
||||
|
||||
@pytest.mark.parametrize("ret_val, expected", [
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept(value=IsaConceptNode([]))), True),
|
||||
(ReturnValueConcept("some_name", False, ParserResultConcept(value=IsaConceptNode([]))), False),
|
||||
(ReturnValueConcept("some_name", True, "not a ParserResultConcept"), False),
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept()), False),
|
||||
])
|
||||
def test_i_can_match(self, ret_val, expected):
|
||||
context = self.get_context()
|
||||
assert AddConceptInSetEvaluator().matches(context, ret_val) == expected
|
||||
|
||||
def test_i_cannot_add_if_the_concept_does_not_exists(self):
|
||||
context = self.get_context()
|
||||
|
||||
ret_val = get_ret_val("foo", "bar")
|
||||
res = AddConceptInSetEvaluator().eval(context, ret_val)
|
||||
|
||||
assert not res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
assert res.value.body == "foo"
|
||||
|
||||
def test_i_cannot_add_if_the_set_does_not_exists(self):
|
||||
context = self.get_context()
|
||||
foo = Concept("foo")
|
||||
context.sheerka.set_id_if_needed(foo, False)
|
||||
context.sheerka.add_in_cache(foo)
|
||||
|
||||
ret_val = get_ret_val("foo", "bar")
|
||||
res = AddConceptInSetEvaluator().eval(context, ret_val)
|
||||
|
||||
assert not res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
assert res.value.body == "bar"
|
||||
|
||||
def test_i_can_add_concept_to_a_set_of_concept(self):
|
||||
context = self.get_context()
|
||||
foo = Concept("foo")
|
||||
context.sheerka.set_id_if_needed(foo, False)
|
||||
context.sheerka.add_in_cache(foo)
|
||||
|
||||
bar = Concept("bar")
|
||||
context.sheerka.set_id_if_needed(bar, False)
|
||||
context.sheerka.add_in_cache(bar)
|
||||
|
||||
ret_val = get_ret_val("foo", "bar")
|
||||
res = AddConceptInSetEvaluator().eval(context, ret_val)
|
||||
|
||||
assert res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.SUCCESS)
|
||||
|
||||
def test_i_can_add_concept_with_a_body_to_a_set_of_concept(self):
|
||||
context = self.get_context()
|
||||
foo = Concept("foo", body="1")
|
||||
context.sheerka.set_id_if_needed(foo, False)
|
||||
context.sheerka.add_in_cache(foo)
|
||||
|
||||
bar = Concept("bar")
|
||||
context.sheerka.set_id_if_needed(bar, False)
|
||||
context.sheerka.add_in_cache(bar)
|
||||
|
||||
ret_val = get_ret_val("foo", "bar")
|
||||
res = AddConceptInSetEvaluator().eval(context, ret_val)
|
||||
|
||||
assert res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.SUCCESS)
|
||||
|
||||
def test_i_cannot_add_the_same_concept_twice(self):
|
||||
context = self.get_context()
|
||||
foo = Concept("foo")
|
||||
context.sheerka.set_id_if_needed(foo, False)
|
||||
context.sheerka.add_in_cache(foo)
|
||||
|
||||
bar = Concept("bar")
|
||||
context.sheerka.set_id_if_needed(bar, False)
|
||||
context.sheerka.add_in_cache(bar)
|
||||
|
||||
ret_val = get_ret_val("foo", "bar")
|
||||
AddConceptInSetEvaluator().eval(context, ret_val)
|
||||
res = AddConceptInSetEvaluator().eval(context, ret_val)
|
||||
|
||||
assert not res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.CONCEPT_ALREADY_IN_SET)
|
||||
assert res.value.concept == foo
|
||||
assert res.value.concept_set == bar
|
||||
@@ -0,0 +1,117 @@
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
|
||||
from core.concept import Concept, ConceptParts
|
||||
from evaluators.ConceptEvaluator import ConceptEvaluator
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
class TestAddConceptEvaluator(TestUsingMemoryBasedSheerka):
|
||||
|
||||
@pytest.mark.parametrize("ret_val, expected", [
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept(value=Concept())), True),
|
||||
(ReturnValueConcept("some_name", False, ParserResultConcept(value=Concept())), False),
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept(value="Not a concept")), False),
|
||||
(ReturnValueConcept("some_name", True, Concept()), False),
|
||||
])
|
||||
def test_i_can_match(self, ret_val, expected):
|
||||
context = self.get_context()
|
||||
assert ConceptEvaluator().matches(context, ret_val) == expected
|
||||
|
||||
def test_i_can_evaluate_concept(self):
|
||||
context = self.get_context()
|
||||
concept = Concept(name="foo",
|
||||
where="1",
|
||||
pre="2",
|
||||
post="3").def_prop("a", "4").def_prop("b", "5")
|
||||
|
||||
evaluator = ConceptEvaluator()
|
||||
item = self.pretval(concept)
|
||||
result = evaluator.eval(context, item)
|
||||
|
||||
assert result.who == evaluator.name
|
||||
assert result.status
|
||||
assert result.value.name == "foo"
|
||||
assert result.value.get_metadata_value(ConceptParts.WHERE) == 1
|
||||
assert result.value.get_metadata_value(ConceptParts.PRE) == 2
|
||||
assert result.value.get_metadata_value(ConceptParts.POST) == 3
|
||||
assert result.value.get_prop("a") == 4
|
||||
assert result.value.get_prop("b") == 5
|
||||
assert result.value.key == "foo"
|
||||
assert result.parents == [item]
|
||||
|
||||
def test_body_is_returned_when_defined_and_requested(self):
|
||||
context = self.get_context()
|
||||
concept = Concept(name="foo",
|
||||
body="'I have a value'",
|
||||
where="1",
|
||||
pre="2",
|
||||
post="3").set_prop("a", "4").set_prop("b", "5")
|
||||
|
||||
evaluator = ConceptEvaluator(return_body=True)
|
||||
item = self.pretval(concept)
|
||||
result = evaluator.eval(context, item)
|
||||
|
||||
assert result.who == evaluator.name
|
||||
assert result.status
|
||||
assert result.value == "I have a value"
|
||||
assert result.parents == [item]
|
||||
|
||||
def test_body_is_not_returned_if_not_requested(self):
|
||||
context = self.get_context()
|
||||
concept = Concept(name="foo",
|
||||
body="'I have a value'",
|
||||
where="1",
|
||||
pre="2",
|
||||
post="3").set_prop("a", "4").set_prop("b", "5")
|
||||
|
||||
evaluator = ConceptEvaluator(return_body=False) # which is the default behaviour
|
||||
item = self.pretval(concept)
|
||||
result = evaluator.eval(context, item)
|
||||
|
||||
assert result.who == evaluator.name
|
||||
assert result.status
|
||||
assert result.value == concept
|
||||
assert result.parents == [item]
|
||||
|
||||
def test_i_can_eval_if_with_the_same_name_is_defined_in_the_context(self):
|
||||
# If we evaluate Concept("foo", body="a").set_prop("a", "'property_a'")
|
||||
# ConceptEvaluator will be called to resolve 'a' while we know that 'a' refers to the string 'property_a'
|
||||
|
||||
context = self.get_context()
|
||||
context.obj = Concept("other").set_prop("foo", "'some_other_value'")
|
||||
concept = Concept(name="foo")
|
||||
|
||||
item = self.pretval(concept)
|
||||
result = ConceptEvaluator().eval(context, item)
|
||||
|
||||
assert result.status
|
||||
assert result.value == "'some_other_value'"
|
||||
|
||||
def test_i_cannot_recognize_a_concept_if_one_of_the_prop_is_unknown(self):
|
||||
context = self.get_context()
|
||||
context.sheerka.add_in_cache(Concept(name="one").init_key())
|
||||
concept_plus = context.sheerka.add_in_cache(Concept(name="a plus b")
|
||||
.def_prop("a", "one")
|
||||
.def_prop("b", "two").init_key())
|
||||
|
||||
evaluator = ConceptEvaluator()
|
||||
item = self.pretval(concept_plus)
|
||||
result = evaluator.eval(context, item)
|
||||
|
||||
assert not result.status
|
||||
assert context.sheerka.isinstance(result.value, BuiltinConcepts.CONCEPT_EVAL_ERROR)
|
||||
assert result.value.concept == concept_plus
|
||||
assert result.value.property_name == "b"
|
||||
assert context.sheerka.isinstance(result.value.error, BuiltinConcepts.TOO_MANY_ERRORS)
|
||||
|
||||
def test_that_error_concepts_are_transformed_into_errors_only_if_the_key_is_different(self):
|
||||
context = self.get_context()
|
||||
|
||||
error_concept = context.sheerka.new(BuiltinConcepts.ERROR)
|
||||
item = self.pretval(error_concept)
|
||||
result = ConceptEvaluator().eval(context, item)
|
||||
|
||||
assert not context.sheerka.is_success(error_concept) # it's indeed an error
|
||||
assert result.status
|
||||
assert result.value == error_concept
|
||||
@@ -0,0 +1,72 @@
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import ReturnValueConcept, BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from evaluators.EvalEvaluator import EvalEvaluator
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
eval_requested = ReturnValueConcept("some_name", True, Concept(key=BuiltinConcepts.CONCEPT_EVAL_REQUESTED))
|
||||
|
||||
|
||||
def retval(obj, who="who", status=True):
|
||||
"""ret_val"""
|
||||
return ReturnValueConcept(who, status, obj)
|
||||
|
||||
|
||||
class TestEvalEvaluator(TestUsingMemoryBasedSheerka):
|
||||
def test_i_can_match_and_eval(self):
|
||||
context = self.get_context()
|
||||
|
||||
to_eval1 = ReturnValueConcept("some_name", True, Concept(name="2", body="to eval").auto_init())
|
||||
to_eval2 = ReturnValueConcept("some_name", True, Concept(name="3", body="also to eval").auto_init())
|
||||
|
||||
return_values = [
|
||||
ReturnValueConcept("some_name", True, "not to eval"),
|
||||
ReturnValueConcept("some_name", True, Concept(name="not to eval")),
|
||||
ReturnValueConcept("some_name", False, Concept(name="1", body="'not to eval'")),
|
||||
to_eval1,
|
||||
to_eval2,
|
||||
eval_requested
|
||||
]
|
||||
|
||||
evaluator = EvalEvaluator()
|
||||
assert evaluator.matches(context, return_values)
|
||||
|
||||
evaluated = evaluator.eval(context, return_values)
|
||||
assert len(evaluated) == 2
|
||||
assert evaluated[0].value == to_eval1.body.body
|
||||
assert evaluated[0].parents == [to_eval1, eval_requested]
|
||||
|
||||
assert evaluated[1].value == to_eval2.body.body
|
||||
assert evaluated[1].parents == [to_eval2, eval_requested]
|
||||
|
||||
@pytest.mark.parametrize("return_values, expected", [
|
||||
([retval(Concept("foo", body="bar")), eval_requested], True),
|
||||
([retval(Concept("status is false", body="bar"), status=False), eval_requested], True),
|
||||
([retval("string_value"), eval_requested], True),
|
||||
([retval(Concept("no body")), eval_requested], True),
|
||||
([retval(Concept("eval requested missing", body="bar"))], False),
|
||||
])
|
||||
def test_i_cannot_match_if_eval_request_is_not_present(self, return_values, expected):
|
||||
context = self.get_context()
|
||||
assert EvalEvaluator().matches(context, return_values) == expected
|
||||
|
||||
def test_concept_eval_requested_is_reduced_when_nothing_to_reduce(self):
|
||||
context = self.get_context()
|
||||
|
||||
return_values = [
|
||||
ReturnValueConcept("some_name", True, "not to eval"),
|
||||
ReturnValueConcept("some_name", True, Concept(name="not to eval")),
|
||||
ReturnValueConcept("some_name", False, Concept(name="1", body="not to eval")),
|
||||
eval_requested
|
||||
]
|
||||
|
||||
evaluator = EvalEvaluator()
|
||||
assert evaluator.matches(context, return_values)
|
||||
|
||||
evaluated = evaluator.eval(context, return_values)
|
||||
assert evaluated == ReturnValueConcept(
|
||||
"evaluators.Eval",
|
||||
False,
|
||||
context.sheerka.new(BuiltinConcepts.CONCEPT_EVAL_REQUESTED))
|
||||
assert evaluated.parents == [eval_requested]
|
||||
@@ -0,0 +1,104 @@
|
||||
import ast
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
|
||||
from core.concept import Concept, ConceptParts, DoNotResolve
|
||||
from evaluators.LexerNodeEvaluator import LexerNodeEvaluator
|
||||
from parsers.ConceptLexerParser import ConceptNode, ConceptLexerParser, StrMatch, UnrecognizedTokensNode, SourceCodeNode
|
||||
from parsers.PythonParser import PythonNode
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
class TestLexerNodeEvaluator(TestUsingMemoryBasedSheerka):
|
||||
|
||||
def from_parsing(self, context, grammar, expression):
|
||||
parser = ConceptLexerParser()
|
||||
parser.initialize(context, grammar)
|
||||
|
||||
ret_val = parser.parse(context, expression)
|
||||
assert ret_val.status
|
||||
return ret_val
|
||||
|
||||
def from_fragments(self, *fragments):
|
||||
nodes = []
|
||||
for fragment in fragments:
|
||||
if isinstance(fragment, str):
|
||||
node = PythonNode(fragment, ast.parse(fragment.strip(), mode="eval"))
|
||||
nodes.append(SourceCodeNode(node, 0, 0, [], fragment))
|
||||
else:
|
||||
nodes.append(ConceptNode(fragment, 0, 0, [], fragment.name))
|
||||
|
||||
return ReturnValueConcept("somme_name", True, ParserResultConcept(value=nodes))
|
||||
|
||||
def init(self, concept, grammar, text):
|
||||
context = self.get_context()
|
||||
if isinstance(concept, list):
|
||||
for c in concept:
|
||||
context.sheerka.add_in_cache(c)
|
||||
else:
|
||||
context.sheerka.add_in_cache(concept)
|
||||
ret_val = self.from_parsing(context, grammar, text)
|
||||
node = ret_val.value.value[0]
|
||||
|
||||
return context, node
|
||||
|
||||
@pytest.mark.parametrize("ret_val, expected", [
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept(value=[ConceptNode(Concept(), 0, 0)])), True),
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept(value=ConceptNode(Concept(), 0, 0))), True),
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept(value=[SourceCodeNode(0, 0, [])])), True),
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept(value=SourceCodeNode(0, 0, []))), True),
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept(value=[UnrecognizedTokensNode(0, 0, [])])), False),
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept(value=UnrecognizedTokensNode(0, 0, []))), False),
|
||||
(ReturnValueConcept("some_name", False, ParserResultConcept(value=[ConceptNode(Concept(), 0, 0)])), False),
|
||||
(ReturnValueConcept("some_name", False, ParserResultConcept(value=ConceptNode(Concept(), 0, 0))), False),
|
||||
(ReturnValueConcept("some_name", False, ParserResultConcept(value=[SourceCodeNode(0, 0, [])])), False),
|
||||
(ReturnValueConcept("some_name", False, ParserResultConcept(value=SourceCodeNode(0, 0, []))), False),
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept(value="Not a concept node")), False),
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept(value=["Not a concept node"])), False),
|
||||
(ReturnValueConcept("some_name", True, [ConceptNode(Concept(), 0, 0)]), False),
|
||||
(ReturnValueConcept("some_name", True, ConceptNode(Concept(), 0, 0)), False),
|
||||
])
|
||||
def test_i_can_match(self, ret_val, expected):
|
||||
context = self.get_context()
|
||||
assert LexerNodeEvaluator().matches(context, ret_val) == expected
|
||||
|
||||
def test_concept_is_returned_when_only_one_in_the_list(self):
|
||||
foo = Concept("foo")
|
||||
context = self.get_context()
|
||||
context.sheerka.add_in_cache(foo)
|
||||
|
||||
ret_val = self.from_parsing(context, {foo: StrMatch("foo")}, "foo")
|
||||
|
||||
evaluator = LexerNodeEvaluator()
|
||||
result = evaluator.eval(context, ret_val)
|
||||
wrapper = result.body
|
||||
return_value = result.body.body
|
||||
|
||||
assert result.who == evaluator.name
|
||||
assert result.status
|
||||
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||
assert wrapper.parser == evaluator
|
||||
assert wrapper.source == "foo"
|
||||
assert return_value == Concept("foo").init_key()
|
||||
assert return_value.compiled[ConceptParts.BODY] == DoNotResolve("foo")
|
||||
assert result.parents == [ret_val]
|
||||
|
||||
def test_concept_python_node_is_returned_when_source_code(self):
|
||||
context = self.get_context()
|
||||
foo = Concept("foo")
|
||||
ret_val = self.from_fragments(foo, " + 1")
|
||||
|
||||
evaluator = LexerNodeEvaluator()
|
||||
result = evaluator.eval(context, ret_val)
|
||||
wrapper = result.body
|
||||
return_value = result.body.body
|
||||
|
||||
assert result.who == evaluator.name
|
||||
assert result.status
|
||||
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||
assert wrapper.parser == evaluator
|
||||
assert wrapper.source == "foo + 1"
|
||||
|
||||
assert return_value == PythonNode('foo + 1', ast.parse("__C__foo__C__ + 1", mode="eval"))
|
||||
assert return_value.concepts == {"__C__foo__C__": foo}
|
||||
assert result.parents == [ret_val]
|
||||
@@ -0,0 +1,200 @@
|
||||
from core.builtin_concepts import ReturnValueConcept, BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from evaluators.BaseEvaluator import BaseEvaluator
|
||||
from evaluators.MutipleSameSuccessEvaluator import MultipleSameSuccessEvaluator
|
||||
from parsers.BaseParser import BaseParser
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
class TestMultipleSameSuccessEvaluator(TestUsingMemoryBasedSheerka):
|
||||
|
||||
def test_i_can_match_and_eval(self):
|
||||
context = self.get_context()
|
||||
sheerka = context.sheerka
|
||||
|
||||
return_values = [
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name", False, "Not relevant"),
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name2", False, "Not relevant"),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="1", body="value").auto_init()),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="2", body="value").auto_init()),
|
||||
ReturnValueConcept("some_name", True, sheerka.new(BuiltinConcepts.REDUCE_REQUESTED))
|
||||
]
|
||||
|
||||
evaluator = MultipleSameSuccessEvaluator()
|
||||
assert evaluator.matches(context, return_values)
|
||||
|
||||
evaluated = evaluator.eval(context, return_values)
|
||||
assert evaluated.status
|
||||
assert evaluated.value == Concept(name="1", body="value").auto_init() # the first concept is returned
|
||||
|
||||
def test_i_can_match_and_eval_when_no_body(self):
|
||||
context = self.get_context()
|
||||
sheerka = context.sheerka
|
||||
|
||||
return_values = [
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name", False, "Not relevant"),
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name2", False, "Not relevant"),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="1").auto_init()),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="1").auto_init()),
|
||||
ReturnValueConcept("some_name", True, sheerka.new(BuiltinConcepts.REDUCE_REQUESTED))
|
||||
]
|
||||
|
||||
evaluator = MultipleSameSuccessEvaluator()
|
||||
assert evaluator.matches(context, return_values)
|
||||
|
||||
evaluated = evaluator.eval(context, return_values)
|
||||
assert evaluated.status
|
||||
assert evaluated.value == Concept(name="1").auto_init()
|
||||
|
||||
def test_i_can_match_and_eval_when_value_is_not_a_concept(self):
|
||||
context = self.get_context()
|
||||
sheerka = context.sheerka
|
||||
|
||||
return_values = [
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name", False, "Not relevant"),
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name2", False, "Not relevant"),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, "value"),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, "value"),
|
||||
ReturnValueConcept("some_name", True, sheerka.new(BuiltinConcepts.REDUCE_REQUESTED))
|
||||
]
|
||||
|
||||
evaluator = MultipleSameSuccessEvaluator()
|
||||
assert evaluator.matches(context, return_values)
|
||||
|
||||
evaluated = evaluator.eval(context, return_values)
|
||||
assert evaluated.status
|
||||
assert evaluated.value == "value"
|
||||
|
||||
def test_i_can_match_and_eval_when_at_least_one_value_is_a_concept_concept_evaluator_first(self):
|
||||
context = self.get_context()
|
||||
sheerka = context.sheerka
|
||||
|
||||
return_values = [
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name", False, "Not relevant"),
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name2", False, "Not relevant"),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "Python", True, "value"),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "other", True, "value"),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "Python", True, Concept(name="2", body="value").auto_init()),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "Concept", True, Concept(name="1", body="value").auto_init()),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "Python", True, Concept(name="3", body="value").auto_init()),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "other", True, "value"),
|
||||
ReturnValueConcept("some_name", True, sheerka.new(BuiltinConcepts.REDUCE_REQUESTED))
|
||||
]
|
||||
|
||||
evaluator = MultipleSameSuccessEvaluator()
|
||||
assert evaluator.matches(context, return_values)
|
||||
|
||||
evaluated = evaluator.eval(context, return_values)
|
||||
assert evaluated.status
|
||||
assert evaluated.value == Concept(name="1", body="value").auto_init() # the concept is returned, not the value
|
||||
|
||||
def test_i_can_match_and_eval_when_at_least_one_value_is_a_concept_python_evaluator_first(self):
|
||||
context = self.get_context()
|
||||
sheerka = context.sheerka
|
||||
|
||||
return_values = [
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name", False, "Not relevant"),
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name2", False, "Not relevant"),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "Python", True, "value"),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "other", True, "value"),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "other", True, Concept(name="2", body="value").auto_init()),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "Python", True, Concept(name="1", body="value").auto_init()),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "other", True, Concept(name="3", body="value").auto_init()),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "other", True, "value"),
|
||||
ReturnValueConcept("some_name", True, sheerka.new(BuiltinConcepts.REDUCE_REQUESTED))
|
||||
]
|
||||
|
||||
evaluator = MultipleSameSuccessEvaluator()
|
||||
assert evaluator.matches(context, return_values)
|
||||
|
||||
evaluated = evaluator.eval(context, return_values)
|
||||
assert evaluated.status
|
||||
assert evaluated.value == Concept(name="1", body="value").auto_init() # the concept is returned, not the value
|
||||
|
||||
def test_i_can_match_even_if_the_value_are_not_the_same_but_eval_will_fail(self):
|
||||
context = self.get_context()
|
||||
sheerka = context.sheerka
|
||||
|
||||
return_values = [
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name", False, "Not relevant"),
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name2", False, "Not relevant"),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="1", body="value").auto_init()),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="2", body="value2").auto_init()),
|
||||
ReturnValueConcept("some_name", True, sheerka.new(BuiltinConcepts.REDUCE_REQUESTED))
|
||||
]
|
||||
|
||||
evaluator = MultipleSameSuccessEvaluator()
|
||||
assert evaluator.matches(context, return_values)
|
||||
assert evaluator.eval(context, return_values) is None
|
||||
|
||||
def test_i_can_match_even_if_the_value_are_not_the_same_but_eval_will_fail_when_no_body(self):
|
||||
context = self.get_context()
|
||||
sheerka = context.sheerka
|
||||
|
||||
return_values = [
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name", False, "Not relevant"),
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name2", False, "Not relevant"),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="1").auto_init()),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="2").auto_init()),
|
||||
ReturnValueConcept("some_name", True, sheerka.new(BuiltinConcepts.REDUCE_REQUESTED))
|
||||
]
|
||||
|
||||
evaluator = MultipleSameSuccessEvaluator()
|
||||
assert evaluator.matches(context, return_values)
|
||||
assert evaluator.eval(context, return_values) is None
|
||||
|
||||
def test_i_can_match_if_no_parser(self):
|
||||
context = self.get_context()
|
||||
sheerka = context.sheerka
|
||||
|
||||
return_values = [
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="1", body="value").auto_init()),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="2", body="value").auto_init()),
|
||||
ReturnValueConcept("some_name", True, sheerka.new(BuiltinConcepts.REDUCE_REQUESTED))
|
||||
]
|
||||
|
||||
assert MultipleSameSuccessEvaluator().matches(context, return_values)
|
||||
|
||||
def test_i_cannot_match_if_not_reduced_requested(self):
|
||||
context = self.get_context()
|
||||
sheerka = context.sheerka
|
||||
|
||||
return_values = [
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name", False, "Not relevant"),
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name2", False, "Not relevant"),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="1", body="value").auto_init()),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="2", body="value").auto_init()),
|
||||
ReturnValueConcept("some_name", True, Concept(name="2", body="value")),
|
||||
# ReturnValueConcept("some_name", True, sheerka.new(BuiltinConcepts.REDUCE_REQUESTED))
|
||||
]
|
||||
|
||||
assert not MultipleSameSuccessEvaluator().matches(context, return_values)
|
||||
|
||||
def test_i_cannot_match_if_only_one_successful_evaluator(self):
|
||||
context = self.get_context()
|
||||
sheerka = context.sheerka
|
||||
|
||||
return_values = [
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name", False, "Not relevant"),
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name2", False, "Not relevant"),
|
||||
# ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="1", body="value")),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="2", body="value").auto_init()),
|
||||
ReturnValueConcept("some_name", True, Concept(name="2", body="value")),
|
||||
ReturnValueConcept("some_name", True, sheerka.new(BuiltinConcepts.REDUCE_REQUESTED))
|
||||
]
|
||||
|
||||
assert not MultipleSameSuccessEvaluator().matches(context, return_values)
|
||||
|
||||
def test_i_cannot_match_if_at_least_one_parser_is_successful(self):
|
||||
context = self.get_context()
|
||||
sheerka = context.sheerka
|
||||
|
||||
return_values = [
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name", False, "Not relevant"),
|
||||
ReturnValueConcept(BaseParser.PREFIX + "some_name2", True, "Not relevant"),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="1", body="value").auto_init()),
|
||||
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="2", body="value").auto_init()),
|
||||
ReturnValueConcept("some_name", True, sheerka.new(BuiltinConcepts.REDUCE_REQUESTED))
|
||||
]
|
||||
|
||||
assert not MultipleSameSuccessEvaluator().matches(context, return_values)
|
||||
@@ -0,0 +1,73 @@
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import ReturnValueConcept, BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from evaluators.OneErrorEvaluator import OneErrorEvaluator
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
def r(value, status=True):
|
||||
return ReturnValueConcept(value, status, value)
|
||||
|
||||
|
||||
reduce_requested = ReturnValueConcept("some_name", True, Concept(key=BuiltinConcepts.REDUCE_REQUESTED))
|
||||
|
||||
|
||||
class TestOneErrorEvaluator(TestUsingMemoryBasedSheerka):
|
||||
@pytest.mark.parametrize("return_values, expected", [
|
||||
([r("evaluators.one error", False), reduce_requested], True),
|
||||
([r("evaluators.one error", False), r("failed", False), r("failed", False), reduce_requested], True),
|
||||
([r("evaluators.error", False), r("not a parser in success"), reduce_requested], True),
|
||||
([r("evaluators.no reduce required", False), r("failed", False), r("failed", False)], False),
|
||||
([r("evaluators.no reduce required", False)], False),
|
||||
([r("evaluators.error", False), r("evaluators.success"), reduce_requested], False),
|
||||
([r("evaluators.error", False), r("parsers.success"), reduce_requested], False),
|
||||
([r("evaluators.success"), r("not an evaluator in error", False), reduce_requested], False),
|
||||
([r("evaluators.error", False), r("evaluators.another error", False), reduce_requested], False),
|
||||
])
|
||||
def test_i_can_match(self, return_values, expected):
|
||||
context = self.get_context()
|
||||
assert OneErrorEvaluator().matches(context, return_values) == expected
|
||||
|
||||
def test_i_can_eval(self):
|
||||
context = self.get_context()
|
||||
|
||||
return_values = [
|
||||
r("evaluators.one error", False),
|
||||
r("parsers.failed", False),
|
||||
r("parsers.failed", False),
|
||||
reduce_requested
|
||||
]
|
||||
|
||||
evaluator = OneErrorEvaluator()
|
||||
evaluator.matches(context, return_values)
|
||||
res = evaluator.eval(context, return_values)
|
||||
|
||||
assert not res.status
|
||||
assert res.body == "evaluators.one error"
|
||||
assert len(res.parents) == 4
|
||||
|
||||
def test_unwanted_return_values_are_not_eaten(self):
|
||||
context = self.get_context()
|
||||
|
||||
a_successful_concept = r("successful concept")
|
||||
a_concept_in_error = r("concept in error", False)
|
||||
return_values = [
|
||||
r("evaluators.one error", False),
|
||||
r("parsers.failed", False),
|
||||
r("parsers.failed", False),
|
||||
a_successful_concept,
|
||||
a_concept_in_error,
|
||||
reduce_requested
|
||||
]
|
||||
|
||||
evaluator = OneErrorEvaluator()
|
||||
evaluator.matches(context, return_values)
|
||||
res = evaluator.eval(context, return_values)
|
||||
|
||||
assert not res.status
|
||||
assert res.body == "evaluators.one error"
|
||||
assert len(res.parents) == 4
|
||||
|
||||
assert a_successful_concept not in res.parents
|
||||
@@ -0,0 +1,72 @@
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import ReturnValueConcept, BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from evaluators.OneSuccessEvaluator import OneSuccessEvaluator
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
def r(value, status=True):
|
||||
return ReturnValueConcept(value, status, value)
|
||||
|
||||
|
||||
reduce_requested = ReturnValueConcept("some_name", True, Concept(key=BuiltinConcepts.REDUCE_REQUESTED))
|
||||
|
||||
|
||||
class TestOneSuccessEvaluator(TestUsingMemoryBasedSheerka):
|
||||
|
||||
@pytest.mark.parametrize("return_values, expected", [
|
||||
([r("evaluators.one success"), reduce_requested], True),
|
||||
([r("evaluators.one success"), r("failed", False), r("failed", False), reduce_requested], True),
|
||||
([r("evaluators.no reduce required"), r("failed", False), r("failed", False)], False),
|
||||
([r("evaluators.no reduce required")], False),
|
||||
([r("evaluators.failed", False), r("failed", False), r("failed", False), reduce_requested], False),
|
||||
([r("evaluators.failed", False), r("not evaluator success"), reduce_requested], False),
|
||||
([r("evaluators.success"), r("evaluators.another success"), reduce_requested], False),
|
||||
([r("evaluators.one success"), r("other success"), r("failed", False), reduce_requested], True),
|
||||
([r("evaluators.one success"), r("parsers.success"), reduce_requested], False),
|
||||
])
|
||||
def test_i_can_match(self, return_values, expected):
|
||||
context = self.get_context()
|
||||
assert OneSuccessEvaluator().matches(context, return_values) == expected
|
||||
|
||||
def test_i_can_eval(self):
|
||||
context = self.get_context()
|
||||
|
||||
return_values = [
|
||||
r("evaluators.one success"),
|
||||
r("failed", False),
|
||||
r("failed", False),
|
||||
reduce_requested
|
||||
]
|
||||
|
||||
evaluator = OneSuccessEvaluator()
|
||||
matches = evaluator.matches(context, return_values)
|
||||
res = evaluator.eval(context, return_values)
|
||||
|
||||
assert matches
|
||||
assert res.status
|
||||
assert res.body == "evaluators.one success"
|
||||
assert len(res.parents) == 4
|
||||
|
||||
def test_i_do_not_eat_the_other_success(self):
|
||||
context = self.get_context()
|
||||
|
||||
not_a_parser_success = r("other success")
|
||||
return_values = [
|
||||
r("evaluators.one success"),
|
||||
not_a_parser_success,
|
||||
r("failed", False),
|
||||
reduce_requested
|
||||
]
|
||||
|
||||
evaluator = OneSuccessEvaluator()
|
||||
matches = evaluator.matches(context, return_values)
|
||||
res = evaluator.eval(context, return_values)
|
||||
|
||||
assert matches
|
||||
assert res.status
|
||||
assert res.body == "evaluators.one success"
|
||||
assert len(res.parents) == 3
|
||||
|
||||
assert not_a_parser_success not in res.parents
|
||||
@@ -0,0 +1,49 @@
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept, UserInputConcept
|
||||
from core.concept import Concept
|
||||
from evaluators.PrepareEvalEvaluator import PrepareEvalEvaluator
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
r = ReturnValueConcept
|
||||
|
||||
|
||||
class TestPrepareEvalEvaluator(TestUsingMemoryBasedSheerka):
|
||||
|
||||
@pytest.mark.parametrize("ret_val, expected", [
|
||||
(r("name", True, UserInputConcept("eval 1 + 1")), True),
|
||||
(r("name", True, UserInputConcept(" eval 1 + 1")), True),
|
||||
(r("name", True, UserInputConcept("eval")), False),
|
||||
(r("name", True, UserInputConcept("1+1")), False),
|
||||
(r("name", True, UserInputConcept("")), False),
|
||||
(r("name", True, UserInputConcept("eval ")), False),
|
||||
(r("name", True, UserInputConcept([])), False),
|
||||
(r("name", True, Concept("foo")), False),
|
||||
(r("name", True, "not a concept"), False),
|
||||
(r("name", False, UserInputConcept("eval 1 + 1")), False),
|
||||
(r("name", False, UserInputConcept(" eval 1 + 1")), False),
|
||||
])
|
||||
def test_i_can_match(self, ret_val, expected):
|
||||
context = self.get_context()
|
||||
assert PrepareEvalEvaluator().matches(context, ret_val) == expected
|
||||
|
||||
@pytest.mark.parametrize("ret_val, expected", [
|
||||
(r("name", True, UserInputConcept("eval 1 + 1")), "1 + 1"),
|
||||
(r("name", True, UserInputConcept(" eval 1 + 1")), "1 + 1"),
|
||||
])
|
||||
def test_i_can_eval(self, ret_val, expected):
|
||||
context = self.get_context()
|
||||
sheerka = context.sheerka
|
||||
|
||||
prepare_evaluator = PrepareEvalEvaluator()
|
||||
prepare_evaluator.matches(context, ret_val)
|
||||
res = prepare_evaluator.eval(context, ret_val)
|
||||
|
||||
assert len(res) == 2
|
||||
assert res[0].status
|
||||
assert sheerka.isinstance(res[0].body, BuiltinConcepts.USER_INPUT)
|
||||
assert res[0].body.body == expected
|
||||
|
||||
assert res[1].status
|
||||
assert sheerka.isinstance(res[1].body, BuiltinConcepts.CONCEPT_EVAL_REQUESTED)
|
||||
@@ -0,0 +1,138 @@
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from evaluators.PythonEvaluator import PythonEvaluator
|
||||
from parsers.PythonParser import PythonNode, PythonParser
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
def get_context_name(context):
|
||||
return context.name
|
||||
|
||||
|
||||
class TestPythonEvaluator(TestUsingMemoryBasedSheerka):
|
||||
|
||||
@pytest.mark.parametrize("ret_val, expected", [
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept(value=PythonNode("", None))), True),
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept(value="other thing")), False),
|
||||
(ReturnValueConcept("some_name", False, "not relevant"), False),
|
||||
(ReturnValueConcept("some_name", True, Concept()), False)
|
||||
])
|
||||
def test_i_can_match(self, ret_val, expected):
|
||||
context = self.get_context()
|
||||
assert PythonEvaluator().matches(context, ret_val) == expected
|
||||
|
||||
@pytest.mark.parametrize("text, expected", [
|
||||
("1 + 1", 2),
|
||||
("sheerka.test()", "I have access to Sheerka !"),
|
||||
("a=10\na", 10),
|
||||
])
|
||||
def test_i_can_eval(self, text, expected):
|
||||
context = self.get_context()
|
||||
parsed = PythonParser().parse(context, text)
|
||||
|
||||
evaluated = PythonEvaluator().eval(context, parsed)
|
||||
|
||||
assert evaluated.status
|
||||
assert evaluated.value == expected
|
||||
|
||||
@pytest.mark.parametrize("concept", [
|
||||
Concept("foo"),
|
||||
Concept("foo", body="2"),
|
||||
Concept("foo").set_prop("prop", "'a'"),
|
||||
Concept("foo", body="bar")
|
||||
])
|
||||
def test_i_cannot_eval_simple_concept(self, concept):
|
||||
context = self.get_context()
|
||||
context.sheerka.add_in_cache(Concept("foo"))
|
||||
|
||||
parsed = PythonParser().parse(context, "foo")
|
||||
evaluated = PythonEvaluator().eval(context, parsed)
|
||||
|
||||
assert not evaluated.status
|
||||
assert context.sheerka.isinstance(evaluated.value, BuiltinConcepts.NOT_FOR_ME)
|
||||
|
||||
def test_i_can_eval_expression_with_that_references_concepts(self):
|
||||
"""
|
||||
I can test modules with variables
|
||||
:return:
|
||||
"""
|
||||
context = self.get_context()
|
||||
context.sheerka.add_in_cache(Concept("foo", body="1"))
|
||||
|
||||
parsed = PythonParser().parse(context, "foo + 2")
|
||||
evaluated = PythonEvaluator().eval(context, parsed)
|
||||
|
||||
assert evaluated.status
|
||||
assert evaluated.value == 3
|
||||
|
||||
def test_i_can_eval_module_with_that_references_concepts(self):
|
||||
"""
|
||||
I can test modules with variables
|
||||
:return:
|
||||
"""
|
||||
context = self.get_context()
|
||||
context.sheerka.add_in_cache(Concept("foo"))
|
||||
|
||||
parsed = PythonParser().parse(context, "def a(b):\n return b\na(c:foo:)")
|
||||
evaluated = PythonEvaluator().eval(context, parsed)
|
||||
|
||||
assert evaluated.status
|
||||
assert evaluated.value == Concept("foo").init_key()
|
||||
|
||||
def test_i_can_eval_module_with_that_references_concepts_with_body(self):
|
||||
"""
|
||||
I can test modules with variables
|
||||
:return:
|
||||
"""
|
||||
context = self.get_context()
|
||||
context.sheerka.add_in_cache(Concept("foo", body="2"))
|
||||
|
||||
parsed = PythonParser().parse(context, "def a(b):\n return b\na(foo)")
|
||||
evaluated = PythonEvaluator().eval(context, parsed)
|
||||
|
||||
assert evaluated.status
|
||||
assert evaluated.value == 2
|
||||
|
||||
def test_i_can_eval_concept_token(self):
|
||||
context = self.get_context()
|
||||
context.sheerka.add_in_cache(Concept("foo", body="2"))
|
||||
|
||||
parsed = PythonParser().parse(context, "get_context_name(c:foo:)")
|
||||
python_evaluator = PythonEvaluator()
|
||||
python_evaluator.locals["get_context_name"] = get_context_name
|
||||
evaluated = python_evaluator.eval(context, parsed)
|
||||
|
||||
assert evaluated.status
|
||||
assert evaluated.value == "foo"
|
||||
|
||||
# sanity, does not work otherwise
|
||||
parsed = PythonParser().parse(context, "get_context_name(foo)")
|
||||
python_evaluator = PythonEvaluator()
|
||||
python_evaluator.locals["get_context_name"] = get_context_name
|
||||
evaluated = python_evaluator.eval(context, parsed)
|
||||
|
||||
assert not evaluated.status
|
||||
assert evaluated.body.body.args[0] == "'int' object has no attribute 'name'"
|
||||
|
||||
@pytest.mark.parametrize("text, concept_key, concept_id, use_concept", [
|
||||
("__C__key__C__", "key", None, False),
|
||||
("__C__key__id__C__", "key", "id", False),
|
||||
("__C__USE_CONCEPT__key__id__C__", "key", "id", True),
|
||||
("__C__USE_CONCEPT__key__id__C__", "key", "id", True),
|
||||
])
|
||||
def test_i_can_resolve_name(self, text, concept_key, concept_id, use_concept):
|
||||
context = self.get_context()
|
||||
assert PythonEvaluator().resolve_name(context, text) == (concept_key, concept_id, use_concept)
|
||||
|
||||
@pytest.mark.parametrize("text", [
|
||||
"__C__",
|
||||
"__C__key",
|
||||
"__C__key____",
|
||||
"__C____",
|
||||
"__C__USE_CONCEPT__",
|
||||
])
|
||||
def test_i_cannot_resolve_name(self, text):
|
||||
context = self.get_context()
|
||||
assert PythonEvaluator().resolve_name(context, text) is None
|
||||
@@ -0,0 +1,105 @@
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import ReturnValueConcept, BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from evaluators.TooManySuccessEvaluator import TooManySuccessEvaluator
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
def r(name, status=True, value=None):
|
||||
value = value or name
|
||||
return ReturnValueConcept(name, status, value)
|
||||
|
||||
|
||||
reduce_requested = ReturnValueConcept("some_name", True, Concept(key=BuiltinConcepts.REDUCE_REQUESTED))
|
||||
|
||||
|
||||
class TestTooManySuccessEvaluator(TestUsingMemoryBasedSheerka):
|
||||
|
||||
@pytest.mark.parametrize("return_values, expected", [
|
||||
([r("evaluators.success1"), r("evaluators.success2"), reduce_requested], True),
|
||||
([r("evaluators.success1"), r("evaluators.success2"), r("other"), reduce_requested], True),
|
||||
([r("evaluators.success1"), r("evaluators.success2"), r("other", False), reduce_requested], True),
|
||||
([r("evaluators.a", value=Concept("c1", body="1")),
|
||||
r("evaluators.a", value=Concept("c2", body="1")),
|
||||
r("other"),
|
||||
reduce_requested], True),
|
||||
([r("evaluators.a", value=Concept("c1", body="1")),
|
||||
r("evaluators.a", value=Concept("c2", body="1")),
|
||||
r("parsers.other", False),
|
||||
reduce_requested], True),
|
||||
([r("evaluators.a", value=Concept("c1", body="1")),
|
||||
r("evaluators.a", value=Concept("c2", body="1")),
|
||||
r("parsers.other"),
|
||||
reduce_requested], False),
|
||||
([r("evaluators.success1"), reduce_requested], False),
|
||||
([reduce_requested], False),
|
||||
([r("evaluators.success1"), r("evaluators.success2")], False),
|
||||
])
|
||||
def test_i_can_match(self, return_values, expected):
|
||||
context = self.get_context()
|
||||
assert TooManySuccessEvaluator().matches(context, return_values) == expected
|
||||
|
||||
def test_i_can_eval(self):
|
||||
context = self.get_context()
|
||||
|
||||
value1 = r("evaluators.a", value=Concept("c1", body="1"))
|
||||
value2 = r("evaluators.a", value=Concept("c2", body="2"))
|
||||
return_values = [
|
||||
value1,
|
||||
value2,
|
||||
r("other", False),
|
||||
reduce_requested
|
||||
]
|
||||
|
||||
evaluator = TooManySuccessEvaluator()
|
||||
matches = evaluator.matches(context, return_values)
|
||||
res = evaluator.eval(context, return_values)
|
||||
|
||||
assert matches
|
||||
assert not res.status
|
||||
assert context.sheerka.isinstance(res.body, BuiltinConcepts.TOO_MANY_SUCCESS)
|
||||
assert res.body.body == [value1, value2]
|
||||
assert len(res.parents) == 4
|
||||
|
||||
def test_i_can_eval_when_same_success(self):
|
||||
context = self.get_context()
|
||||
|
||||
return_values = [
|
||||
r("evaluators.a", value=Concept("c1", body="1").auto_init()),
|
||||
r("evaluators.a", value=Concept("c2", body="1").auto_init()),
|
||||
r("other", False),
|
||||
reduce_requested
|
||||
]
|
||||
|
||||
evaluator = TooManySuccessEvaluator()
|
||||
matches = evaluator.matches(context, return_values)
|
||||
res = evaluator.eval(context, return_values)
|
||||
|
||||
assert matches
|
||||
assert res is None
|
||||
|
||||
def test_other_success_are_not_reduced(self):
|
||||
context = self.get_context()
|
||||
|
||||
value1 = r("evaluators.a", value=Concept("c1", body="1").auto_init())
|
||||
value2 = r("evaluators.a", value=Concept("c2", body="2").auto_init())
|
||||
other_success = r("other")
|
||||
return_values = [
|
||||
value1,
|
||||
value2,
|
||||
other_success,
|
||||
reduce_requested
|
||||
]
|
||||
|
||||
evaluator = TooManySuccessEvaluator()
|
||||
matches = evaluator.matches(context, return_values)
|
||||
res = evaluator.eval(context, return_values)
|
||||
|
||||
assert matches
|
||||
assert not res.status
|
||||
assert context.sheerka.isinstance(res.body, BuiltinConcepts.TOO_MANY_SUCCESS)
|
||||
assert res.body.body == [value1, value2]
|
||||
assert len(res.parents) == 3
|
||||
assert other_success not in res.parents
|
||||
@@ -0,0 +1,625 @@
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import Concept, PROPERTIES_TO_SERIALIZE, Property, simplec
|
||||
from evaluators.MutipleSameSuccessEvaluator import MultipleSameSuccessEvaluator
|
||||
from parsers.ConceptLexerParser import Sequence, StrMatch, OrderedChoice, Optional, ConceptExpression
|
||||
from sdp.sheerkaDataProvider import SheerkaDataProvider
|
||||
|
||||
from tests.TestUsingFileBasedSheerka import TestUsingFileBasedSheerka
|
||||
|
||||
|
||||
class TestSheerkaNonReg(TestUsingFileBasedSheerka):
|
||||
|
||||
@pytest.mark.parametrize("text, expected", [
|
||||
("1 + 1", 2),
|
||||
("sheerka.test()", 'I have access to Sheerka !')
|
||||
])
|
||||
def test_i_can_eval_python_expressions_with_no_variable(self, text, expected):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
res = sheerka.evaluate_user_input(text)
|
||||
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].value == expected
|
||||
|
||||
def test_i_can_eval_concept_with_python_body(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept = Concept(name="one", body="1")
|
||||
sheerka.add_in_cache(concept)
|
||||
|
||||
text = "one"
|
||||
res = sheerka.evaluate_user_input(text)
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].value == simplec("one", 1) # by default, the concept is returned
|
||||
|
||||
def test_i_can_eval_concept_with_concept_body(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept_one = Concept(name="one")
|
||||
concept_un = Concept(name="un", body="one")
|
||||
sheerka.add_in_cache(concept_one)
|
||||
sheerka.add_in_cache(concept_un)
|
||||
|
||||
res = sheerka.evaluate_user_input("un")
|
||||
return_value = res[0].value
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert return_value == simplec("un", simplec("one", None))
|
||||
|
||||
def test_i_can_eval_concept_with_no_body(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept = Concept(name="one")
|
||||
sheerka.add_in_cache(concept)
|
||||
|
||||
text = "one"
|
||||
res = sheerka.evaluate_user_input(text)
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].value == concept
|
||||
assert id(res[0].value) != id(concept)
|
||||
|
||||
def test_is_unique_property_is_used_when_evaluating(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept = Concept(name="one", is_unique=True)
|
||||
sheerka.add_in_cache(concept)
|
||||
|
||||
text = "one"
|
||||
res = sheerka.evaluate_user_input(text)
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].value == concept
|
||||
assert id(res[0].value) == id(concept)
|
||||
|
||||
def test_i_can_eval_def_concept_request(self):
|
||||
text = """
|
||||
def concept a + b
|
||||
where isinstance(a, int) and isinstance(b, int)
|
||||
pre isinstance(a, int) and isinstance(b, int)
|
||||
post isinstance(res, int)
|
||||
as:
|
||||
def func(x,y):
|
||||
return x+y
|
||||
func(a,b)
|
||||
"""
|
||||
|
||||
expected = self.get_default_concept()
|
||||
expected.metadata.id = "1001"
|
||||
expected.metadata.desc = None
|
||||
expected.metadata.props = [("a", None), ("b", None)]
|
||||
expected.init_key()
|
||||
|
||||
sheerka = self.get_sheerka()
|
||||
res = sheerka.evaluate_user_input(text)
|
||||
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert sheerka.isinstance(res[0].value, BuiltinConcepts.NEW_CONCEPT)
|
||||
|
||||
concept_saved = res[0].value.body
|
||||
|
||||
for prop in PROPERTIES_TO_SERIALIZE:
|
||||
assert getattr(concept_saved.metadata, prop) == getattr(expected.metadata, prop)
|
||||
|
||||
assert concept_saved.key in sheerka.cache_by_key
|
||||
assert concept_saved.id in sheerka.cache_by_id
|
||||
assert sheerka.sdp.io.exists(
|
||||
sheerka.sdp.io.get_obj_path(SheerkaDataProvider.ObjectsFolder, concept_saved.get_digest()))
|
||||
|
||||
def test_i_can_eval_def_concept_part_when_one_part_is_a_ref_of_another_concept(self):
|
||||
"""
|
||||
In this test, we test that the properties of 'concept a xx b' (which are 'a' and 'b')
|
||||
are correctly detected, thanks to the source code 'a plus b' in its body
|
||||
:return:
|
||||
"""
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
# concept 'a plus b' is known
|
||||
concept_a_plus_b = Concept(name="a plus b").def_prop("a").def_prop("b")
|
||||
sheerka.add_in_cache(concept_a_plus_b)
|
||||
|
||||
res = sheerka.evaluate_user_input("def concept a xx b as a plus b")
|
||||
expected = Concept(name="a xx b", body="a plus b").def_prop("a").def_prop("b").init_key()
|
||||
expected.metadata.id = "1001"
|
||||
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert sheerka.isinstance(res[0].value, BuiltinConcepts.NEW_CONCEPT)
|
||||
|
||||
concept_saved = res[0].value.body
|
||||
|
||||
for prop in PROPERTIES_TO_SERIALIZE:
|
||||
assert getattr(concept_saved.metadata, prop) == getattr(expected.metadata, prop)
|
||||
|
||||
assert concept_saved.key in sheerka.cache_by_key
|
||||
assert concept_saved.id in sheerka.cache_by_id
|
||||
assert sheerka.sdp.io.exists(
|
||||
sheerka.sdp.io.get_obj_path(SheerkaDataProvider.ObjectsFolder, concept_saved.get_digest()))
|
||||
|
||||
def test_i_cannot_eval_the_same_def_concept_twice(self):
|
||||
text = """
|
||||
def concept a + b
|
||||
where isinstance(a, int) and isinstance(b, int)
|
||||
pre isinstance(a, int) and isinstance(b, int)
|
||||
post isinstance(res, int)
|
||||
as:
|
||||
def func(x,y):
|
||||
return x+y
|
||||
func(a,b)
|
||||
"""
|
||||
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.evaluate_user_input(text)
|
||||
res = sheerka.evaluate_user_input(text)
|
||||
|
||||
assert len(res) == 1
|
||||
assert not res[0].status
|
||||
assert sheerka.isinstance(res[0].value, BuiltinConcepts.CONCEPT_ALREADY_DEFINED)
|
||||
|
||||
@pytest.mark.parametrize("text", [
|
||||
"",
|
||||
" ",
|
||||
"\n",
|
||||
])
|
||||
def test_i_can_eval_a_empty_input(self, text):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
res = sheerka.evaluate_user_input(text)
|
||||
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert sheerka.isinstance(res[0].value, BuiltinConcepts.NOP)
|
||||
|
||||
def test_i_can_eval_concept_with_variable(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept_hello = Concept(name="hello a").def_prop("a")
|
||||
concept_foo = Concept(name="foo")
|
||||
sheerka.add_in_cache(concept_hello)
|
||||
sheerka.add_in_cache(concept_foo)
|
||||
|
||||
res = sheerka.evaluate_user_input("hello foo")
|
||||
return_value = res[0].value
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert sheerka.isinstance(return_value, concept_hello)
|
||||
assert return_value.props["a"].value == concept_foo
|
||||
|
||||
def test_i_can_eval_concept_with_variable_and_python_as_body(self):
|
||||
sheerka = self.get_sheerka()
|
||||
hello_a = sheerka.add_in_cache(Concept(name="hello a", body="'hello ' + a").def_prop("a"))
|
||||
sheerka.add_in_cache(Concept(name="foo", body="'foo'"))
|
||||
|
||||
res = sheerka.evaluate_user_input("hello foo")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert sheerka.isinstance(res[0].value, hello_a)
|
||||
assert res[0].value.body == "hello foo"
|
||||
assert res[0].value.metadata.is_evaluated
|
||||
assert res[0].value.props["a"].value == simplec("foo", "foo")
|
||||
assert res[0].value.props["a"].value.metadata.is_evaluated
|
||||
|
||||
def test_i_can_eval_duplicate_concepts_with_same_value(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
sheerka.add_in_cache(Concept(name="hello a", body="'hello ' + a").def_prop("a"))
|
||||
sheerka.add_in_cache(Concept(name="hello foo", body="'hello foo'"))
|
||||
sheerka.add_in_cache(Concept(name="foo", body="'foo'"))
|
||||
|
||||
res = sheerka.evaluate_user_input("hello foo")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].value.body == "hello foo"
|
||||
assert res[0].who == sheerka.get_evaluator_name(MultipleSameSuccessEvaluator.NAME)
|
||||
|
||||
def test_i_cannot_manage_duplicate_concepts_when_the_values_are_different(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
sheerka.add_in_cache(Concept(name="hello a", body="'hello ' + a").def_prop("a"))
|
||||
sheerka.add_in_cache(Concept(name="hello foo", body="'hello foo'"))
|
||||
sheerka.add_in_cache(Concept(name="foo", body="'another value'"))
|
||||
|
||||
res = sheerka.evaluate_user_input("hello foo")
|
||||
assert len(res) == 1
|
||||
assert not res[0].status
|
||||
assert sheerka.isinstance(res[0].value, BuiltinConcepts.TOO_MANY_SUCCESS)
|
||||
|
||||
concepts = res[0].value.body
|
||||
assert len(concepts) == 2
|
||||
sorted_values = sorted(concepts, key=lambda x: x.value.body)
|
||||
assert sorted_values[0].value.body == "hello another value"
|
||||
assert sorted_values[1].value.body == "hello foo"
|
||||
|
||||
def test_i_can_manage_concepts_with_the_same_key_when_values_are_the_same(self):
|
||||
sheerka = self.get_sheerka()
|
||||
context = self.get_context(sheerka)
|
||||
|
||||
sheerka.create_new_concept(context, Concept(name="hello a", body="'hello ' + a").def_prop("a"))
|
||||
sheerka.create_new_concept(context, Concept(name="hello b", body="'hello ' + b").def_prop("b"))
|
||||
|
||||
res = sheerka.evaluate_user_input("hello 'foo'")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].value.body == "hello foo" # I don't know yet the one to choose
|
||||
assert res[0].who == sheerka.get_evaluator_name(MultipleSameSuccessEvaluator.NAME)
|
||||
|
||||
def test_i_can_create_concepts_with_python_code_as_body(self):
|
||||
sheerka = self.get_sheerka()
|
||||
context = self.get_context(sheerka)
|
||||
|
||||
sheerka.create_new_concept(context, Concept(name="concepts", body="sheerka.concepts()"))
|
||||
res = sheerka.evaluate_user_input("concepts")
|
||||
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert isinstance(res[0].value.body, list)
|
||||
|
||||
def test_i_can_create_concept_with_bnf_definition(self):
|
||||
sheerka = self.get_sheerka(False, False)
|
||||
a = Concept("a")
|
||||
sheerka.add_in_cache(a)
|
||||
sheerka.concepts_definition_cache = {a: OrderedChoice("one", "two")}
|
||||
|
||||
res = sheerka.evaluate_user_input("def concept plus from bnf a ('plus' plus)?")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert sheerka.isinstance(res[0].value, BuiltinConcepts.NEW_CONCEPT)
|
||||
|
||||
saved_concept = sheerka.sdp.get_safe(sheerka.CONCEPTS_ENTRY, "plus")
|
||||
assert saved_concept.key == "plus"
|
||||
assert saved_concept.metadata.definition == "a ('plus' plus)?"
|
||||
assert "a" in saved_concept.props
|
||||
assert "plus" in saved_concept.props
|
||||
|
||||
saved_definitions = sheerka.sdp.get_safe(sheerka.CONCEPTS_DEFINITIONS_ENTRY)
|
||||
expected_bnf = Sequence(
|
||||
a, Optional(Sequence(StrMatch("plus"), ConceptExpression("plus", rule_name="plus"))))
|
||||
assert saved_definitions[saved_concept] == expected_bnf
|
||||
|
||||
new_concept = res[0].value.body
|
||||
assert new_concept.metadata.name == "plus"
|
||||
assert new_concept.metadata.definition == "a ('plus' plus)?"
|
||||
assert new_concept.bnf == expected_bnf
|
||||
assert "a" in new_concept.props
|
||||
assert "plus" in new_concept.props
|
||||
|
||||
def test_i_can_eval_bnf_definitions(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept_a = sheerka.evaluate_user_input("def concept a from bnf 'one' | 'two'")[0].body.body
|
||||
|
||||
res = sheerka.evaluate_user_input("one")
|
||||
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert sheerka.isinstance(res[0].value, concept_a)
|
||||
|
||||
def test_i_can_eval_bnf_definitions_with_variables(self):
|
||||
sheerka = self.get_sheerka()
|
||||
concept_a = sheerka.evaluate_user_input("def concept a from bnf 'one' | 'two'")[0].body.body
|
||||
concept_b = sheerka.evaluate_user_input("def concept b from bnf a 'three'")[0].body.body
|
||||
|
||||
res = sheerka.evaluate_user_input("one three")
|
||||
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
return_value = res[0].value
|
||||
|
||||
assert sheerka.isinstance(return_value, concept_b)
|
||||
assert return_value.body == "one three"
|
||||
assert return_value.metadata.is_evaluated
|
||||
|
||||
assert return_value.props["a"] == Property("a", sheerka.new(concept_a.key, body="one").init_key())
|
||||
assert return_value.props["a"].value.metadata.is_evaluated
|
||||
|
||||
def test_i_can_eval_bnf_definitions_from_separate_instances(self):
|
||||
"""
|
||||
Same test then before,
|
||||
but make sure that the BNF are correctly persisted and loaded
|
||||
"""
|
||||
sheerka = self.get_sheerka(False)
|
||||
concept_a = sheerka.evaluate_user_input("def concept a from bnf 'one' 'two'")[0].body.body
|
||||
|
||||
res = self.get_sheerka(False).evaluate_user_input("one two")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert sheerka.isinstance(res[0].value, concept_a)
|
||||
|
||||
# add another bnf definition
|
||||
concept_b = sheerka.evaluate_user_input("def concept b from bnf a 'three'")[0].body.body
|
||||
|
||||
res = self.get_sheerka(False).evaluate_user_input("one two") # previous one still works
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert sheerka.isinstance(res[0].value, concept_a)
|
||||
|
||||
res = self.get_sheerka(False).evaluate_user_input("one two three") # new one works
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert sheerka.isinstance(res[0].value, concept_b)
|
||||
assert res[0].value.body == "one two three"
|
||||
assert res[0].value.props["a"] == Property("a", sheerka.new(concept_a.key, body="one two").init_key())
|
||||
|
||||
@pytest.mark.parametrize("desc, definitions", [
|
||||
("Simple form", [
|
||||
"def concept one as 1",
|
||||
"def concept two as 2",
|
||||
"def concept twenties from bnf 'twenty' (one|two)=unit as 20 + unit"
|
||||
]),
|
||||
("When twenty is a concept", [
|
||||
"def concept one as 1",
|
||||
"def concept two as 2",
|
||||
"def concept twenty as 20",
|
||||
"def concept twenties from bnf twenty (one|two)=unit as twenty + unit"
|
||||
]),
|
||||
("When digit is a concept", [
|
||||
"def concept one as 1",
|
||||
"def concept two as 2",
|
||||
"def concept twenty as 20",
|
||||
"def concept digit from bnf one|two",
|
||||
"def concept twenties from bnf twenty digit as twenty + digit"
|
||||
]),
|
||||
("When using isa and concept twenty", [
|
||||
"def concept one as 1",
|
||||
"def concept two as 2",
|
||||
"def concept number",
|
||||
"one isa number",
|
||||
"two isa number",
|
||||
"def concept twenties from bnf 'twenty' number as 20 + number"
|
||||
]),
|
||||
("When using isa and concept twenty", [
|
||||
"def concept one as 1",
|
||||
"def concept two as 2",
|
||||
"def concept twenty as 20",
|
||||
"def concept number",
|
||||
"one isa number",
|
||||
"two isa number",
|
||||
"def concept twenties from bnf twenty number as 20 + number"
|
||||
]),
|
||||
])
|
||||
def test_i_can_mix_concept_with_python_to_define_numbers(self, desc, definitions):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
for definition in definitions:
|
||||
sheerka.evaluate_user_input(definition)
|
||||
|
||||
res = sheerka.evaluate_user_input("twenty one")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert sheerka.isinstance(res[0].body, "twenties")
|
||||
assert res[0].body.body == 21
|
||||
|
||||
res = sheerka.evaluate_user_input("twenty one + 1")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body == 22
|
||||
|
||||
res = sheerka.evaluate_user_input("twenty one + one")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body == 22
|
||||
|
||||
res = sheerka.evaluate_user_input("twenty one + twenty two")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body == 43
|
||||
|
||||
res = sheerka.evaluate_user_input("1 + twenty one")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body == 22
|
||||
|
||||
res = sheerka.evaluate_user_input("1 + 1 + twenty one")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body == 23
|
||||
|
||||
def test_i_can_mix_bnf_and_isa(self):
|
||||
"""
|
||||
if 'one' isa 'number, twenty number should be recognized
|
||||
:return:
|
||||
"""
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.evaluate_user_input("def concept one as 1")
|
||||
sheerka.evaluate_user_input("def concept two as 2")
|
||||
sheerka.evaluate_user_input("def concept number")
|
||||
sheerka.evaluate_user_input("one isa number")
|
||||
sheerka.evaluate_user_input("two isa number")
|
||||
sheerka.evaluate_user_input("def concept twenties from bnf 'twenty' number as 20 + number")
|
||||
|
||||
res = sheerka.evaluate_user_input("twenty one")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body == simplec("twenties", 21)
|
||||
|
||||
res = sheerka.evaluate_user_input("twenty one + 1")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body == 22
|
||||
|
||||
res = sheerka.evaluate_user_input("twenty one + one")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body == 22
|
||||
|
||||
res = sheerka.evaluate_user_input("twenty one + twenty two")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body == 43
|
||||
|
||||
res = sheerka.evaluate_user_input("1 + twenty one")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body == 22
|
||||
|
||||
res = sheerka.evaluate_user_input("1 + 1 + twenty one")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body == 23
|
||||
|
||||
def test_i_can_mix_concept_of_concept(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
definitions = [
|
||||
"def concept one as 1",
|
||||
"def concept two as 2",
|
||||
"def concept twenties from bnf 'twenty' (one|two)=unit as 20 + unit",
|
||||
"def concept a plus b as a + b"
|
||||
]
|
||||
|
||||
for definition in definitions:
|
||||
sheerka.evaluate_user_input(definition)
|
||||
|
||||
res = sheerka.evaluate_user_input("1 plus 2")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body.body == 3
|
||||
|
||||
res = sheerka.evaluate_user_input("1 plus one")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body.body == 2
|
||||
|
||||
res = sheerka.evaluate_user_input("1 + 1 plus 1")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body.body == 3
|
||||
|
||||
res = sheerka.evaluate_user_input("1 plus twenty one")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body.body == 22
|
||||
|
||||
res = sheerka.evaluate_user_input("one plus 1")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body.body == 2
|
||||
|
||||
res = sheerka.evaluate_user_input("one plus two")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body.body == 3
|
||||
|
||||
res = sheerka.evaluate_user_input("one plus twenty one")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body.body == 22
|
||||
|
||||
res = sheerka.evaluate_user_input("twenty one plus 1")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body.body == 22
|
||||
|
||||
res = sheerka.evaluate_user_input("twenty one plus one")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body.body == 22
|
||||
|
||||
res = sheerka.evaluate_user_input("twenty one plus twenty two")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body.body == 43
|
||||
|
||||
@pytest.mark.xfail
|
||||
def test_i_can_evaluate_concept_of_concept_when_multiple_choices(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
definitions = [
|
||||
"def concept little a where a",
|
||||
"def concept blue a where a",
|
||||
"def concept little blue a where a",
|
||||
"def concept house"
|
||||
]
|
||||
|
||||
for definition in definitions:
|
||||
sheerka.evaluate_user_input(definition)
|
||||
|
||||
### CAUTION ####
|
||||
# this test cannot work !!
|
||||
# it is just to hint the result that I would like to achieve
|
||||
|
||||
res = sheerka.evaluate_user_input("little blue house")
|
||||
assert len(res) == 2
|
||||
assert res[0].status
|
||||
assert res[0].body == "little(blue(house))"
|
||||
|
||||
assert res[1].status
|
||||
assert res[1].body == "little blue(house)"
|
||||
|
||||
def test_i_can_say_that_a_concept_isa_another_concept(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.evaluate_user_input("def concept foo")
|
||||
sheerka.evaluate_user_input("def concept bar")
|
||||
|
||||
res = sheerka.evaluate_user_input("foo isa bar")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert sheerka.isinstance(res[0].body, BuiltinConcepts.SUCCESS)
|
||||
|
||||
def test_eval_does_not_break_valid_result(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.evaluate_user_input("def concept one as 1")
|
||||
sheerka.evaluate_user_input("def concept two as 2")
|
||||
|
||||
res = sheerka.evaluate_user_input("one + two")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body == 3
|
||||
|
||||
res = sheerka.evaluate_user_input("eval one + two")
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body == 3
|
||||
|
||||
@pytest.mark.parametrize("text", [
|
||||
"'hello",
|
||||
'"foo" + "string',
|
||||
"c::",
|
||||
"c:foo\nbar:",
|
||||
"c:foo",
|
||||
"def concept 'name",
|
||||
"def concept name from bnf 'name"
|
||||
])
|
||||
def test_i_can_manage_tokenizer_error(self, text):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.add_in_cache(Concept("foo"))
|
||||
|
||||
res = sheerka.evaluate_user_input(text)
|
||||
|
||||
assert len(res) > 1
|
||||
for r in [r for r in res if r.who.startswith("parsers.")]:
|
||||
assert not r.status
|
||||
|
||||
def test_i_can_recognize_concept_from_string(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.add_in_cache(Concept("one", body="1"))
|
||||
|
||||
res = sheerka.evaluate_user_input("'one'")
|
||||
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body == "one"
|
||||
|
||||
res = sheerka.evaluate_user_input("eval 'one'")
|
||||
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body == "one"
|
||||
|
||||
@pytest.mark.parametrize("expression", [
|
||||
"def concept twenties from bnf 'twenty' (one | two)=unit as 20 + unit",
|
||||
"def concept twenties from bnf 'twenty' (one | two)=unit as twenty + unit",
|
||||
"def concept twenties from bnf twenty (one | two)=unit as 20 + unit",
|
||||
"def concept twenties from bnf twenty (one | two)=unit as twenty + unit",
|
||||
])
|
||||
def test_i_can_evaluate_bnf_concepts(self, expression):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
sheerka.evaluate_user_input("def concept one as 1")
|
||||
sheerka.evaluate_user_input("def concept two as 2")
|
||||
sheerka.evaluate_user_input("def concept twenty as 20")
|
||||
sheerka.evaluate_user_input(expression)
|
||||
res = sheerka.evaluate_user_input("eval twenty one")
|
||||
|
||||
assert len(res) == 1
|
||||
assert res[0].status
|
||||
assert res[0].body == 21
|
||||
@@ -1,6 +1,6 @@
|
||||
import pytest
|
||||
|
||||
from core.tokenizer import Tokenizer, Token, TokenKind
|
||||
from core.tokenizer import Tokenizer, TokenKind
|
||||
from parsers.BaseParser import BaseParser
|
||||
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from core.tokenizer import Tokenizer, TokenKind, LexerError
|
||||
from parsers.BaseParser import UnexpectedTokenErrorNode
|
||||
from parsers.BnfParser import BnfParser, UnexpectedEndOfFileError
|
||||
from parsers.ConceptLexerParser import StrMatch, Optional, ZeroOrMore, OrderedChoice, Sequence, OneOrMore, \
|
||||
ConceptLexerParser, ConceptExpression, cnode
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
class ClassWithName:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
|
||||
class TestBnfParser(TestUsingMemoryBasedSheerka):
|
||||
|
||||
@pytest.mark.parametrize("expression, expected", [
|
||||
("'str'", StrMatch("str")),
|
||||
("1", StrMatch("1")),
|
||||
(" 1", StrMatch("1")),
|
||||
(",", StrMatch(",")),
|
||||
("'foo'?", Optional(StrMatch("foo"))),
|
||||
("'foo'*", ZeroOrMore(StrMatch("foo"))),
|
||||
("'foo'+", OneOrMore(StrMatch("foo"))),
|
||||
("1 | 2 | 3", OrderedChoice(StrMatch("1"), StrMatch("2"), StrMatch("3"))),
|
||||
("1|2|3", OrderedChoice(StrMatch("1"), StrMatch("2"), StrMatch("3"))),
|
||||
("1'|' 2 '|' 3", Sequence(StrMatch("1"), StrMatch("|"), StrMatch("2"), StrMatch("|"), StrMatch("3"))),
|
||||
("1 2 'foo'", Sequence(StrMatch("1"), StrMatch("2"), StrMatch("foo"))),
|
||||
("1 2 | 3 4+", OrderedChoice(
|
||||
Sequence(StrMatch("1"), StrMatch("2")),
|
||||
Sequence(StrMatch("3"), OneOrMore(StrMatch("4"))))),
|
||||
(
|
||||
"1 (2 | 3) 4+",
|
||||
Sequence(StrMatch("1"), OrderedChoice(StrMatch("2"), StrMatch("3")), OneOrMore(StrMatch("4")))),
|
||||
("(1|2)+", OneOrMore(OrderedChoice(StrMatch("1"), StrMatch("2")))),
|
||||
("(1 2)+", OneOrMore(Sequence(StrMatch("1"), StrMatch("2")))),
|
||||
("1 *", Sequence(StrMatch("1"), StrMatch("*"))),
|
||||
("1 ?", Sequence(StrMatch("1"), StrMatch("?"))),
|
||||
("1 +", Sequence(StrMatch("1"), StrMatch("+"))),
|
||||
("(1|*) +", Sequence(OrderedChoice(StrMatch("1"), StrMatch("*")), StrMatch("+"))),
|
||||
("1, :&", Sequence(StrMatch("1"), StrMatch(","), StrMatch(":"), StrMatch("&"))),
|
||||
("(1 )", StrMatch("1")),
|
||||
("'str'=var", StrMatch("str", rule_name="var")),
|
||||
("'foo'?=var", Optional(StrMatch("foo"), rule_name="var")),
|
||||
("('foo'?)=var", Optional(StrMatch("foo"), rule_name="var")),
|
||||
("'foo'*=var", ZeroOrMore(StrMatch("foo"), rule_name="var")),
|
||||
("('foo'*)=var", ZeroOrMore(StrMatch("foo"), rule_name="var")),
|
||||
("'foo'+=var", OneOrMore(StrMatch("foo"), rule_name="var")),
|
||||
("('foo'+)=var", OneOrMore(StrMatch("foo"), rule_name="var")),
|
||||
("'foo'=var?", Optional(StrMatch("foo", rule_name="var"))),
|
||||
("('foo'=var)?", Optional(StrMatch("foo", rule_name="var"))),
|
||||
("'foo'=var*", ZeroOrMore(StrMatch("foo", rule_name="var"))),
|
||||
("('foo'=var)*", ZeroOrMore(StrMatch("foo", rule_name="var"))),
|
||||
("'foo'=var+", OneOrMore(StrMatch("foo", rule_name="var"))),
|
||||
("('foo'=var)+", OneOrMore(StrMatch("foo", rule_name="var"))),
|
||||
("(1 | 2 | 3)=var", OrderedChoice(StrMatch("1"), StrMatch("2"), StrMatch("3"), rule_name="var")),
|
||||
("(1 2)=var", Sequence(StrMatch("1"), StrMatch("2"), rule_name="var")),
|
||||
("(1 2)+=var", OneOrMore(Sequence(StrMatch("1"), StrMatch("2")), rule_name="var")),
|
||||
("(1 2)=var+", OneOrMore(Sequence(StrMatch("1"), StrMatch("2"), rule_name="var"))),
|
||||
])
|
||||
def test_i_can_parse_regex(self, expression, expected):
|
||||
parser = BnfParser()
|
||||
res = parser.parse(self.get_context(), Tokenizer(expression))
|
||||
|
||||
assert not parser.has_error
|
||||
assert res.status
|
||||
assert res.value.value == expected
|
||||
assert res.value.source == expression
|
||||
|
||||
@pytest.mark.parametrize("expression, expected", [
|
||||
("foo", Concept("foo").init_key()),
|
||||
("foo*", ZeroOrMore(Concept("foo").init_key())),
|
||||
("foo 'and' bar+", Sequence(Concept("foo").init_key(), StrMatch("and"), OneOrMore(Concept("bar").init_key()))),
|
||||
("foo | bar?", OrderedChoice(Concept("foo").init_key(), Optional(Concept("bar").init_key()))),
|
||||
("'str' = var", Sequence(StrMatch("str"), StrMatch("="), Concept("var").init_key())),
|
||||
("'str''='var", Sequence(StrMatch("str"), StrMatch("="), Concept("var").init_key())),
|
||||
])
|
||||
def test_i_can_parse_regex_with_concept(self, expression, expected):
|
||||
foo = Concept("foo")
|
||||
bar = Concept("bar")
|
||||
var = Concept("var")
|
||||
context = self.get_context()
|
||||
|
||||
for c in (foo, bar, var):
|
||||
context.sheerka.add_in_cache(c)
|
||||
parser = BnfParser()
|
||||
res = parser.parse(context, Tokenizer(expression))
|
||||
|
||||
assert not parser.has_error
|
||||
assert res.status
|
||||
assert res.value.value == expected
|
||||
assert res.value.source == expression
|
||||
|
||||
def test_i_can_parse_regex_with_concept_when_the_concept_is_still_under_definition(self):
|
||||
expression = "foo"
|
||||
expected = ConceptExpression("foo")
|
||||
|
||||
context = self.get_context()
|
||||
context.obj = ClassWithName("foo")
|
||||
|
||||
parser = BnfParser()
|
||||
res = parser.parse(context, Tokenizer(expression))
|
||||
|
||||
assert not parser.has_error
|
||||
assert res.status
|
||||
assert res.value.value == expected
|
||||
assert res.value.source == expression
|
||||
|
||||
@pytest.mark.parametrize("expression, error", [
|
||||
("1 ", UnexpectedEndOfFileError()),
|
||||
("1|", UnexpectedEndOfFileError()),
|
||||
("(1|)", UnexpectedTokenErrorNode("Unexpected token 'Token(<EOF>)'", [TokenKind.RPAR])),
|
||||
("1=", UnexpectedTokenErrorNode("Unexpected token 'Token(<EOF>)'", [TokenKind.IDENTIFIER])),
|
||||
("'name", LexerError("Missing Trailing quote", "'name", 5, 1, 6))
|
||||
])
|
||||
def test_i_can_detect_errors(self, expression, error):
|
||||
parser = BnfParser()
|
||||
res = parser.parse(self.get_context(), Tokenizer(expression))
|
||||
ret_value = res.value.value
|
||||
assert parser.has_error
|
||||
assert not res.status
|
||||
assert ret_value[0] == error
|
||||
|
||||
def test_i_can_use_the_result_of_regex_parsing_to_parse_a_text(self):
|
||||
foo = Concept(name="foo")
|
||||
bar = Concept(name="bar")
|
||||
context = self.get_context()
|
||||
context.sheerka.add_in_cache(foo)
|
||||
context.sheerka.add_in_cache(bar)
|
||||
|
||||
regex_parser = BnfParser()
|
||||
foo_definition = regex_parser.parse(context, "'twenty' | 'thirty'").value.value
|
||||
bar_definition = regex_parser.parse(context, "foo ('one' | 'two')").value.value
|
||||
|
||||
concepts = {bar: bar_definition, foo: foo_definition}
|
||||
concept_parser = ConceptLexerParser()
|
||||
concept_parser.initialize(context, concepts)
|
||||
|
||||
res = concept_parser.parse(context, "twenty two")
|
||||
assert res.status
|
||||
assert res.value.body == [cnode("bar", 0, 2, "twenty two")]
|
||||
|
||||
res = concept_parser.parse(context, "thirty one")
|
||||
assert res.status
|
||||
assert res.value.body == [cnode("bar", 0, 2, "thirty one")]
|
||||
|
||||
res = concept_parser.parse(context, "twenty")
|
||||
assert res.status
|
||||
assert res.value.body == [cnode("foo", 0, 0, "twenty")]
|
||||
|
||||
def test_i_cannot_parse_when_too_many_concepts(self):
|
||||
foo1 = Concept(name="foo", body="1")
|
||||
foo2 = Concept(name="foo", body="2")
|
||||
context = self.get_context()
|
||||
context.sheerka.cache_by_key["foo"] = [foo1, foo2]
|
||||
|
||||
regex_parser = BnfParser()
|
||||
res = regex_parser.parse(context, "foo")
|
||||
|
||||
assert not res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.CANNOT_RESOLVE_CONCEPT)
|
||||
assert res.value.body == ('key', 'foo')
|
||||
|
||||
def test_i_cannot_parse_when_unknown_concept(self):
|
||||
context = self.get_context()
|
||||
|
||||
regex_parser = BnfParser()
|
||||
res = regex_parser.parse(self.get_context(), "foo")
|
||||
|
||||
assert not res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
assert res.value.body == ('key', 'foo')
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,193 @@
|
||||
import ast
|
||||
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import ParserResultConcept, ReturnValueConcept, BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from core.tokenizer import Token, TokenKind, Tokenizer
|
||||
from parsers.ConceptLexerParser import ConceptNode, UnrecognizedTokensNode, SourceCodeNode
|
||||
from parsers.ConceptsWithConceptsParser import ConceptsWithConceptsParser
|
||||
from parsers.MultipleConceptsParser import MultipleConceptsParser
|
||||
from parsers.PythonParser import PythonNode
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
multiple_concepts_parser = MultipleConceptsParser()
|
||||
|
||||
|
||||
def ret_val(*args):
|
||||
result = []
|
||||
index = 0
|
||||
source = ""
|
||||
for item in args:
|
||||
if isinstance(item, Concept):
|
||||
tokens = [Token(TokenKind.IDENTIFIER, item.name, 0, 0, 0)]
|
||||
result.append(ConceptNode(item, index, index, tokens, item.name))
|
||||
index += 1
|
||||
source += item.name
|
||||
elif isinstance(item, PythonNode):
|
||||
tokens = list(Tokenizer(item.source))[:-1] # strip trailing EOF
|
||||
result.append(SourceCodeNode(item, index, index + len(tokens) - 1, tokens, item.source))
|
||||
index += len(tokens)
|
||||
source += item.source
|
||||
else:
|
||||
tokens = list(Tokenizer(item))[:-1] # strip trailing EOF
|
||||
result.append(UnrecognizedTokensNode(index, index + len(tokens) - 1, tokens))
|
||||
index += len(tokens)
|
||||
source += item
|
||||
|
||||
return ReturnValueConcept(
|
||||
"who",
|
||||
False,
|
||||
ParserResultConcept(parser=multiple_concepts_parser, value=result, source=source))
|
||||
|
||||
|
||||
class TestConceptsWithConceptsParser(TestUsingMemoryBasedSheerka):
|
||||
|
||||
def init(self, concepts, inputs):
|
||||
context = self.get_context()
|
||||
for concept in concepts:
|
||||
context.sheerka.create_new_concept(context, concept)
|
||||
|
||||
return context, ret_val(*inputs)
|
||||
|
||||
def execute(self, concepts, inputs):
|
||||
context, input_return_values = self.init(concepts, inputs)
|
||||
|
||||
parser = ConceptsWithConceptsParser()
|
||||
result = parser.parse(context, input_return_values.body)
|
||||
|
||||
wrapper = result.body
|
||||
return_value = result.body.body
|
||||
|
||||
return context, parser, result, wrapper, return_value
|
||||
|
||||
@pytest.mark.parametrize("text, interested", [
|
||||
("not parser result", False),
|
||||
(ParserResultConcept(parser="not multiple_concepts_parser"), False),
|
||||
(ParserResultConcept(parser=multiple_concepts_parser, value=[]), True),
|
||||
])
|
||||
def test_not_interested(self, text, interested):
|
||||
context = self.get_context()
|
||||
|
||||
res = ConceptsWithConceptsParser().parse(context, text)
|
||||
if interested:
|
||||
assert res is not None
|
||||
else:
|
||||
assert res is None
|
||||
|
||||
def test_i_can_parse_composition_of_concepts(self):
|
||||
foo = Concept("foo")
|
||||
bar = Concept("bar")
|
||||
plus = Concept("a plus b").def_prop("a").def_prop("b")
|
||||
|
||||
context, parser, result, wrapper, return_value = self.execute([foo, bar, plus], [foo, " plus ", bar])
|
||||
|
||||
assert result.status
|
||||
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||
assert result.who == wrapper.parser.name
|
||||
assert wrapper.source == "foo plus bar"
|
||||
assert context.sheerka.isinstance(return_value, plus)
|
||||
|
||||
assert return_value.compiled["a"] == foo
|
||||
assert return_value.compiled["b"] == bar
|
||||
|
||||
# sanity check, I can evaluate the result
|
||||
evaluated = context.sheerka.evaluate_concept(context, return_value)
|
||||
assert evaluated.key == return_value.key
|
||||
assert evaluated.get_prop("a") == foo.init_key()
|
||||
assert evaluated.get_prop("b") == bar.init_key()
|
||||
|
||||
def test_i_can_parse_when_composition_of_source_code(self):
|
||||
plus = Concept("a plus b", body="a + b").def_prop("a").def_prop("b")
|
||||
left = PythonNode("1+1", ast.parse("1+1", mode="eval"))
|
||||
right = PythonNode("2+2", ast.parse("2+2", mode="eval"))
|
||||
context, parser, result, wrapper, return_value = self.execute([plus], [left, " plus ", right])
|
||||
|
||||
assert result.status
|
||||
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||
assert result.who == wrapper.parser.name
|
||||
assert wrapper.source == "1+1 plus 2+2"
|
||||
assert context.sheerka.isinstance(return_value, plus)
|
||||
|
||||
left_parser_result = ParserResultConcept(parser=parser, source="1+1", value=left)
|
||||
right_parser_result = ParserResultConcept(parser=parser, source="2+2", value=right)
|
||||
assert return_value.compiled["a"] == [ReturnValueConcept(parser.name, True, left_parser_result)]
|
||||
assert return_value.compiled["b"] == [ReturnValueConcept(parser.name, True, right_parser_result)]
|
||||
|
||||
# sanity check, I can evaluate the result
|
||||
evaluated = context.sheerka.evaluate_concept(context, return_value)
|
||||
assert evaluated.key == return_value.key
|
||||
assert evaluated.get_prop("a") == 2
|
||||
assert evaluated.get_prop("b") == 4
|
||||
assert evaluated.body == 6
|
||||
|
||||
def test_i_can_parse_when_mix_of_concept_and_code(self):
|
||||
plus = Concept("a plus b").def_prop("a").def_prop("b")
|
||||
code = PythonNode("1+1", ast.parse("1+1", mode="eval"))
|
||||
foo = Concept("foo")
|
||||
context, parser, result, wrapper, return_value = self.execute([plus, foo], [foo, " plus ", code])
|
||||
|
||||
assert result.status
|
||||
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||
assert result.who == wrapper.parser.name
|
||||
assert wrapper.source == "foo plus 1+1"
|
||||
assert context.sheerka.isinstance(return_value, plus)
|
||||
|
||||
code_parser_result = ParserResultConcept(parser=parser, source="1+1", value=code)
|
||||
assert return_value.compiled["a"] == foo
|
||||
assert return_value.compiled["b"] == [ReturnValueConcept(parser.name, True, code_parser_result)]
|
||||
|
||||
# sanity check, I can evaluate the result
|
||||
evaluated = context.sheerka.evaluate_concept(context, return_value)
|
||||
assert evaluated.key == return_value.key
|
||||
assert evaluated.get_prop("a") == foo.init_key()
|
||||
assert evaluated.get_prop("b") == 2
|
||||
|
||||
def test_i_can_parse_when_multiple_concepts_are_recognized(self):
|
||||
foo = Concept("foo")
|
||||
bar = Concept("bar")
|
||||
plus_1 = Concept("a plus b", body="body1").def_prop("a").def_prop("b")
|
||||
plus_2 = Concept("a plus b", body="body2").def_prop("a").def_prop("b")
|
||||
|
||||
context, input_return_values = self.init([foo, bar, plus_1, plus_2], [foo, " plus ", bar])
|
||||
parser = ConceptsWithConceptsParser()
|
||||
result = parser.parse(context, input_return_values.body)
|
||||
|
||||
assert len(result) == 2
|
||||
|
||||
res = result[0]
|
||||
wrapper = res.value
|
||||
return_value = res.value.value
|
||||
assert res.status
|
||||
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||
assert res.who == wrapper.parser.name
|
||||
assert wrapper.source == "foo plus bar"
|
||||
assert context.sheerka.isinstance(return_value, plus_1)
|
||||
assert return_value.compiled["a"] == foo
|
||||
assert return_value.compiled["b"] == bar
|
||||
|
||||
res = result[1]
|
||||
wrapper = res.value
|
||||
return_value = res.value.value
|
||||
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||
assert res.who == wrapper.parser.name
|
||||
assert wrapper.source == "foo plus bar"
|
||||
assert context.sheerka.isinstance(return_value, plus_2)
|
||||
assert return_value.compiled["a"] == foo
|
||||
assert return_value.compiled["b"] == bar
|
||||
|
||||
def test_i_cannot_parse_when_unknown_concept(self):
|
||||
foo = Concept("foo")
|
||||
bar = Concept("bar")
|
||||
|
||||
context, input_return_values = self.init([foo, bar], [foo, " plus ", bar])
|
||||
parser = ConceptsWithConceptsParser()
|
||||
result = parser.parse(context, input_return_values.body)
|
||||
wrapper = result.body
|
||||
return_value = result.body.body
|
||||
|
||||
assert not result.status
|
||||
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.NOT_FOR_ME)
|
||||
assert result.who == parser.name
|
||||
assert return_value == input_return_values.body.body
|
||||
@@ -0,0 +1,345 @@
|
||||
import pytest
|
||||
import ast
|
||||
|
||||
from core.builtin_concepts import ParserResultConcept, BuiltinConcepts, ReturnValueConcept
|
||||
from core.concept import Concept
|
||||
from parsers.ConceptLexerParser import OrderedChoice, StrMatch
|
||||
from parsers.PythonParser import PythonParser, PythonNode
|
||||
from core.tokenizer import Keywords, Tokenizer, LexerError
|
||||
from parsers.DefaultParser import DefaultParser, NameNode, SyntaxErrorNode, CannotHandleErrorNode, IsaConceptNode
|
||||
from parsers.DefaultParser import UnexpectedTokenErrorNode, DefConceptNode
|
||||
from parsers.BnfParser import BnfParser
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
def get_def_concept(name, where=None, pre=None, post=None, body=None, definition=None):
|
||||
def_concept = DefConceptNode([], name=NameNode(list(Tokenizer(name))))
|
||||
|
||||
if body:
|
||||
def_concept.body = get_concept_part(body)
|
||||
if where:
|
||||
def_concept.where = get_concept_part(where)
|
||||
if pre:
|
||||
def_concept.pre = get_concept_part(pre)
|
||||
if post:
|
||||
def_concept.post = get_concept_part(post)
|
||||
if definition:
|
||||
def_concept.definition = ReturnValueConcept(
|
||||
"parsers.Bnf",
|
||||
True,
|
||||
definition)
|
||||
|
||||
return def_concept
|
||||
|
||||
|
||||
def get_concept_part(part):
|
||||
if isinstance(part, str):
|
||||
node = PythonNode(part, ast.parse(part, mode="eval"))
|
||||
return ReturnValueConcept(
|
||||
who="parsers.Default",
|
||||
status=True,
|
||||
value=ParserResultConcept(
|
||||
source=part,
|
||||
parser=PythonParser(),
|
||||
value=node))
|
||||
|
||||
if isinstance(part, PythonNode):
|
||||
return ReturnValueConcept(
|
||||
who="parsers.Default",
|
||||
status=True,
|
||||
value=ParserResultConcept(
|
||||
source=part.source,
|
||||
parser=PythonParser(),
|
||||
value=part))
|
||||
|
||||
if isinstance(part, ReturnValueConcept):
|
||||
return part
|
||||
|
||||
|
||||
class TestDefaultParser(TestUsingMemoryBasedSheerka):
|
||||
|
||||
@pytest.mark.parametrize("text, expected", [
|
||||
("def concept hello", get_def_concept(name="hello")),
|
||||
("def concept hello ", get_def_concept(name="hello")),
|
||||
("def concept a + b", get_def_concept(name="a + b")),
|
||||
("def concept a+b", get_def_concept(name="a + b")),
|
||||
("def concept 'a+b'+c", get_def_concept(name="'a+b' + c")),
|
||||
("def concept 'as if'", get_def_concept(name="'as if'")),
|
||||
("def concept 'as' if", get_def_concept(name="'as if'")),
|
||||
("def concept hello as 'hello'", get_def_concept(name="hello", body="'hello'")),
|
||||
("def concept hello as 1", get_def_concept(name="hello", body="1")),
|
||||
("def concept hello as 1 + 1", get_def_concept(name="hello", body="1 + 1")),
|
||||
])
|
||||
def test_i_can_parse_def_concept(self, text, expected):
|
||||
parser = DefaultParser()
|
||||
res = parser.parse(self.get_context(), text)
|
||||
node = res.value.value
|
||||
|
||||
assert res.status
|
||||
assert res.who == parser.name
|
||||
assert res.value.source == text
|
||||
assert isinstance(res.value, ParserResultConcept)
|
||||
assert node == expected
|
||||
|
||||
def test_i_can_parse_complex_def_concept_statement(self):
|
||||
text = """def concept a plus b
|
||||
where a,b
|
||||
pre isinstance(a, int) and isinstance(b, float)
|
||||
post isinstance(res, int)
|
||||
as res = a + b
|
||||
"""
|
||||
parser = DefaultParser()
|
||||
res = parser.parse(self.get_context(), text)
|
||||
return_value = res.value
|
||||
expected_concept = get_def_concept(
|
||||
name="a plus b",
|
||||
where="a,b",
|
||||
pre="isinstance(a, int) and isinstance(b, float)",
|
||||
post="isinstance(res, int)",
|
||||
body=PythonNode("res = a + b", ast.parse("res = a + b", mode="exec"))
|
||||
)
|
||||
|
||||
assert res.status
|
||||
assert isinstance(return_value, ParserResultConcept)
|
||||
assert return_value.value == expected_concept
|
||||
|
||||
def test_i_can_have_mutilines_declarations(self):
|
||||
text = """
|
||||
def concept add one to a as
|
||||
def func(x):
|
||||
return x+1
|
||||
func(a)
|
||||
"""
|
||||
|
||||
expected_concept = get_def_concept(
|
||||
name="add one to a ",
|
||||
body=PythonNode(
|
||||
"def func(x):\n return x+1\nfunc(a)",
|
||||
ast.parse("def func(x):\n return x+1\nfunc(a)", mode="exec"))
|
||||
)
|
||||
|
||||
parser = DefaultParser()
|
||||
res = parser.parse(self.get_context(), text)
|
||||
return_value = res.value
|
||||
|
||||
assert res.status
|
||||
assert isinstance(return_value, ParserResultConcept)
|
||||
assert return_value.value == expected_concept
|
||||
|
||||
def test_i_can_use_colon_to_use_indentation(self):
|
||||
text = """
|
||||
def concept add one to a as:
|
||||
def func(x):
|
||||
return x+1
|
||||
func(a)
|
||||
"""
|
||||
|
||||
expected_concept = get_def_concept(
|
||||
name="add one to a ",
|
||||
body=PythonNode(
|
||||
"def func(x):\n return x+1\nfunc(a)",
|
||||
ast.parse("def func(x):\n return x+1\nfunc(a)", mode="exec"))
|
||||
)
|
||||
|
||||
parser = DefaultParser()
|
||||
res = parser.parse(self.get_context(), text)
|
||||
return_value = res.value
|
||||
|
||||
assert res.status
|
||||
assert isinstance(return_value, ParserResultConcept)
|
||||
assert return_value.value == expected_concept
|
||||
|
||||
def test_indentation_is_mandatory_after_a_colon(self):
|
||||
text = """
|
||||
def concept add one to a as:
|
||||
def func(x):
|
||||
return x+1
|
||||
func(a)
|
||||
"""
|
||||
|
||||
parser = DefaultParser()
|
||||
res = parser.parse(self.get_context(), text)
|
||||
return_value = res.value
|
||||
|
||||
assert not res.status
|
||||
assert isinstance(return_value, ParserResultConcept)
|
||||
assert isinstance(return_value.value[0], SyntaxErrorNode)
|
||||
assert return_value.value[0].message == "Indentation not found."
|
||||
|
||||
def test_indentation_is_not_allowed_if_the_colon_is_missing(self):
|
||||
text = """
|
||||
def concept add one to a as
|
||||
def func(x):
|
||||
return x+1
|
||||
func(a)
|
||||
"""
|
||||
context = self.get_context()
|
||||
|
||||
parser = DefaultParser()
|
||||
res = parser.parse(context, text)
|
||||
return_value = res.value
|
||||
|
||||
assert not res.status
|
||||
assert context.sheerka.isinstance(return_value, BuiltinConcepts.TOO_MANY_ERRORS)
|
||||
|
||||
def test_name_is_mandatory(self):
|
||||
text = "def concept as 'hello'"
|
||||
|
||||
parser = DefaultParser()
|
||||
res = parser.parse(self.get_context(), text)
|
||||
return_value = res.value
|
||||
|
||||
assert not res.status
|
||||
assert isinstance(return_value, ParserResultConcept)
|
||||
assert isinstance(return_value.value[0], SyntaxErrorNode)
|
||||
assert return_value.value[0].message == "Name is mandatory"
|
||||
|
||||
def test_concept_keyword_is_mandatory_but_the_concept_is_recognized(self):
|
||||
text = "def hello as a where b pre c post d"
|
||||
|
||||
expected_concept = get_def_concept(name="hello", body="a", where="b", pre="c", post="d")
|
||||
parser = DefaultParser()
|
||||
res = parser.parse(self.get_context(), text)
|
||||
return_value = res.value
|
||||
|
||||
assert not res.status
|
||||
assert isinstance(return_value, ParserResultConcept)
|
||||
assert isinstance(return_value.value[0], UnexpectedTokenErrorNode)
|
||||
assert return_value.value[0].message == "Syntax error."
|
||||
assert return_value.value[0].expected_tokens == [Keywords.CONCEPT]
|
||||
assert return_value.try_parsed == expected_concept
|
||||
|
||||
@pytest.mark.parametrize("text", [
|
||||
"def concept hello where 1+",
|
||||
"def concept hello pre 1+",
|
||||
"def concept hello post 1+",
|
||||
"def concept hello as 1+"
|
||||
])
|
||||
def test_i_can_detect_error_in_declaration(self, text):
|
||||
context = self.get_context()
|
||||
sheerka = context.sheerka
|
||||
|
||||
parser = DefaultParser()
|
||||
res = parser.parse(context, text)
|
||||
return_value = res.value
|
||||
|
||||
assert not res.status
|
||||
assert sheerka.isinstance(return_value, BuiltinConcepts.TOO_MANY_ERRORS)
|
||||
|
||||
def test_new_line_is_not_allowed_in_the_name(self):
|
||||
text = "def concept hello \n my friend as 'hello'"
|
||||
|
||||
parser = DefaultParser()
|
||||
res = parser.parse(self.get_context(), text)
|
||||
return_value = res.value
|
||||
|
||||
assert not res.status
|
||||
assert return_value.value == [SyntaxErrorNode([], "Newline are not allowed in name.")]
|
||||
|
||||
def test_i_can_parse_def_concept_from_regex(self):
|
||||
context = self.get_context()
|
||||
a_concept = Concept("a_concept")
|
||||
context.sheerka.add_in_cache(a_concept)
|
||||
|
||||
text = "def concept name from bnf a_concept | 'a_string' as __definition[0]"
|
||||
parser = DefaultParser()
|
||||
res = parser.parse(context, text)
|
||||
node = res.value.value
|
||||
definition = OrderedChoice(a_concept, StrMatch("a_string"))
|
||||
parser_result = ParserResultConcept(BnfParser(), "a_concept | 'a_string'", definition, definition)
|
||||
expected = get_def_concept(name="name", body="__definition[0]", definition=parser_result)
|
||||
|
||||
assert res.status
|
||||
assert res.who == parser.name
|
||||
assert res.value.source == text
|
||||
assert isinstance(res.value, ParserResultConcept)
|
||||
assert node == expected
|
||||
|
||||
def test_i_can_parse_def_concept_where_bnf_references_itself(self):
|
||||
context = self.get_context()
|
||||
a_concept = Concept("a_concept")
|
||||
context.sheerka.add_in_cache(a_concept)
|
||||
|
||||
text = "def concept name from bnf 'a' + name?"
|
||||
parser = DefaultParser()
|
||||
parser.parse(context, text)
|
||||
|
||||
assert not parser.has_error
|
||||
|
||||
def test_i_can_detect_empty_bnf_declaration(self):
|
||||
text = "def concept name from bnf as __definition[0]"
|
||||
|
||||
parser = DefaultParser()
|
||||
res = parser.parse(self.get_context(), text)
|
||||
|
||||
assert not res.status
|
||||
assert res.value.value[0] == SyntaxErrorNode([], "Empty declaration")
|
||||
|
||||
def test_i_can_detect_not_for_me(self):
|
||||
text = "hello world"
|
||||
context = self.get_context()
|
||||
parser = DefaultParser()
|
||||
res = parser.parse(context, text)
|
||||
|
||||
assert not res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.NOT_FOR_ME)
|
||||
assert isinstance(res.value.body[0], CannotHandleErrorNode)
|
||||
|
||||
def test_i_can_parse_is_a(self):
|
||||
parser = DefaultParser()
|
||||
text = "the name of my 'concept' isa the name of the set"
|
||||
res = parser.parse(self.get_context(), text)
|
||||
expected = IsaConceptNode([],
|
||||
concept=NameNode(list(Tokenizer("the name of my 'concept'"))),
|
||||
set=NameNode(list(Tokenizer("the name of the set"))))
|
||||
|
||||
assert res.status
|
||||
assert res.who == parser.name
|
||||
assert res.value.source == text
|
||||
assert isinstance(res.value, ParserResultConcept)
|
||||
assert res.value.value == expected
|
||||
|
||||
@pytest.mark.parametrize("text", [
|
||||
"concept",
|
||||
"isa number",
|
||||
"name isa",
|
||||
"def",
|
||||
"def concept_name"
|
||||
])
|
||||
def test_i_cannot_parse_invalid_entries(self, text):
|
||||
parser = DefaultParser()
|
||||
res = parser.parse(self.get_context(), text)
|
||||
|
||||
assert not res.status
|
||||
assert isinstance(res.body, ParserResultConcept)
|
||||
assert isinstance(res.body.body[0], UnexpectedTokenErrorNode)
|
||||
|
||||
@pytest.mark.parametrize("text, error_msg, error_text", [
|
||||
("'name", "Missing Trailing quote", "'name"),
|
||||
("foo isa 'name", "Missing Trailing quote", "'name"),
|
||||
("def concept 'name", "Missing Trailing quote", "'name"),
|
||||
("def concept name as 'body", "Missing Trailing quote", "'body"),
|
||||
("def concept name from bnf 'expression", "Missing Trailing quote", "'expression"),
|
||||
("def concept c::", "Concept name not found", ""),
|
||||
])
|
||||
def test_i_cannot_parse_when_tokenizer_fails(self, text, error_msg, error_text):
|
||||
parser = DefaultParser()
|
||||
res = parser.parse(self.get_context(), text)
|
||||
|
||||
assert not res.status
|
||||
assert isinstance(res.body, ParserResultConcept)
|
||||
assert isinstance(res.body.body[0], LexerError)
|
||||
assert res.body.body[0].message == error_msg
|
||||
assert res.body.body[0].text == error_text
|
||||
|
||||
def test_i_cannot_parse_bnf_definition_referencing_unknown_concept(self):
|
||||
context = self.get_context()
|
||||
text = "def concept name from bnf unknown"
|
||||
|
||||
parser = DefaultParser()
|
||||
res = parser.parse(context, text)
|
||||
|
||||
assert not res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
assert res.value.body == ("key", "unknown")
|
||||
@@ -0,0 +1,138 @@
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from core.tokenizer import Tokenizer
|
||||
from parsers.ExactConceptParser import ExactConceptParser
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
def metadata_prop(concept, prop_name):
|
||||
for name, value in concept.metadata.props:
|
||||
if name == prop_name:
|
||||
return value
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_concept(name, variables):
|
||||
c = Concept(name=name)
|
||||
if variables:
|
||||
for v in variables:
|
||||
c.def_prop(v)
|
||||
c.init_key()
|
||||
return c
|
||||
|
||||
|
||||
class TestExactConceptParser(TestUsingMemoryBasedSheerka):
|
||||
|
||||
def test_i_can_compute_combinations(self):
|
||||
parser = ExactConceptParser()
|
||||
res = parser.combinations(["foo", "bar", "baz"])
|
||||
|
||||
assert res == {('foo', 'bar', 'baz'),
|
||||
('__var__0', 'bar', 'baz'),
|
||||
('foo', '__var__0', 'baz'),
|
||||
('foo', 'bar', '__var__0'),
|
||||
('__var__0', '__var__1', 'baz'),
|
||||
('__var__0', 'bar', '__var__1'),
|
||||
('foo', '__var__0', '__var__1'),
|
||||
('__var__0', '__var__1', '__var__2')}
|
||||
|
||||
def test_i_can_compute_combinations_with_duplicates(self):
|
||||
parser = ExactConceptParser()
|
||||
res = parser.combinations(["foo", "bar", "foo"])
|
||||
|
||||
assert res == {('foo', 'bar', 'foo'),
|
||||
('__var__0', 'bar', '__var__0'),
|
||||
('foo', '__var__0', 'foo'),
|
||||
('__var__0', '__var__1', '__var__0'),
|
||||
('__var__1', '__var__0', '__var__1')}
|
||||
# TODO: the last tuple is not possible, so the algo can be improved
|
||||
|
||||
def test_i_can_recognize_a_simple_concept(self):
|
||||
context = self.get_context()
|
||||
concept = get_concept("hello world", [])
|
||||
context.sheerka.add_in_cache(concept)
|
||||
|
||||
source = "hello world"
|
||||
results = ExactConceptParser().parse(context, source)
|
||||
|
||||
assert len(results) == 1
|
||||
assert results[0].status
|
||||
assert results[0].value.value == concept
|
||||
|
||||
def test_i_can_recognize_concepts_defined_several_times(self):
|
||||
context = self.get_context()
|
||||
context.sheerka.add_in_cache(get_concept("hello world", []))
|
||||
context.sheerka.add_in_cache(get_concept("hello a", ["a"]))
|
||||
|
||||
source = "hello world"
|
||||
results = ExactConceptParser().parse(context, source)
|
||||
|
||||
assert len(results) == 2
|
||||
results = sorted(results, key=lambda x: x.value.value.name) # because of the usage of sets
|
||||
|
||||
assert results[0].status
|
||||
assert results[0].value.value.name == "hello a"
|
||||
assert metadata_prop(results[0].value.value, "a") == "world"
|
||||
|
||||
assert results[1].status
|
||||
assert results[1].value.value.name == "hello world"
|
||||
|
||||
def test_i_can_recognize_a_concept_with_variables(self):
|
||||
context = self.get_context()
|
||||
concept = get_concept("a + b", ["a", "b"])
|
||||
context.sheerka.add_in_cache(concept)
|
||||
source = "10 + 5"
|
||||
results = ExactConceptParser().parse(context, source)
|
||||
|
||||
assert len(results) == 1
|
||||
assert results[0].status
|
||||
concept_found = results[0].value.value
|
||||
assert concept_found.key == concept.key
|
||||
assert metadata_prop(concept_found, "a") == "10"
|
||||
assert metadata_prop(concept_found, "b") == "5"
|
||||
|
||||
def test_i_can_recognize_a_concept_with_duplicate_variables(self):
|
||||
context = self.get_context()
|
||||
concept = get_concept("a + b + a", ["a", "b"])
|
||||
context.sheerka.cache_by_key[concept.key] = concept
|
||||
source = "10 + 5 + 10"
|
||||
results = ExactConceptParser().parse(context, source)
|
||||
|
||||
assert len(results) == 1
|
||||
assert results[0].status
|
||||
concept_found = results[0].value.value
|
||||
assert concept_found.key == concept.key
|
||||
assert metadata_prop(concept_found, "a") == "10"
|
||||
assert metadata_prop(concept_found, "b") == "5"
|
||||
|
||||
def test_i_can_manage_unknown_concept(self):
|
||||
context = self.get_context()
|
||||
source = "def concept hello world" # this is not a concept by itself
|
||||
res = ExactConceptParser().parse(context, source)
|
||||
|
||||
assert not res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
assert res.value.body == "def concept hello world"
|
||||
|
||||
def test_i_can_detect_concepts_too_long(self):
|
||||
context = self.get_context()
|
||||
source = "a very very long concept that cannot be an unique one"
|
||||
res = ExactConceptParser().parse(context, source)
|
||||
|
||||
assert not res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.CONCEPT_TOO_LONG)
|
||||
assert res.value.body == "a very very long concept that cannot be an unique one"
|
||||
|
||||
def test_i_can_detect_concept_from_tokens(self):
|
||||
context = self.get_context()
|
||||
concept = get_concept("hello world", [])
|
||||
context.sheerka.add_in_cache(concept)
|
||||
|
||||
source = "hello world"
|
||||
results = ExactConceptParser().parse(context, list(Tokenizer(source)))
|
||||
|
||||
assert len(results) == 1
|
||||
assert results[0].status
|
||||
assert results[0].value.value == concept
|
||||
@@ -0,0 +1,215 @@
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import ParserResultConcept, BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from core.tokenizer import Tokenizer, TokenKind, Token
|
||||
from parsers.ConceptLexerParser import ConceptLexerParser, ConceptNode, Sequence, cnode, utnode, scnode, SourceCodeNode
|
||||
from parsers.MultipleConceptsParser import MultipleConceptsParser
|
||||
from parsers.PythonParser import PythonNode
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
def get_return_value(context, grammar, expression):
|
||||
parser = ConceptLexerParser()
|
||||
parser.initialize(context, grammar)
|
||||
|
||||
ret_val = parser.parse(context, expression)
|
||||
assert not ret_val.status
|
||||
return ret_val
|
||||
|
||||
|
||||
class TestMultipleConceptsParser(TestUsingMemoryBasedSheerka):
|
||||
|
||||
def init(self, concepts, grammar, expression):
|
||||
context = self.get_context()
|
||||
for c in concepts:
|
||||
context.sheerka.create_new_concept(context, c)
|
||||
return_value = get_return_value(context, grammar, expression)
|
||||
|
||||
return context, return_value
|
||||
|
||||
def test_not_interested_if_not_parser_result(self):
|
||||
context = self.get_context()
|
||||
text = "not parser result"
|
||||
|
||||
res = MultipleConceptsParser().parse(context, text)
|
||||
assert res is None
|
||||
|
||||
def test_not_interested_if_not_from_concept_lexer_parser(self):
|
||||
context = self.get_context()
|
||||
text = ParserResultConcept(parser="not concept lexer", value="some value")
|
||||
|
||||
res = MultipleConceptsParser().parse(context, text)
|
||||
assert res is None
|
||||
|
||||
def test_i_can_parse_exact_concepts(self):
|
||||
foo = Concept("foo", body="'foo'")
|
||||
bar = Concept("bar", body="'bar'")
|
||||
baz = Concept("baz", body="'baz'")
|
||||
grammar = {}
|
||||
context, return_value = self.init([foo, bar, baz], grammar, "bar foo baz")
|
||||
|
||||
parser = MultipleConceptsParser()
|
||||
ret_val = parser.parse(context, return_value.body)
|
||||
|
||||
assert ret_val.status
|
||||
assert ret_val.who == parser.name
|
||||
assert context.sheerka.isinstance(ret_val.value, BuiltinConcepts.PARSER_RESULT)
|
||||
assert ret_val.value.value == [
|
||||
ConceptNode(bar, 0, 0, source="bar"),
|
||||
ConceptNode(foo, 2, 2, source="foo"),
|
||||
ConceptNode(baz, 4, 4, source="baz")]
|
||||
assert ret_val.value.source == "bar foo baz"
|
||||
|
||||
def test_i_can_parse_when_ending_with_bnf(self):
|
||||
foo = Concept("foo", body="'foo'")
|
||||
bar = Concept("bar", body="'bar'")
|
||||
grammar = {foo: Sequence("foo1", "foo2", "foo3")}
|
||||
context, return_value = self.init([foo, bar], grammar, "bar foo1 foo2 foo3")
|
||||
|
||||
parser = MultipleConceptsParser()
|
||||
ret_val = parser.parse(context, return_value.body)
|
||||
|
||||
assert ret_val.status
|
||||
assert ret_val.who == parser.name
|
||||
assert context.sheerka.isinstance(ret_val.value, BuiltinConcepts.PARSER_RESULT)
|
||||
assert ret_val.value.value == [cnode("bar", 0, 0, "bar"), cnode("foo", 2, 6, "foo1 foo2 foo3")]
|
||||
assert ret_val.value.source == "bar foo1 foo2 foo3"
|
||||
|
||||
def test_i_can_parse_when_starting_with_bnf(self):
|
||||
foo = Concept("foo", body="'foo'")
|
||||
bar = Concept("bar", body="'bar'")
|
||||
grammar = {foo: Sequence("foo1", "foo2", "foo3")}
|
||||
context, return_value = self.init([foo, bar], grammar, "foo1 foo2 foo3 bar")
|
||||
|
||||
parser = MultipleConceptsParser()
|
||||
ret_val = parser.parse(context, return_value.body)
|
||||
|
||||
assert ret_val.status
|
||||
assert ret_val.who == parser.name
|
||||
assert context.sheerka.isinstance(ret_val.value, BuiltinConcepts.PARSER_RESULT)
|
||||
assert ret_val.value.value == [cnode("foo", 0, 4, "foo1 foo2 foo3"), cnode("bar", 6, 6, "bar")]
|
||||
assert ret_val.value.source == "foo1 foo2 foo3 bar"
|
||||
|
||||
def test_i_can_parse_when_concept_are_mixed(self):
|
||||
foo = Concept("foo")
|
||||
bar = Concept("bar")
|
||||
baz = Concept("baz")
|
||||
grammar = {foo: Sequence("foo1", "foo2", "foo3")}
|
||||
context, return_value = self.init([foo, bar, baz], grammar, "baz foo1 foo2 foo3 bar")
|
||||
|
||||
parser = MultipleConceptsParser()
|
||||
ret_val = parser.parse(context, return_value.body)
|
||||
|
||||
assert ret_val.status
|
||||
assert ret_val.who == parser.name
|
||||
assert context.sheerka.isinstance(ret_val.value, BuiltinConcepts.PARSER_RESULT)
|
||||
assert ret_val.value.value == [
|
||||
cnode("baz", 0, 0, "baz"),
|
||||
cnode("foo", 2, 6, "foo1 foo2 foo3"),
|
||||
cnode("bar", 8, 8, "bar")]
|
||||
assert ret_val.value.source == "baz foo1 foo2 foo3 bar"
|
||||
|
||||
def test_i_can_parse_when_multiple_concepts_are_matching(self):
|
||||
foo = Concept("foo")
|
||||
bar = Concept("bar", body="bar1")
|
||||
baz = Concept("bar", body="bar2")
|
||||
grammar = {foo: "foo"}
|
||||
context, return_value = self.init([foo, bar, baz], grammar, "foo bar")
|
||||
|
||||
parser = MultipleConceptsParser()
|
||||
ret_val = parser.parse(context, return_value.body)
|
||||
|
||||
assert len(ret_val) == 2
|
||||
assert ret_val[0].status
|
||||
assert ret_val[0].value.value == [cnode("foo", 0, 0, "foo"), cnode("bar", 2, 2, "bar")]
|
||||
assert ret_val[0].value.source == "foo bar"
|
||||
assert ret_val[0].value.value[1].concept.metadata.body == "bar1"
|
||||
|
||||
assert ret_val[1].status
|
||||
assert ret_val[1].value.value == [cnode("foo", 0, 0, "foo"), cnode("bar", 2, 2, "bar")]
|
||||
assert ret_val[1].value.source == "foo bar"
|
||||
assert ret_val[1].value.value[1].concept.metadata.body == "bar2"
|
||||
|
||||
def test_i_can_parse_when_source_code(self):
|
||||
foo = Concept("foo")
|
||||
grammar = {foo: "foo"}
|
||||
context, return_value = self.init([foo], grammar, "1 foo")
|
||||
|
||||
parser = MultipleConceptsParser()
|
||||
ret_val = parser.parse(context, return_value.body)
|
||||
wrapper = ret_val.value
|
||||
value = ret_val.value.value
|
||||
|
||||
assert ret_val.status
|
||||
assert ret_val.who == parser.name
|
||||
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||
assert wrapper.source == "1 foo"
|
||||
assert value == [
|
||||
scnode(0, 1, "1 "),
|
||||
cnode("foo", 2, 2, "foo")]
|
||||
|
||||
def test_i_cannot_parse_when_unrecognized_token(self):
|
||||
twenty_two = Concept("twenty two")
|
||||
one = Concept("one")
|
||||
grammar = {twenty_two: Sequence("twenty", "two")}
|
||||
context, return_value = self.init([twenty_two, one], grammar, "twenty two + one")
|
||||
|
||||
parser = MultipleConceptsParser()
|
||||
ret_val = parser.parse(context, return_value.body)
|
||||
|
||||
assert not ret_val.status
|
||||
assert ret_val.who == parser.name
|
||||
assert context.sheerka.isinstance(ret_val.value, BuiltinConcepts.PARSER_RESULT)
|
||||
assert ret_val.value.value == [
|
||||
cnode("twenty two", 0, 2, "twenty two"),
|
||||
utnode(3, 5, " + "),
|
||||
cnode("one", 6, 6, "one")
|
||||
]
|
||||
assert ret_val.value.source == "twenty two + one"
|
||||
|
||||
def test_i_cannot_parse_when_unknown_concepts(self):
|
||||
twenty_two = Concept("twenty two")
|
||||
one = Concept("one")
|
||||
grammar = {twenty_two: Sequence("twenty", "two")}
|
||||
context, return_value = self.init([twenty_two, one], grammar, "twenty two plus one")
|
||||
|
||||
parser = MultipleConceptsParser()
|
||||
ret_val = parser.parse(context, return_value.body)
|
||||
|
||||
assert not ret_val.status
|
||||
assert ret_val.who == parser.name
|
||||
assert context.sheerka.isinstance(ret_val.value, BuiltinConcepts.PARSER_RESULT)
|
||||
assert ret_val.value.value == [
|
||||
cnode("twenty two", 0, 2, "twenty two"),
|
||||
utnode(3, 5, " plus "),
|
||||
cnode("one", 6, 6, "one")
|
||||
]
|
||||
assert ret_val.value.source == "twenty two plus one"
|
||||
|
||||
@pytest.mark.parametrize("text, expected_source, expected_end", [
|
||||
("True", "True", 0),
|
||||
("1 == 1", "1 == 1", 5),
|
||||
("1!xdf", "1", 0),
|
||||
("1", "1", 0),
|
||||
])
|
||||
def test_i_can_get_source_code_node(self, text, expected_source, expected_end):
|
||||
tokens = list(Tokenizer(text))[:-1] # strip trailing EOF
|
||||
|
||||
start_index = 5 # a random number different of zero
|
||||
res = MultipleConceptsParser().get_source_code_node(self.get_context(), start_index, tokens)
|
||||
|
||||
assert isinstance(res, SourceCodeNode)
|
||||
assert isinstance(res.node, PythonNode)
|
||||
assert res.source == expected_source
|
||||
assert res.start == start_index
|
||||
assert res.end == start_index + expected_end
|
||||
|
||||
def test_i_cannot_parse_null_text(self):
|
||||
res = MultipleConceptsParser().get_source_code_node(self.get_context(), 0, [])
|
||||
assert res is None
|
||||
|
||||
eof = Token(TokenKind.EOF, "", 0, 0, 0)
|
||||
res = MultipleConceptsParser().get_source_code_node(self.get_context(), 0, [eof])
|
||||
assert res is None
|
||||
@@ -0,0 +1,77 @@
|
||||
import ast
|
||||
import pytest
|
||||
from core.builtin_concepts import ParserResultConcept
|
||||
from core.tokenizer import Tokenizer, LexerError
|
||||
from parsers.PythonParser import PythonNode, PythonParser, PythonErrorNode
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
class TestPythonParser(TestUsingMemoryBasedSheerka):
|
||||
|
||||
@pytest.mark.parametrize("text, expected", [
|
||||
("1+1", PythonNode("1+1", ast.parse("1+1", mode="eval"))),
|
||||
("a=10", PythonNode("a=10", ast.parse("a=10", mode="exec"))),
|
||||
])
|
||||
def test_i_can_parse_a_simple_expression(self, text, expected):
|
||||
parser = PythonParser()
|
||||
res = parser.parse(self.get_context(), text)
|
||||
|
||||
assert res.status
|
||||
assert res.who == parser.name
|
||||
assert isinstance(res.value, ParserResultConcept)
|
||||
assert res.value.value == expected
|
||||
|
||||
@pytest.mark.parametrize("text, expected", [
|
||||
("1+1", PythonNode("1+1", ast.parse("1+1", mode="eval"))),
|
||||
("a=10", PythonNode("a=10", ast.parse("a=10", mode="exec"))),
|
||||
])
|
||||
def test_i_can_parse_from_tokens(self, text, expected):
|
||||
parser = PythonParser()
|
||||
tokens = list(Tokenizer(text))
|
||||
res = parser.parse(self.get_context(), tokens)
|
||||
|
||||
assert res.status
|
||||
assert res.who == parser.name
|
||||
assert isinstance(res.value, ParserResultConcept)
|
||||
assert res.value.value == expected
|
||||
|
||||
@pytest.mark.parametrize("text", [
|
||||
"1+",
|
||||
"'name",
|
||||
"foo = 'name"
|
||||
])
|
||||
def test_i_can_detect_error(self, text):
|
||||
parser = PythonParser()
|
||||
res = parser.parse(self.get_context(), text)
|
||||
|
||||
assert not res.status
|
||||
assert res.who == parser.name
|
||||
assert isinstance(res.value, ParserResultConcept)
|
||||
assert isinstance(res.value.value[0], PythonErrorNode)
|
||||
assert isinstance(res.value.value[0].exception, SyntaxError)
|
||||
|
||||
@pytest.mark.parametrize("text, error_msg, error_text", [
|
||||
("c::", "Concept name not found", ""),
|
||||
("c:: + 1", "Concept name not found", ""),
|
||||
])
|
||||
def test_i_can_detect_lexer_errors(self, text, error_msg, error_text):
|
||||
parser = PythonParser()
|
||||
res = parser.parse(self.get_context(), text)
|
||||
|
||||
assert not res.status
|
||||
assert isinstance(res.body, ParserResultConcept)
|
||||
assert isinstance(res.body.body[0], LexerError)
|
||||
assert res.body.body[0].message == error_msg
|
||||
assert res.body.body[0].text == error_text
|
||||
|
||||
def test_i_can_parse_a_concept(self):
|
||||
text = "c:concept_name: + 1"
|
||||
|
||||
parser = PythonParser()
|
||||
res = parser.parse(self.get_context(), text)
|
||||
|
||||
assert res
|
||||
assert res.value.value == PythonNode(
|
||||
"c:concept_name: + 1",
|
||||
ast.parse("__C__USE_CONCEPT__concept_name__C__+1", mode="eval"))
|
||||
@@ -0,0 +1,138 @@
|
||||
import ast
|
||||
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import ParserResultConcept, BuiltinConcepts, ReturnValueConcept
|
||||
from core.concept import Concept
|
||||
from core.tokenizer import Token, TokenKind, Tokenizer
|
||||
from parsers.ConceptLexerParser import ConceptNode, UnrecognizedTokensNode
|
||||
from parsers.MultipleConceptsParser import MultipleConceptsParser
|
||||
from parsers.PythonParser import PythonNode, PythonErrorNode
|
||||
from parsers.PythonWithConceptsParser import PythonWithConceptsParser
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
multiple_concepts_parser = MultipleConceptsParser()
|
||||
|
||||
|
||||
def ret_val(*args):
|
||||
result = []
|
||||
index = 0
|
||||
for item in args:
|
||||
if isinstance(item, Concept):
|
||||
tokens = [Token(TokenKind.IDENTIFIER, item.name, 0, 0, 0)]
|
||||
result.append(ConceptNode(item, index, index, tokens, item.name))
|
||||
index += 1
|
||||
else:
|
||||
tokens = list(Tokenizer(item))
|
||||
result.append(UnrecognizedTokensNode(index, index + len(tokens) - 1, tokens))
|
||||
index += len(tokens)
|
||||
|
||||
return ReturnValueConcept("who", False, ParserResultConcept(parser=multiple_concepts_parser, value=result))
|
||||
|
||||
|
||||
def to_str_ast(expression):
|
||||
return PythonNode.get_dump(ast.parse(expression, mode="eval"))
|
||||
|
||||
|
||||
class TestPythonWithConceptsParser(TestUsingMemoryBasedSheerka):
|
||||
|
||||
@pytest.mark.parametrize("text, interested", [
|
||||
("not parser result", False),
|
||||
(ParserResultConcept(parser="not multiple_concepts_parser"), False),
|
||||
(ParserResultConcept(parser=multiple_concepts_parser, value=[]), True),
|
||||
])
|
||||
def test_not_interested(self, text, interested):
|
||||
context = self.get_context()
|
||||
|
||||
res = PythonWithConceptsParser().parse(context, text)
|
||||
if interested:
|
||||
assert res is not None
|
||||
else:
|
||||
assert res is None
|
||||
|
||||
def test_i_can_parse_concepts_and_python(self):
|
||||
context = self.get_context()
|
||||
foo = Concept("foo")
|
||||
input_return_value = ret_val(foo, " + 1")
|
||||
|
||||
parser = PythonWithConceptsParser()
|
||||
result = parser.parse(context, input_return_value.body)
|
||||
wrapper = result.value
|
||||
return_value = result.value.value
|
||||
|
||||
assert result.status
|
||||
assert result.who == parser.name
|
||||
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||
assert wrapper.source == "foo + 1"
|
||||
assert isinstance(return_value, PythonNode)
|
||||
assert return_value.source == "foo + 1"
|
||||
assert return_value.get_dump(return_value.ast_) == to_str_ast("__C__foo__C__ + 1")
|
||||
assert return_value.concepts["__C__foo__C__"] == foo
|
||||
|
||||
def test_i_can_parse_concepts_and_python_when_concept_is_known(self):
|
||||
context = self.get_context()
|
||||
foo = Concept("foo")
|
||||
foo = context.sheerka.create_new_concept(context, foo).body.body
|
||||
input_return_value = ret_val(foo, " + 1")
|
||||
|
||||
parser = PythonWithConceptsParser()
|
||||
result = parser.parse(context, input_return_value.body)
|
||||
wrapper = result.value
|
||||
return_value = result.value.value
|
||||
|
||||
assert result.status
|
||||
assert result.who == parser.name
|
||||
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||
assert wrapper.source == "foo + 1"
|
||||
assert isinstance(return_value, PythonNode)
|
||||
assert return_value.source == "foo + 1"
|
||||
assert return_value.get_dump(return_value.ast_) == to_str_ast("__C__foo__1001__C__ + 1")
|
||||
assert return_value.concepts["__C__foo__1001__C__"] == foo
|
||||
|
||||
def test_i_can_parse_when_concept_name_has_invalid_characters(self):
|
||||
context = self.get_context()
|
||||
foo = Concept("foo et > (,")
|
||||
foo = context.sheerka.create_new_concept(context, foo).body.body
|
||||
input_return_value = ret_val(foo, " + 1")
|
||||
|
||||
parser = PythonWithConceptsParser()
|
||||
result = parser.parse(context, input_return_value.body)
|
||||
return_value = result.value.value
|
||||
|
||||
assert result.status
|
||||
assert return_value.concepts["__C__foo0et000000__1001__C__"] == foo
|
||||
|
||||
def test_python_ids_mappings_are_correct_when_concepts_with_the_same_name(self):
|
||||
context = self.get_context()
|
||||
foo1 = Concept("foo")
|
||||
foo2 = Concept("foo")
|
||||
foo3 = context.sheerka.create_new_concept(context, Concept("foo", body="foo3")).body.body
|
||||
foo4 = context.sheerka.create_new_concept(context, Concept("foo", body="foo4")).body.body
|
||||
|
||||
input_return_value = ret_val(foo1, "+", foo2, "+", foo3, "+", foo4)
|
||||
|
||||
parser = PythonWithConceptsParser()
|
||||
result = parser.parse(context, input_return_value.body)
|
||||
return_value = result.value.value
|
||||
|
||||
assert result.status
|
||||
assert return_value.concepts["__C__foo__C__"] == foo1
|
||||
assert return_value.concepts["__C__foo_1__C__"] == foo2
|
||||
assert return_value.concepts["__C__foo__1001__C__"] == foo3
|
||||
assert return_value.concepts["__C__foo__1002__C__"] == foo4
|
||||
|
||||
def test_i_cannot_parse_if_syntax_error(self):
|
||||
context = self.get_context()
|
||||
foo = Concept("foo")
|
||||
foo = context.sheerka.create_new_concept(context, foo).body.body
|
||||
input_return_value = ret_val(foo, " + ")
|
||||
|
||||
parser = PythonWithConceptsParser()
|
||||
result = parser.parse(context, input_return_value.body)
|
||||
wrapper = result.value
|
||||
return_value = result.value.value
|
||||
|
||||
assert not result.status
|
||||
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||
assert isinstance(return_value[0], PythonErrorNode)
|
||||
@@ -12,7 +12,7 @@ import json
|
||||
from sdp.sheerkaSerializer import JsonSerializer, Serializer, PickleSerializer
|
||||
import core.utils
|
||||
|
||||
tests_root = path.abspath("../build/tests")
|
||||
tests_root = path.abspath("../../build/tests")
|
||||
evt_digest = "3a571cb6034ef6fc8d7fe91948d0d29728eed74de02bac7968b0e9facca2c2d7"
|
||||
|
||||
|
||||
@@ -821,17 +821,17 @@ def test_i_can_set_using_reference(root):
|
||||
entry, key = sdp.set(evt_digest, "entry", ObjWithKey(2, "foo"), use_ref=True)
|
||||
|
||||
state = sdp.load_state(sdp.get_snapshot())
|
||||
assert state.data == {"entry": {"2": '##REF##:95b5cbab545dded0b90b57a3d15a157b9a559fb586ee2f8d6ccbc6d2491f1268'}}
|
||||
assert state.data == {"entry": {"2": '##REF##:43f07065c7bad051cdd726bdfa4de7f8d754c31486c65ddb31d6b6548dec3db9'}}
|
||||
assert entry == "entry"
|
||||
assert key == "2"
|
||||
|
||||
assert sdp.io.exists(sdp.io.get_obj_path(SheerkaDataProvider.ObjectsFolder,
|
||||
"95b5cbab545dded0b90b57a3d15a157b9a559fb586ee2f8d6ccbc6d2491f1268"))
|
||||
"43f07065c7bad051cdd726bdfa4de7f8d754c31486c65ddb31d6b6548dec3db9"))
|
||||
|
||||
# sanity check, make sure that I can load back
|
||||
loaded = sdp.get(entry, key)
|
||||
assert loaded == ObjWithKey(2, "foo")
|
||||
assert getattr(loaded, Serializer.ORIGIN) == "95b5cbab545dded0b90b57a3d15a157b9a559fb586ee2f8d6ccbc6d2491f1268"
|
||||
assert getattr(loaded, Serializer.ORIGIN) == "43f07065c7bad051cdd726bdfa4de7f8d754c31486c65ddb31d6b6548dec3db9"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("root", [
|
||||
@@ -1430,7 +1430,7 @@ def test_i_can_modify_a_ref(root):
|
||||
state = sdp.load_state(sdp.get_snapshot())
|
||||
assert state.data == {"entry": {
|
||||
"key1": ObjWithKey("key1", "foo"),
|
||||
"key2": "##REF##:d70b0247311645ed18d275337cbcf79ad186d995236cdc8ad4fcfc708085bd3d"}}
|
||||
"key2": "##REF##:041d3cca905b51bc2c66251e73e56b836aae7b9435ee3d7eb05d44bb67ff575e"}}
|
||||
assert entry == "entry"
|
||||
assert key == "key2"
|
||||
|
||||
@@ -37,7 +37,7 @@ def test_i_can_serialize_an_event():
|
||||
def test_i_can_serialize_an_object():
|
||||
obj = Obj("10", "value")
|
||||
serializer = Serializer()
|
||||
serializer.register(JsonSerializer("tests.test_sheerkaSerializer.Obj"))
|
||||
serializer.register(JsonSerializer("tests.sdp.test_sheerkaSerializer.Obj"))
|
||||
context = SerializerContext("kodjo", "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b")
|
||||
|
||||
stream = serializer.serialize(obj, context)
|
||||
@@ -51,7 +51,7 @@ def test_i_can_serialize_an_object():
|
||||
|
||||
|
||||
@pytest.mark.parametrize("obj, expected", [
|
||||
(Obj("10", "value"), "tests.test_sheerkaSerializer.Obj")
|
||||
(Obj("10", "value"), "tests.sdp.test_sheerkaSerializer.Obj")
|
||||
])
|
||||
def test_get_full_qualified_name(obj, expected):
|
||||
assert expected == core.utils.get_full_qualified_name(obj)
|
||||
@@ -1,194 +0,0 @@
|
||||
import ast
|
||||
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
|
||||
from core.concept import VARIABLE_PREFIX, ConceptParts, Concept
|
||||
from core.sheerka import Sheerka, ExecutionContext
|
||||
from core.tokenizer import Tokenizer
|
||||
from evaluators.AddConceptEvaluator import AddConceptEvaluator
|
||||
from parsers.BaseParser import BaseParser
|
||||
from parsers.ConceptLexerParser import Sequence, StrMatch, ZeroOrMore, ConceptExpression
|
||||
from parsers.BnfParser import BnfParser
|
||||
from parsers.DefaultParser import DefConceptNode, NameNode
|
||||
from parsers.PythonParser import PythonNode, PythonParser
|
||||
from sdp.sheerkaDataProvider import Event
|
||||
|
||||
|
||||
def get_context():
|
||||
sheerka = Sheerka(skip_builtins_in_db=True)
|
||||
sheerka.initialize("mem://")
|
||||
return ExecutionContext("test", Event(), sheerka)
|
||||
|
||||
|
||||
def get_def_concept(name, where=None, pre=None, post=None, body=None, definition=None):
|
||||
concept = DefConceptNode([], name=NameNode(list(Tokenizer(name))))
|
||||
|
||||
if body:
|
||||
concept.body = get_concept_part(body)
|
||||
if where:
|
||||
concept.where = get_concept_part(where)
|
||||
if pre:
|
||||
concept.pre = get_concept_part(pre)
|
||||
if post:
|
||||
concept.post = get_concept_part(post)
|
||||
if definition:
|
||||
concept.definition = definition
|
||||
|
||||
return ReturnValueConcept(BaseParser.PREFIX + "some_name", True, ParserResultConcept(value=concept))
|
||||
|
||||
|
||||
def get_concept_part(part):
|
||||
if isinstance(part, str):
|
||||
node = PythonNode(part, ast.parse(part, mode="eval"))
|
||||
return ReturnValueConcept(
|
||||
who="parsers.Default",
|
||||
status=True,
|
||||
value=ParserResultConcept(
|
||||
source=part,
|
||||
parser=PythonParser(),
|
||||
value=node))
|
||||
|
||||
if isinstance(part, PythonNode):
|
||||
return ReturnValueConcept(
|
||||
who="parsers.Default",
|
||||
status=True,
|
||||
value=ParserResultConcept(
|
||||
source=part.source,
|
||||
parser=PythonParser(),
|
||||
value=part))
|
||||
|
||||
if isinstance(part, ReturnValueConcept):
|
||||
return part
|
||||
|
||||
|
||||
def get_concept_definition(source, parsing_expression):
|
||||
return ReturnValueConcept(
|
||||
who="Parsers:RegexParser",
|
||||
status=True,
|
||||
value=ParserResultConcept(
|
||||
source=source,
|
||||
parser=BnfParser(),
|
||||
value=parsing_expression
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ret_val, expected", [
|
||||
(ReturnValueConcept(BaseParser.PREFIX + "some_name", True, ParserResultConcept(value=DefConceptNode([]))), True),
|
||||
(ReturnValueConcept(BaseParser.PREFIX + "some_name", False, ParserResultConcept(value=DefConceptNode([]))), False),
|
||||
(ReturnValueConcept(BaseParser.PREFIX + "some_name", True, "not a ParserResultConcept"), False),
|
||||
(ReturnValueConcept(BaseParser.PREFIX + "some_name", True, ParserResultConcept()), False),
|
||||
])
|
||||
def test_i_can_match(ret_val, expected):
|
||||
context = get_context()
|
||||
assert AddConceptEvaluator().matches(context, ret_val) == expected
|
||||
|
||||
|
||||
def test_that_the_source_is_correctly_set():
|
||||
context = get_context()
|
||||
def_concept_return_value = get_def_concept(
|
||||
name="hello a",
|
||||
definition=get_concept_definition("hello a", Sequence(StrMatch("hello"), StrMatch("a"))),
|
||||
where="isinstance(a, str )",
|
||||
pre="a is not None",
|
||||
body="print('hello' + a)")
|
||||
|
||||
evaluated = AddConceptEvaluator().eval(context, def_concept_return_value)
|
||||
|
||||
assert evaluated.status
|
||||
assert context.sheerka.isinstance(evaluated.body, BuiltinConcepts.NEW_CONCEPT)
|
||||
|
||||
created_concept = evaluated.body.body
|
||||
assert created_concept.metadata.name == "hello a"
|
||||
assert created_concept.metadata.where == "isinstance(a, str )"
|
||||
assert created_concept.metadata.pre == "a is not None"
|
||||
assert created_concept.metadata.post is None
|
||||
assert created_concept.metadata.body == "print('hello' + a)"
|
||||
assert created_concept.metadata.definition == "hello a"
|
||||
|
||||
|
||||
# def test_that_the_ast_is_correctly_initialized():
|
||||
# """
|
||||
# When I parse the definition of a concept, I evaluate the metadata (like the body)
|
||||
# I wanted to keep in cache these evaluation for further utilisation but I have
|
||||
# a serialization issue.
|
||||
# So I had to comment concept.add_codes(def_concept_node.get_asts()) around line 85
|
||||
# So this test is now irrelevant
|
||||
# :return:
|
||||
# """
|
||||
# context = get_context()
|
||||
# def_concept_return_value = get_concept(
|
||||
# name="hello a",
|
||||
# definition=get_concept_definition("hello a", Sequence(StrMatch("hello"), StrMatch("a"))),
|
||||
# where="isinstance(a, str )",
|
||||
# pre="a is not None",
|
||||
# body="print('hello' + a)")
|
||||
#
|
||||
# evaluated = AddConceptEvaluator().eval(context, def_concept_return_value)
|
||||
#
|
||||
# assert evaluated.status
|
||||
# assert context.sheerka.isinstance(evaluated.body, BuiltinConcepts.NEW_CONCEPT)
|
||||
#
|
||||
# created_concept = evaluated.body.body
|
||||
#
|
||||
# assert ConceptParts.WHERE in created_concept.compiled
|
||||
# assert ConceptParts.PRE in created_concept.compiled
|
||||
# assert ConceptParts.BODY in created_concept.compiled
|
||||
# assert ConceptParts.POST not in created_concept.compiled
|
||||
|
||||
|
||||
def test_that_the_new_concept_is_correctly_saved_in_db():
|
||||
context = get_context()
|
||||
def_concept_return_value = get_def_concept(
|
||||
name="hello a",
|
||||
definition=get_concept_definition("hello a", Sequence(StrMatch("hello"), StrMatch("a"))),
|
||||
where="isinstance(a, str )",
|
||||
pre="a is not None",
|
||||
body="print('hello' + a)")
|
||||
|
||||
# sanity. Make sure that the concept does not already exist
|
||||
from_db = context.sheerka.get("hello " + VARIABLE_PREFIX + "0")
|
||||
assert context.sheerka.isinstance(from_db, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
|
||||
AddConceptEvaluator().eval(context, def_concept_return_value)
|
||||
context.sheerka.concepts_cache = {} # reset cache
|
||||
from_db = context.sheerka.get("hello " + VARIABLE_PREFIX + "0")
|
||||
|
||||
assert from_db.metadata.key == f"hello {VARIABLE_PREFIX}0"
|
||||
assert from_db.metadata.name == "hello a"
|
||||
assert from_db.metadata.where == "isinstance(a, str )"
|
||||
assert from_db.metadata.pre == "a is not None"
|
||||
assert from_db.metadata.post is None
|
||||
assert from_db.metadata.body == "print('hello' + a)"
|
||||
assert from_db.metadata.definition == "hello a"
|
||||
assert len(from_db.metadata.props) == 1
|
||||
assert from_db.metadata.props[0] == ("a", None)
|
||||
assert "a" in from_db.props
|
||||
|
||||
assert from_db.compiled == {} # ast is not saved in db
|
||||
|
||||
|
||||
def test_i_can_get_props_from_python_node():
|
||||
ret_val = get_concept_part("isinstance(a, str)")
|
||||
context = get_context()
|
||||
|
||||
assert AddConceptEvaluator.get_props(context.sheerka, ret_val, ["a"]) == ["a"]
|
||||
|
||||
|
||||
def test_i_can_get_props_from_another_concept():
|
||||
concept = Concept("hello").def_prop("a").def_prop("b")
|
||||
ret_val = ReturnValueConcept(who="some_parser",
|
||||
status=True,
|
||||
value=ParserResultConcept(value=concept))
|
||||
|
||||
assert AddConceptEvaluator.get_props(get_context(), ret_val, []) == ["a", "b"]
|
||||
|
||||
|
||||
def test_i_can_get_props_from_definition():
|
||||
parsing_expression = Sequence(ConceptExpression('mult'), ZeroOrMore(Sequence(StrMatch("+"), ConceptExpression("add"))))
|
||||
ret_val = get_concept_definition("mult (('+'|'-') add)?", parsing_expression)
|
||||
|
||||
assert AddConceptEvaluator.get_props(get_context(), ret_val, []) == ["add", "mult"]
|
||||
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from core.sheerka import Sheerka, ExecutionContext
|
||||
from core.tokenizer import Tokenizer
|
||||
from evaluators.AddConceptInSetEvaluator import AddConceptInSetEvaluator
|
||||
from parsers.DefaultParser import IsaConceptNode, NameNode
|
||||
from sdp.sheerkaDataProvider import Event
|
||||
|
||||
|
||||
def get_context():
|
||||
sheerka = Sheerka(skip_builtins_in_db=True)
|
||||
sheerka.initialize("mem://")
|
||||
return ExecutionContext("test", Event(), sheerka)
|
||||
|
||||
|
||||
def get_ret_val(concept_name, concept_set_name):
|
||||
n1 = NameNode(list(Tokenizer(concept_name)))
|
||||
n2 = NameNode(list(Tokenizer(concept_set_name)))
|
||||
|
||||
return ReturnValueConcept("some_name", True, ParserResultConcept(value=IsaConceptNode([], n1, n2)))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ret_val, expected", [
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept(value=IsaConceptNode([]))), True),
|
||||
(ReturnValueConcept("some_name", False, ParserResultConcept(value=IsaConceptNode([]))), False),
|
||||
(ReturnValueConcept("some_name", True, "not a ParserResultConcept"), False),
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept()), False),
|
||||
])
|
||||
def test_i_can_match(ret_val, expected):
|
||||
context = get_context()
|
||||
assert AddConceptInSetEvaluator().matches(context, ret_val) == expected
|
||||
|
||||
|
||||
def test_i_cannot_add_if_the_concept_does_not_exists():
|
||||
context = get_context()
|
||||
|
||||
ret_val = get_ret_val("foo", "bar")
|
||||
res = AddConceptInSetEvaluator().eval(context, ret_val)
|
||||
|
||||
assert not res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
assert res.value.body == "foo"
|
||||
|
||||
|
||||
def test_i_cannot_add_if_the_set_does_not_exists():
|
||||
context = get_context()
|
||||
foo = Concept("foo")
|
||||
context.sheerka.set_id_if_needed(foo, False)
|
||||
context.sheerka.add_in_cache(foo)
|
||||
|
||||
ret_val = get_ret_val("foo", "bar")
|
||||
res = AddConceptInSetEvaluator().eval(context, ret_val)
|
||||
|
||||
assert not res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
assert res.value.body == "bar"
|
||||
|
||||
|
||||
def test_i_can_add_concept_to_a_set_of_concept():
|
||||
context = get_context()
|
||||
foo = Concept("foo")
|
||||
context.sheerka.set_id_if_needed(foo, False)
|
||||
context.sheerka.add_in_cache(foo)
|
||||
|
||||
bar = Concept("bar")
|
||||
context.sheerka.set_id_if_needed(bar, False)
|
||||
context.sheerka.add_in_cache(bar)
|
||||
|
||||
ret_val = get_ret_val("foo", "bar")
|
||||
res = AddConceptInSetEvaluator().eval(context, ret_val)
|
||||
|
||||
assert res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.SUCCESS)
|
||||
|
||||
|
||||
def test_i_can_add_concept_with_a_body_to_a_set_of_concept():
|
||||
context = get_context()
|
||||
foo = Concept("foo", body="1")
|
||||
context.sheerka.set_id_if_needed(foo, False)
|
||||
context.sheerka.add_in_cache(foo)
|
||||
|
||||
bar = Concept("bar")
|
||||
context.sheerka.set_id_if_needed(bar, False)
|
||||
context.sheerka.add_in_cache(bar)
|
||||
|
||||
ret_val = get_ret_val("foo", "bar")
|
||||
res = AddConceptInSetEvaluator().eval(context, ret_val)
|
||||
|
||||
assert res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.SUCCESS)
|
||||
|
||||
|
||||
def test_i_cannot_add_the_same_concept_twice():
|
||||
context = get_context()
|
||||
foo = Concept("foo")
|
||||
context.sheerka.set_id_if_needed(foo, False)
|
||||
context.sheerka.add_in_cache(foo)
|
||||
|
||||
bar = Concept("bar")
|
||||
context.sheerka.set_id_if_needed(bar, False)
|
||||
context.sheerka.add_in_cache(bar)
|
||||
|
||||
ret_val = get_ret_val("foo", "bar")
|
||||
AddConceptInSetEvaluator().eval(context, ret_val)
|
||||
res = AddConceptInSetEvaluator().eval(context, ret_val)
|
||||
|
||||
assert not res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.CONCEPT_ALREADY_IN_SET)
|
||||
assert res.value.concept == foo
|
||||
assert res.value.concept_set == bar
|
||||
@@ -1,185 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from core.sheerka import Sheerka, ExecutionContext
|
||||
from core.tokenizer import Tokenizer, TokenKind, LexerError
|
||||
from parsers.BaseParser import UnexpectedTokenErrorNode
|
||||
from parsers.BnfParser import BnfParser, UnexpectedEndOfFileError
|
||||
from parsers.ConceptLexerParser import StrMatch, Optional, ZeroOrMore, OrderedChoice, Sequence, OneOrMore, \
|
||||
ConceptLexerParser, ConceptNode, ConceptExpression, cnode
|
||||
from sdp.sheerkaDataProvider import Event
|
||||
|
||||
|
||||
def get_context():
|
||||
sheerka = Sheerka(skip_builtins_in_db=True)
|
||||
sheerka.initialize("mem://")
|
||||
|
||||
return ExecutionContext("sheerka", Event(), sheerka)
|
||||
|
||||
|
||||
class ClassWithName():
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expression, expected", [
|
||||
("'str'", StrMatch("str")),
|
||||
("1", StrMatch("1")),
|
||||
(" 1", StrMatch("1")),
|
||||
(",", StrMatch(",")),
|
||||
("'foo'?", Optional(StrMatch("foo"))),
|
||||
("'foo'*", ZeroOrMore(StrMatch("foo"))),
|
||||
("'foo'+", OneOrMore(StrMatch("foo"))),
|
||||
("1 | 2 | 3", OrderedChoice(StrMatch("1"), StrMatch("2"), StrMatch("3"))),
|
||||
("1|2|3", OrderedChoice(StrMatch("1"), StrMatch("2"), StrMatch("3"))),
|
||||
("1'|' 2 '|' 3", Sequence(StrMatch("1"), StrMatch("|"), StrMatch("2"), StrMatch("|"), StrMatch("3"))),
|
||||
("1 2 'foo'", Sequence(StrMatch("1"), StrMatch("2"), StrMatch("foo"))),
|
||||
("1 2 | 3 4+", OrderedChoice(
|
||||
Sequence(StrMatch("1"), StrMatch("2")),
|
||||
Sequence(StrMatch("3"), OneOrMore(StrMatch("4"))))),
|
||||
("1 (2 | 3) 4+", Sequence(StrMatch("1"), OrderedChoice(StrMatch("2"), StrMatch("3")), OneOrMore(StrMatch("4")))),
|
||||
("(1|2)+", OneOrMore(OrderedChoice(StrMatch("1"), StrMatch("2")))),
|
||||
("(1 2)+", OneOrMore(Sequence(StrMatch("1"), StrMatch("2")))),
|
||||
("1 *", Sequence(StrMatch("1"), StrMatch("*"))),
|
||||
("1 ?", Sequence(StrMatch("1"), StrMatch("?"))),
|
||||
("1 +", Sequence(StrMatch("1"), StrMatch("+"))),
|
||||
("(1|*) +", Sequence(OrderedChoice(StrMatch("1"), StrMatch("*")), StrMatch("+"))),
|
||||
("1, :&", Sequence(StrMatch("1"), StrMatch(","), StrMatch(":"), StrMatch("&"))),
|
||||
("(1 )", StrMatch("1")),
|
||||
("'str'=var", StrMatch("str", rule_name="var")),
|
||||
("'foo'?=var", Optional(StrMatch("foo"), rule_name="var")),
|
||||
("('foo'?)=var", Optional(StrMatch("foo"), rule_name="var")),
|
||||
("'foo'*=var", ZeroOrMore(StrMatch("foo"), rule_name="var")),
|
||||
("('foo'*)=var", ZeroOrMore(StrMatch("foo"), rule_name="var")),
|
||||
("'foo'+=var", OneOrMore(StrMatch("foo"), rule_name="var")),
|
||||
("('foo'+)=var", OneOrMore(StrMatch("foo"), rule_name="var")),
|
||||
("'foo'=var?", Optional(StrMatch("foo", rule_name="var"))),
|
||||
("('foo'=var)?", Optional(StrMatch("foo", rule_name="var"))),
|
||||
("'foo'=var*", ZeroOrMore(StrMatch("foo", rule_name="var"))),
|
||||
("('foo'=var)*", ZeroOrMore(StrMatch("foo", rule_name="var"))),
|
||||
("'foo'=var+", OneOrMore(StrMatch("foo", rule_name="var"))),
|
||||
("('foo'=var)+", OneOrMore(StrMatch("foo", rule_name="var"))),
|
||||
("(1 | 2 | 3)=var", OrderedChoice(StrMatch("1"), StrMatch("2"), StrMatch("3"), rule_name="var")),
|
||||
("(1 2)=var", Sequence(StrMatch("1"), StrMatch("2"), rule_name="var")),
|
||||
("(1 2)+=var", OneOrMore(Sequence(StrMatch("1"), StrMatch("2")), rule_name="var")),
|
||||
("(1 2)=var+", OneOrMore(Sequence(StrMatch("1"), StrMatch("2"), rule_name="var"))),
|
||||
])
|
||||
def test_i_can_parse_regex(expression, expected):
|
||||
parser = BnfParser()
|
||||
res = parser.parse(get_context(), Tokenizer(expression))
|
||||
|
||||
assert not parser.has_error
|
||||
assert res.status
|
||||
assert res.value.value == expected
|
||||
assert res.value.source == expression
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expression, expected", [
|
||||
("foo", Concept("foo").init_key()),
|
||||
("foo*", ZeroOrMore(Concept("foo").init_key())),
|
||||
("foo 'and' bar+", Sequence(Concept("foo").init_key(), StrMatch("and"), OneOrMore(Concept("bar").init_key()))),
|
||||
("foo | bar?", OrderedChoice(Concept("foo").init_key(), Optional(Concept("bar").init_key()))),
|
||||
("'str' = var", Sequence(StrMatch("str"), StrMatch("="), Concept("var").init_key())),
|
||||
("'str''='var", Sequence(StrMatch("str"), StrMatch("="), Concept("var").init_key())),
|
||||
])
|
||||
def test_i_can_parse_regex_with_concept(expression, expected):
|
||||
foo = Concept("foo")
|
||||
bar = Concept("bar")
|
||||
var = Concept("var")
|
||||
context = get_context()
|
||||
|
||||
for c in (foo, bar, var):
|
||||
context.sheerka.add_in_cache(c)
|
||||
parser = BnfParser()
|
||||
res = parser.parse(context, Tokenizer(expression))
|
||||
|
||||
assert not parser.has_error
|
||||
assert res.status
|
||||
assert res.value.value == expected
|
||||
assert res.value.source == expression
|
||||
|
||||
|
||||
def test_i_can_parse_regex_with_concept_when_the_concept_is_still_under_definition():
|
||||
expression = "foo"
|
||||
expected = ConceptExpression("foo")
|
||||
|
||||
context = get_context()
|
||||
context.obj = ClassWithName("foo")
|
||||
|
||||
parser = BnfParser()
|
||||
res = parser.parse(context, Tokenizer(expression))
|
||||
|
||||
assert not parser.has_error
|
||||
assert res.status
|
||||
assert res.value.value == expected
|
||||
assert res.value.source == expression
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expression, error", [
|
||||
("1 ", UnexpectedEndOfFileError()),
|
||||
("1|", UnexpectedEndOfFileError()),
|
||||
("(1|)", UnexpectedTokenErrorNode("Unexpected token 'Token(<EOF>)'", [TokenKind.RPAR])),
|
||||
("1=", UnexpectedTokenErrorNode("Unexpected token 'Token(<EOF>)'", [TokenKind.IDENTIFIER])),
|
||||
("'name", LexerError("Missing Trailing quote", "'name", 5, 1, 6))
|
||||
])
|
||||
def test_i_can_detect_errors(expression, error):
|
||||
parser = BnfParser()
|
||||
res = parser.parse(get_context(), Tokenizer(expression))
|
||||
ret_value = res.value.value
|
||||
assert parser.has_error
|
||||
assert not res.status
|
||||
assert ret_value[0] == error
|
||||
|
||||
|
||||
def test_i_can_use_the_result_of_regex_parsing_to_parse_a_text():
|
||||
foo = Concept(name="foo")
|
||||
bar = Concept(name="bar")
|
||||
context = get_context()
|
||||
context.sheerka.add_in_cache(foo)
|
||||
context.sheerka.add_in_cache(bar)
|
||||
|
||||
regex_parser = BnfParser()
|
||||
foo_definition = regex_parser.parse(context, "'twenty' | 'thirty'").value.value
|
||||
bar_definition = regex_parser.parse(context, "foo ('one' | 'two')").value.value
|
||||
|
||||
concepts = {bar: bar_definition, foo: foo_definition}
|
||||
concept_parser = ConceptLexerParser()
|
||||
concept_parser.initialize(context, concepts)
|
||||
|
||||
res = concept_parser.parse(context, "twenty two")
|
||||
assert res.status
|
||||
assert res.value.body == [cnode("bar", 0, 2, "twenty two")]
|
||||
|
||||
res = concept_parser.parse(context, "thirty one")
|
||||
assert res.status
|
||||
assert res.value.body == [cnode("bar", 0, 2, "thirty one")]
|
||||
|
||||
res = concept_parser.parse(context, "twenty")
|
||||
assert res.status
|
||||
assert res.value.body == [cnode("foo", 0, 0, "twenty")]
|
||||
|
||||
|
||||
def test_i_cannot_parse_when_too_many_concepts():
|
||||
foo1 = Concept(name="foo", body="1")
|
||||
foo2 = Concept(name="foo", body="2")
|
||||
context = get_context()
|
||||
context.sheerka.cache_by_key["foo"] = [foo1, foo2]
|
||||
|
||||
regex_parser = BnfParser()
|
||||
res = regex_parser.parse(context, "foo")
|
||||
|
||||
assert not res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.CANNOT_RESOLVE_CONCEPT)
|
||||
assert res.value.body == ('key', 'foo')
|
||||
|
||||
|
||||
def test_i_cannot_parse_when_unknown_concept():
|
||||
context = get_context()
|
||||
|
||||
regex_parser = BnfParser()
|
||||
res = regex_parser.parse(get_context(), "foo")
|
||||
|
||||
assert not res.status
|
||||
assert context.sheerka.isinstance(res.value, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
assert res.value.body == ('key', 'foo')
|
||||
@@ -1,139 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
|
||||
from core.concept import Concept, ConceptParts
|
||||
from core.sheerka import Sheerka, ExecutionContext
|
||||
from evaluators.ConceptEvaluator import ConceptEvaluator
|
||||
from parsers.ExactConceptParser import ExactConceptParser
|
||||
from sdp.sheerkaDataProvider import Event
|
||||
|
||||
|
||||
def get_context():
|
||||
sheerka = Sheerka(skip_builtins_in_db=True)
|
||||
sheerka.initialize("mem://")
|
||||
return ExecutionContext("test", Event(), sheerka)
|
||||
|
||||
|
||||
def get_return_value(concept, source=None):
|
||||
return ReturnValueConcept(
|
||||
"some_name",
|
||||
True,
|
||||
ParserResultConcept(parser=ExactConceptParser(),
|
||||
source=source or concept.name,
|
||||
value=concept,
|
||||
try_parsed=concept))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ret_val, expected", [
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept(value=Concept())), True),
|
||||
(ReturnValueConcept("some_name", False, ParserResultConcept(value=Concept())), False),
|
||||
(ReturnValueConcept("some_name", True, ParserResultConcept(value="Not a concept")), False),
|
||||
(ReturnValueConcept("some_name", True, Concept()), False),
|
||||
])
|
||||
def test_i_can_match(ret_val, expected):
|
||||
context = get_context()
|
||||
assert ConceptEvaluator().matches(context, ret_val) == expected
|
||||
|
||||
|
||||
def test_i_can_evaluate_concept():
|
||||
context = get_context()
|
||||
concept = Concept(name="foo",
|
||||
where="1",
|
||||
pre="2",
|
||||
post="3").def_prop("a", "4").def_prop("b", "5")
|
||||
|
||||
evaluator = ConceptEvaluator()
|
||||
item = get_return_value(concept)
|
||||
result = evaluator.eval(context, item)
|
||||
|
||||
assert result.who == evaluator.name
|
||||
assert result.status
|
||||
assert result.value.name == "foo"
|
||||
assert result.value.get_metadata_value(ConceptParts.WHERE) == 1
|
||||
assert result.value.get_metadata_value(ConceptParts.PRE) == 2
|
||||
assert result.value.get_metadata_value(ConceptParts.POST) == 3
|
||||
assert result.value.get_prop("a") == 4
|
||||
assert result.value.get_prop("b") == 5
|
||||
assert result.value.key == "foo"
|
||||
assert result.parents == [item]
|
||||
|
||||
|
||||
def test_body_is_returned_when_defined_and_requested():
|
||||
context = get_context()
|
||||
concept = Concept(name="foo",
|
||||
body="'I have a value'",
|
||||
where="1",
|
||||
pre="2",
|
||||
post="3").set_prop("a", "4").set_prop("b", "5")
|
||||
|
||||
evaluator = ConceptEvaluator(return_body=True)
|
||||
item = get_return_value(concept)
|
||||
result = evaluator.eval(context, item)
|
||||
|
||||
assert result.who == evaluator.name
|
||||
assert result.status
|
||||
assert result.value == "I have a value"
|
||||
assert result.parents == [item]
|
||||
|
||||
|
||||
def test_body_is_not_returned_if_not_requested():
|
||||
context = get_context()
|
||||
concept = Concept(name="foo",
|
||||
body="'I have a value'",
|
||||
where="1",
|
||||
pre="2",
|
||||
post="3").set_prop("a", "4").set_prop("b", "5")
|
||||
|
||||
evaluator = ConceptEvaluator(return_body=False) # which is the default behaviour
|
||||
item = get_return_value(concept)
|
||||
result = evaluator.eval(context, item)
|
||||
|
||||
assert result.who == evaluator.name
|
||||
assert result.status
|
||||
assert result.value == concept
|
||||
assert result.parents == [item]
|
||||
|
||||
|
||||
def test_i_can_eval_if_with_the_same_name_is_defined_in_the_context():
|
||||
# If we evaluate Concept("foo", body="a").set_prop("a", "'property_a'")
|
||||
# ConceptEvaluator will be called to resolve 'a' while we know that 'a' refers to the string 'property_a'
|
||||
|
||||
context = get_context()
|
||||
context.obj = Concept("other").set_prop("foo", "'some_other_value'")
|
||||
concept = Concept(name="foo")
|
||||
|
||||
item = get_return_value(concept)
|
||||
result = ConceptEvaluator().eval(context, item)
|
||||
|
||||
assert result.status
|
||||
assert result.value == "'some_other_value'"
|
||||
|
||||
|
||||
def test_i_cannot_recognize_a_concept_if_one_of_the_prop_is_unknown():
|
||||
context = get_context()
|
||||
context.sheerka.add_in_cache(Concept(name="one").init_key())
|
||||
concept_plus = context.sheerka.add_in_cache(Concept(name="a plus b")
|
||||
.def_prop("a", "one")
|
||||
.def_prop("b", "two").init_key())
|
||||
|
||||
evaluator = ConceptEvaluator()
|
||||
item = get_return_value(concept_plus)
|
||||
result = evaluator.eval(context, item)
|
||||
|
||||
assert not result.status
|
||||
assert context.sheerka.isinstance(result.value, BuiltinConcepts.CONCEPT_EVAL_ERROR)
|
||||
assert result.value.property_name == "b"
|
||||
assert context.sheerka.isinstance(result.value.error, BuiltinConcepts.TOO_MANY_ERRORS)
|
||||
assert result.value.concept == concept_plus
|
||||
|
||||
|
||||
def test_that_error_concepts_are_transformed_into_errors_only_if_the_key_is_different():
|
||||
context = get_context()
|
||||
|
||||
error_concept = context.sheerka.new(BuiltinConcepts.ERROR)
|
||||
item = get_return_value(error_concept)
|
||||
result = ConceptEvaluator().eval(context, item)
|
||||
|
||||
assert not context.sheerka.is_success(error_concept) # it's indeed an error
|
||||
assert result.status
|
||||
assert result.value == error_concept
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user