Refactored to use a single implementation for concept evaluation

This commit is contained in:
2019-12-21 15:08:06 +01:00
parent b24b858b81
commit 41e0885486
17 changed files with 920 additions and 644 deletions
+48 -11
View File
@@ -41,10 +41,11 @@ class BuiltinConcepts(Enum):
INVALID_RETURN_VALUE = "invalid return value" # the return value of an evaluator is not correct INVALID_RETURN_VALUE = "invalid return value" # the return value of an evaluator is not correct
CONCEPT_ALREADY_DEFINED = "concept already defined" # when you try to add the same concept twice CONCEPT_ALREADY_DEFINED = "concept already defined" # when you try to add the same concept twice
NOP = "no operation" # no operation concept. Does nothing NOP = "no operation" # no operation concept. Does nothing
PROPERTY_EVAL_ERROR = "property evaluation error" # cannot evaluate a property of a concept CONCEPT_EVAL_ERROR = "concept evaluation error" # cannot evaluate a property or metadata of a concept
ENUMERATION = "enum" # represents a list or a set ENUMERATION = "enum" # represents a list or a set
LIST = "list" # represents a list LIST = "list" # represents a list
CANNOT_RESOLVE_VALUE_ERROR = "value cannot be resolved" # don't know how to find concept value CANNOT_RESOLVE_VALUE_ERROR = "value cannot be resolved" # don't know how to find concept value
CONCEPT_NOT_INITIALIZED = "concept not evaluated" # Try to work on a concept that is not evaluated
NODE = "node" NODE = "node"
GENERIC_NODE = "generic node" GENERIC_NODE = "generic node"
@@ -56,6 +57,21 @@ class BuiltinConcepts(Enum):
def __str__(self): def __str__(self):
return "__" + self.name return "__" + self.name
BuiltinErrors = [str(e) for e in {
BuiltinConcepts.ERROR,
BuiltinConcepts.UNKNOWN_CONCEPT,
BuiltinConcepts.CONCEPT_TOO_LONG,
BuiltinConcepts.UNKNOWN_PROPERTY,
BuiltinConcepts.TOO_MANY_SUCCESS,
BuiltinConcepts.TOO_MANY_ERRORS,
BuiltinConcepts.INVALID_RETURN_VALUE,
BuiltinConcepts.CONCEPT_ALREADY_DEFINED,
BuiltinConcepts.CONCEPT_EVAL_ERROR,
BuiltinConcepts.CANNOT_RESOLVE_VALUE_ERROR,
BuiltinConcepts.CONCEPT_NOT_INITIALIZED
}]
""" """
Some concepts have a specific implementation Some concepts have a specific implementation
It's mainly to a have proper __repr__ implementation, or because they are singleton (is_unique=True) It's mainly to a have proper __repr__ implementation, or because they are singleton (is_unique=True)
@@ -251,31 +267,52 @@ class AfterEvaluationConcept(Concept):
super().__init__(BuiltinConcepts.AFTER_EVALUATION, True, True, BuiltinConcepts.AFTER_EVALUATION) super().__init__(BuiltinConcepts.AFTER_EVALUATION, True, True, BuiltinConcepts.AFTER_EVALUATION)
class PropertyEvalError(Concept): class ConceptEvalError(Concept):
def __init__(self, property_name=None, concept=None, error=None): def __init__(self, error=None, concept=None, property_name=None):
super().__init__(BuiltinConcepts.PROPERTY_EVAL_ERROR, super().__init__(BuiltinConcepts.CONCEPT_EVAL_ERROR,
True, True,
False, False,
BuiltinConcepts.PROPERTY_EVAL_ERROR, BuiltinConcepts.CONCEPT_EVAL_ERROR,
property_name) error)
self.set_prop("concept", concept) self.set_prop("concept", concept)
self.set_prop("error", error) self.set_prop("property_name", property_name)
def __repr__(self): def __repr__(self):
return f"PropertyEvalError(property={self.property_name}, concept={self.concept}), error={self.error})" return f"ConceptEvalError(error={self.error}, concept={self.concept}, property={self.property_name})"
@property
def error(self):
return self.body
@property @property
def concept(self): def concept(self):
return self.props["concept"].value return self.props["concept"].value
@property @property
def error(self): def property_name(self):
return self.props["error"].value return self.props["property_name"].value
class ConceptNotInitialized(Concept):
def __init__(self, error=None, concept=None, property_name=None):
super().__init__(BuiltinConcepts.CONCEPT_NOT_INITIALIZED,
True,
False,
BuiltinConcepts.CONCEPT_NOT_INITIALIZED,
error)
self.set_prop("concept", concept)
def __repr__(self):
return f"ConceptNotInitialized(error={self.error}, concept={self.concept})"
@property @property
def property_name(self): def error(self):
return self.body return self.body
@property
def concept(self):
return self.props["concept"].value
class EnumerationConcept(Concept): class EnumerationConcept(Concept):
def __init__(self, iteration=None): def __init__(self, iteration=None):
+20 -2
View File
@@ -1,4 +1,6 @@
import ast import ast
import logging
import core.ast.nodes import core.ast.nodes
from core.ast.nodes import CallNodeConcept, GenericNodeConcept from core.ast.nodes import CallNodeConcept, GenericNodeConcept
from core.ast.visitors import UnreferencedNamesVisitor from core.ast.visitors import UnreferencedNamesVisitor
@@ -31,12 +33,13 @@ def is_same_success(sheerka, return_values):
return True return True
def expect_one(context, return_values): def expect_one(context, return_values, logger=None):
""" """
Checks if there is at least one success return value Checks if there is at least one success return value
If there is more than one, check if it's the same value If there is more than one, check if it's the same value
:param context: :param context:
:param return_values: :param return_values:
:param logger:
:return: :return:
""" """
@@ -58,7 +61,6 @@ def expect_one(context, return_values):
# remove errors when a winner is found # remove errors when a winner is found
if number_of_successful == 1: if number_of_successful == 1:
# log.debug(f"1 / {total_items} good item found.")
return sheerka.ret( return sheerka.ret(
context.who, context.who,
True, True,
@@ -74,6 +76,10 @@ def expect_one(context, return_values):
successful_results[0].value, successful_results[0].value,
parents=return_values) parents=return_values)
else: else:
if logger and logger.isEnabledFor(logging.DEBUG):
context.log(logger, f"Too many successful results found by expect_one()", context.who)
for s in successful_results:
context.log(logger, f"-> {s}", context.who)
return sheerka.ret( return sheerka.ret(
context.who, context.who,
False, False,
@@ -81,6 +87,18 @@ def expect_one(context, return_values):
parents=return_values) parents=return_values)
# only errors, i cannot help you # only errors, i cannot help you
if logger and logger.isEnabledFor(logging.DEBUG):
context.log(logger, f"Too many errors found by expect_one()", context.who)
for s in successful_results:
context.log(logger, f"-> {s}", context.who)
if len(return_values) == 1:
return sheerka.ret(
context.who,
False,
return_values[0],
parents=return_values)
else:
return sheerka.ret( return sheerka.ret(
context.who, context.who,
False, False,
+1 -1
View File
@@ -6,7 +6,6 @@ from core.sheerka_logger import get_logger
import core.utils import core.utils
from core.tokenizer import Tokenizer, TokenKind from core.tokenizer import Tokenizer, TokenKind
PROPERTIES_FOR_DIGEST = ("name", "key", PROPERTIES_FOR_DIGEST = ("name", "key",
"definition", "definition_type", "definition", "definition_type",
"is_builtin", "is_unique", "is_builtin", "is_unique",
@@ -44,6 +43,7 @@ class ConceptMetadata:
definition_type: str # definition can be done with something else than regex definition_type: str # definition can be done with something else than regex
desc: str # possible description for the concept desc: str # possible description for the concept
id: str # unique identifier for a concept. The id will never be modified (but the key can) id: str # unique identifier for a concept. The id will never be modified (but the key can)
is_evaluated: bool = False # True is the concept is evaluated by sheerka.eval_concept()
class Concept: class Concept:
+67 -27
View File
@@ -1,6 +1,6 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
from core.builtin_concepts import BuiltinConcepts, ErrorConcept, ReturnValueConcept from core.builtin_concepts import BuiltinConcepts, ErrorConcept, ReturnValueConcept, BuiltinErrors
from core.concept import Concept, ConceptParts, PROPERTIES_FOR_DIGEST from core.concept import Concept, ConceptParts, PROPERTIES_FOR_DIGEST
from parsers.BaseParser import BaseParser from parsers.BaseParser import BaseParser
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event, SheerkaDataProviderDuplicateKeyError from sdp.sheerkaDataProvider import SheerkaDataProvider, Event, SheerkaDataProviderDuplicateKeyError
@@ -11,7 +11,10 @@ from core.sheerka_logger import console_handler, get_logger
import logging import logging
concept_evaluation_steps = [BuiltinConcepts.EVALUATION, BuiltinConcepts.AFTER_EVALUATION] CONCEPT_EVALUATION_STEPS = [
BuiltinConcepts.BEFORE_EVALUATION,
BuiltinConcepts.EVALUATION,
BuiltinConcepts.AFTER_EVALUATION]
CONCEPT_LEXER_PARSER_CLASS = "parsers.ConceptLexerParser.ConceptLexerParser" CONCEPT_LEXER_PARSER_CLASS = "parsers.ConceptLexerParser.ConceptLexerParser"
DEBUG_TAB_SIZE = 4 DEBUG_TAB_SIZE = 4
@@ -416,17 +419,21 @@ class Sheerka(Concept):
source = getattr(concept.metadata, part_key.value) source = getattr(concept.metadata, part_key.value)
if source is None or not isinstance(source, str) or source == "": if source is None or not isinstance(source, str) or source == "":
# the only sources that I am sure to parse are strings # the only sources that I am sure to parse are strings
# I refuse empty strings for performance, I don't want to handle useless NOPConcepts # I refuse empty strings for performance matters, I don't want to handle useless NOPConcepts
continue continue
else: else:
to_parse = self.ret(context.who, True, self.new(BuiltinConcepts.USER_INPUT, body=source)) to_parse = self.ret(context.who, True, self.new(BuiltinConcepts.USER_INPUT, body=source))
concept.cached_asts[part_key] = self.execute(context, to_parse, steps, logger) concept.cached_asts[part_key] = self.execute(context, to_parse, steps, logger)
for prop in concept.props: for prop in concept.props:
to_parse = self.ret(context.who, True, self.new(BuiltinConcepts.USER_INPUT, body=concept.props[prop].value)) if concept.props[prop].value:
to_parse = self.ret(
context.who,
True,
self.new(BuiltinConcepts.USER_INPUT, body=concept.props[prop].value))
concept.cached_asts[prop] = self.execute(context, to_parse, steps) concept.cached_asts[prop] = self.execute(context, to_parse, steps)
# updates the code of the reference when possible # Updates the cache of concepts when possible
if concept.key in self.concepts_cache: if concept.key in self.concepts_cache:
entry = self.concepts_cache[concept.key] entry = self.concepts_cache[concept.key]
if isinstance(entry, list): if isinstance(entry, list):
@@ -435,32 +442,69 @@ class Sheerka(Concept):
else: else:
self.concepts_cache[concept.key].cached_asts = concept.cached_asts self.concepts_cache[concept.key].cached_asts = concept.cached_asts
def eval_concept(self, context, concept: Concept, properties_to_eval=None, logger=None): def evaluate_concept(self, context, concept: Concept, logger=None):
""" """
Evaluation a concept Evaluation a concept
It means that if the where clause is True, will evaluate the body It means that if the where clause is True, will evaluate the body
:param context: :param context:
:param concept: :param concept:
:param properties_to_eval:
:param logger: :param logger:
:return: :return: value of the evaluation or error
""" """
if concept.metadata.is_evaluated:
return concept
def _resolve(resolve_context, return_value):
r = self.execute(resolve_context, return_value, CONCEPT_EVALUATION_STEPS, logger or self.log)
return core.builtin_helpers.expect_one(context, r)
# 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
#
if len(concept.cached_asts) == 0: if len(concept.cached_asts) == 0:
self.initialize_concept_asts(context, concept, logger) self.initialize_concept_asts(context, concept, logger)
if properties_to_eval is None: # to make sure of the order, it don't use ConceptParts.get_parts()
properties_to_eval = ["where", "pre", "post", "body", "props"] # props must be evaluated first
properties_to_eval = ["props", "where", "pre", "post", "body"]
for prop in properties_to_eval: for prop_to_eval in properties_to_eval:
if prop == "props": if prop_to_eval == "props":
pass for prop_name in (p for p in concept.props if p in concept.cached_asts):
sub_context = context.push(desc=f"Evaluating property '{prop_name}'")
res = _resolve(sub_context, concept.cached_asts[prop_name])
if res.status:
concept.set_prop(prop_name, res.value)
else: else:
part_key = ConceptParts(prop) return self.new(BuiltinConcepts.CONCEPT_EVAL_ERROR,
if concept.cached_asts[part_key] is None: body=res.value,
continue concept=concept,
res = self.execute(context, concept.cached_asts[part_key], concept_evaluation_steps, logger) property_name=prop_name)
res = core.builtin_helpers.expect_one(context, res) else:
setattr(concept.metadata, prop, res.value) part_key = ConceptParts(prop_to_eval)
if part_key in concept.cached_asts and concept.cached_asts[part_key] is not None:
sub_context = context.push(desc=f"Evaluating '{part_key}'", obj=concept)
res = _resolve(sub_context, concept.cached_asts[part_key])
if res.status:
setattr(concept.metadata, prop_to_eval, res.value)
else:
return self.new(BuiltinConcepts.CONCEPT_EVAL_ERROR,
body=res.value,
concept=concept,
property_name=part_key)
#
# TODO : Validate the POST condition
#
concept.init_key() # only does it if needed
concept.metadata.is_evaluated = True
return concept
def add_in_cache(self, concept: Concept): def add_in_cache(self, concept: Concept):
""" """
@@ -601,16 +645,16 @@ class Sheerka(Concept):
return (self.value(obj) for obj in objs) return (self.value(obj) for obj in objs)
def is_success(self, obj): def is_success(self, obj):
if isinstance(obj, bool): if isinstance(obj, bool): # quick win
return obj return obj
if self.isinstance(obj, BuiltinConcepts.RETURN_VALUE): if isinstance(obj, ReturnValueConcept):
return obj.status return obj.status
if self.isinstance(obj, BuiltinConcepts.ERROR): if isinstance(obj, Concept) and obj.metadata.is_builtin and obj.key in BuiltinErrors:
return False return False
return False return obj
def isinstance(self, a, b): def isinstance(self, a, b):
""" """
@@ -724,10 +768,6 @@ class Sheerka(Concept):
log_format = "%(message)s" log_format = "%(message)s"
log_level = logging.INFO log_level = logging.INFO
# logging.root.setLevel(log_level)
# fmt = logging.Formatter(log_format, None, "%")
# console_handler.setFormatter(fmt)
logging.basicConfig(format=log_format, level=log_level, handlers=[console_handler]) logging.basicConfig(format=log_format, level=log_level, handlers=[console_handler])
+8
View File
@@ -195,6 +195,14 @@ class Tokenizer:
yield Token(TokenKind.AMPER, "&", self.i, self.line, self.column) yield Token(TokenKind.AMPER, "&", self.i, self.line, self.column)
self.i += 1 self.i += 1
self.column += 1 self.column += 1
elif c == "<":
yield Token(TokenKind.LESS, "<", self.i, self.line, self.column)
self.i += 1
self.column += 1
elif c == ">":
yield Token(TokenKind.GREATER, ">", self.i, self.line, self.column)
self.i += 1
self.column += 1
elif c == "\n" or c == "\r": elif c == "\n" or c == "\r":
newline = self.eat_newline(self.i) newline = self.eat_newline(self.i)
yield Token(TokenKind.NEWLINE, newline, self.i, self.line, self.column) yield Token(TokenKind.NEWLINE, newline, self.i, self.line, self.column)
+4 -2
View File
@@ -3,6 +3,7 @@ from core.builtin_concepts import ParserResultConcept, ReturnValueConcept
from core.builtin_helpers import get_names from core.builtin_helpers import get_names
from core.concept import Concept from core.concept import Concept
from evaluators.BaseEvaluator import OneReturnValueEvaluator from evaluators.BaseEvaluator import OneReturnValueEvaluator
from parsers.BaseParser import NotInitializedNode
from parsers.ConceptLexerParser import ParsingExpression, ParsingExpressionVisitor from parsers.ConceptLexerParser import ParsingExpression, ParsingExpressionVisitor
from parsers.DefaultParser import DefConceptNode from parsers.DefaultParser import DefConceptNode
@@ -58,7 +59,7 @@ class AddConceptEvaluator(OneReturnValueEvaluator):
# put back the sources # put back the sources
part_ret_val = getattr(def_concept_node, prop) part_ret_val = getattr(def_concept_node, prop)
if not isinstance(part_ret_val, ReturnValueConcept) or not part_ret_val.status: if not isinstance(part_ret_val, ReturnValueConcept) or not part_ret_val.status:
continue # not quite sure that it's possible continue # Nothing to do is not initialized
# update the parts # update the parts
source = self.get_source(part_ret_val) source = self.get_source(part_ret_val)
@@ -82,7 +83,8 @@ class AddConceptEvaluator(OneReturnValueEvaluator):
# finish initialisation # finish initialisation
concept.init_key(def_concept_node.name.tokens) concept.init_key(def_concept_node.name.tokens)
concept.add_codes(def_concept_node.get_asts()) concept.add_codes(def_concept_node.get_asts())
if sheerka.is_success(def_concept_node.definition): if not isinstance(def_concept_node.definition, NotInitializedNode) and \
sheerka.is_success(def_concept_node.definition):
concept.bnf = def_concept_node.definition.value.value concept.bnf = def_concept_node.definition.value.value
ret = sheerka.create_new_concept(context, concept) ret = sheerka.create_new_concept(context, concept)
+15 -33
View File
@@ -1,5 +1,4 @@
from core.builtin_concepts import ParserResultConcept, BuiltinConcepts from core.builtin_concepts import ParserResultConcept, BuiltinConcepts
import core.builtin_helpers
from core.concept import Concept, ConceptParts from core.concept import Concept, ConceptParts
from evaluators.BaseEvaluator import OneReturnValueEvaluator from evaluators.BaseEvaluator import OneReturnValueEvaluator
@@ -31,42 +30,25 @@ class ConceptEvaluator(OneReturnValueEvaluator):
concept = return_value.value.value concept = return_value.value.value
context.log(self.verbose_log, f"Evaluating concept {concept}.", self.name) context.log(self.verbose_log, f"Evaluating concept {concept}.", self.name)
# pre condition should already be validated by the parser. # If the concept that is requested is in the context(at least its name), drop the call.
# It's a mandatory condition for the concept before it can be recognized # Why ?
# If we evaluate Concept("foo", body="a").set_prop("a", "'property_a'")
# The body should be 'property_a', and not a concept called a in our universe
if context.obj and concept.name in context.obj.props:
return sheerka.ret(self.name, False, sheerka.new(BuiltinConcepts.NOT_FOR_ME), parents=[return_value])
if len(concept.cached_asts) == 0: evaluated = sheerka.evaluate_concept(context, concept, self.verbose_log)
sheerka.initialize_concept_asts(context, concept, self.verbose_log)
# TODO; check pre if evaluated.key != concept.key:
# if pre is not true, return Concept with a false value # evaluated.key != concept.key means that we have transformed the concept
# When you successfully evaluate an error, the status should not be false
# Evaluate the properties
for prop in concept.props:
sub_context = context.push(self.name, desc=f"Evaluating property '{prop}'", obj=concept)
res = self.evaluate_parsing(sheerka, sub_context, concept.cached_asts[prop])
if res.status:
concept.set_prop(prop, res.value)
else:
return sheerka.ret( return sheerka.ret(
self.name, self.name,
False, False,
sheerka.new(BuiltinConcepts.PROPERTY_EVAL_ERROR, body=prop, concept=concept, error=res.value), evaluated,
parents=[return_value]) parents=[return_value])
# Returns the concept when no body if ConceptParts.BODY not in evaluated.cached_asts:
if ConceptParts.BODY not in concept.cached_asts: return sheerka.ret(self.name, True, evaluated, parents=[return_value])
return sheerka.ret(self.name, True, concept, parents=[return_value]) else:
return sheerka.ret(self.name, True, evaluated.body, parents=[return_value])
# Evaluate the body otherwise
body = concept.cached_asts[ConceptParts.BODY]
if body is None:
raise NotImplementedError("Seems weird !")
sub_context = context.push(self.name, desc="Evaluating body", obj=concept)
res = self.evaluate_parsing(sheerka, sub_context, body)
return sheerka.ret(self.name, res.status, res.value, parents=[return_value])
def evaluate_parsing(self, sheerka, context, parsing_result):
res = sheerka.execute(context, parsing_result, self.evaluation_steps, self.log)
res = core.builtin_helpers.expect_one(context, res)
return res
+2 -1
View File
@@ -54,7 +54,8 @@ class ConceptNodeEvaluator(OneReturnValueEvaluator):
def update_concept(self, sheerka, concept, underlying, init_empty_body=True): def update_concept(self, sheerka, concept, underlying, init_empty_body=True):
""" """
Updates the property of the concept Updates the properties of the concept
Goes in recursion if the property is a concept
""" """
def _add_prop(c, prop_name, value): def _add_prop(c, prop_name, value):
+12 -5
View File
@@ -28,6 +28,7 @@ class PythonEvaluator(OneReturnValueEvaluator):
node = return_value.value.value node = return_value.value.value
try: try:
context.log(self.verbose_log, f"Evaluating python node {node}.", self.name) context.log(self.verbose_log, f"Evaluating python node {node}.", self.name)
my_locals = self.get_locals(context, node.ast_) my_locals = self.get_locals(context, node.ast_)
context.log(self.verbose_log, f"locals={my_locals}", self.name) context.log(self.verbose_log, f"locals={my_locals}", self.name)
@@ -51,6 +52,7 @@ class PythonEvaluator(OneReturnValueEvaluator):
if context.obj: if context.obj:
context.log(self.verbose_log, context.log(self.verbose_log,
f"Concept '{context.obj}' is in context. Adding its properties to locals if any.", self.name) f"Concept '{context.obj}' is in context. Adding its properties to locals if any.", self.name)
for prop_name, prop_value in context.obj.props.items(): for prop_name, prop_value in context.obj.props.items():
my_locals[prop_name] = prop_value.value my_locals[prop_name] = prop_value.value
@@ -60,18 +62,23 @@ class PythonEvaluator(OneReturnValueEvaluator):
for name in unreferenced_names_visitor.names: for name in unreferenced_names_visitor.names:
context.log(self.verbose_log, f"Resolving '{name}'.", self.name) context.log(self.verbose_log, f"Resolving '{name}'.", self.name)
if name in my_locals:
context.log(self.verbose_log, f"Using value from property.", self.name)
continue
concept = context.sheerka.new(name) concept = context.sheerka.new(name)
if context.sheerka.isinstance(concept, BuiltinConcepts.UNKNOWN_CONCEPT): if context.sheerka.isinstance(concept, BuiltinConcepts.UNKNOWN_CONCEPT):
context.log(self.verbose_log, f"'{name}' is not a concept. Skipping.", self.name) context.log(self.verbose_log, f"'{name}' is not a concept. Skipping.", self.name)
continue continue
context.log(self.verbose_log, f"'{name}' is a concept. Evaluating body.", self.name) context.log(self.verbose_log, f"'{name}' is a concept. Evaluating.", self.name)
sub_context = context.push(self.name, desc=f"Evaluating {concept}'s body", obj=concept) sub_context = context.push(self.name, desc=f"Evaluating '{concept}'", obj=concept)
sub_context.log_new(self.verbose_log) sub_context.log_new(self.verbose_log)
context.sheerka.eval_concept(sub_context, concept, ["body"], self.verbose_log) evaluated = context.sheerka.evaluate_concept(sub_context, concept, self.verbose_log)
if not context.sheerka.isa(concept.body, BuiltinConcepts.ERROR): if evaluated.key == concept.key:
my_locals[name] = concept.body my_locals[name] = evaluated.body or evaluated
return my_locals return my_locals
+1 -73
View File
@@ -371,7 +371,7 @@ class DefaultParser(BaseParser):
self.sheerka.new(BuiltinConcepts.USER_INPUT, body=tokens)) self.sheerka.new(BuiltinConcepts.USER_INPUT, body=tokens))
steps = [BuiltinConcepts.PARSING] steps = [BuiltinConcepts.PARSING]
parsed = self.sheerka.execute(new_context, to_parse, steps, self.verbose_log) parsed = self.sheerka.execute(new_context, to_parse, steps, self.verbose_log)
parsing_result = core.builtin_helpers.expect_one(new_context, parsed) parsing_result = core.builtin_helpers.expect_one(new_context, parsed, self.verbose_log)
if not parsing_result.status: if not parsing_result.status:
self.add_error(parsing_result.value) self.add_error(parsing_result.value)
continue continue
@@ -379,75 +379,3 @@ class DefaultParser(BaseParser):
asts_found_by_parts[keyword] = parsing_result asts_found_by_parts[keyword] = parsing_result
return asts_found_by_parts return asts_found_by_parts
# def parse_expression(self):
# return self.parse_addition()
#
# def parse_addition(self):
# left = self.parse_multiply()
# token = self.get_token()
# if token is None or token.type == TokenKind.EOF:
# return left
#
# if token.type == TokenKind.NUMBER: # example 15 +5 or 15 -5
# right = self.parse_addition()
# return BinaryNode(self.collect_tokens(left, token, right), TokenKind.PLUS, left, right)
#
# if token.type not in (TokenKind.PLUS, TokenKind.MINUS):
# return left
#
# self.next_token()
# right = self.parse_addition()
# return BinaryNode(self.collect_tokens(left, token, right), token.type, left, right)
#
# def parse_multiply(self):
# left = self.parse_atom()
# token = self.get_token()
# if token is None or token.type == TokenKind.EOF:
# return left
#
# if token.type not in (TokenKind.STAR, TokenKind.SLASH):
# return left
#
# self.next_token()
# right = self.parse_multiply()
# return BinaryNode(self.collect_tokens(left, token, right), token.type, left, right)
#
# def parse_atom(self):
# token = self.get_token()
# if token.type == TokenKind.NUMBER:
# self.next_token()
# return NumberNode([token], float(token.value) if '.' in token.value else int(token.value))
# elif token.type == TokenKind.STRING:
# self.next_token()
# return StringNode([token], token.value[1:-1], token.value[0])
# elif token.type == TokenKind.IDENTIFIER:
# if token.value == "true":
# self.next_token()
# return TrueNode([token])
# elif token.value == "false":
# self.next_token()
# return FalseNode([token])
# elif token.value == "null":
# self.next_token()
# return NullNode([token])
# else:
# self.next_token()
# return VariableNode([token], token.value)
# elif token.type == TokenKind.LPAR:
# self.next_token()
# exp = self.parse_expression()
# token = self.get_token()
# self.next_token()
#
# if token.type != TokenKind.RPAR:
# error = UnexpectedTokenErrorNode([token], "Right parenthesis not found.", [TokenKind.RPAR])
# self.add_error(error)
# return error
#
# return exp
# else:
# error = UnexpectedTokenErrorNode([token], "Unexpected token",
# [TokenKind.NUMBER, TokenKind.STRING, TokenKind.IDENTIFIER, "true", "false",
# "null", TokenKind.LPAR])
# return self.add_error(error)
+39 -130
View File
@@ -35,9 +35,12 @@ def test_i_can_match(ret_val, expected):
assert ConceptEvaluator().matches(context, ret_val) == expected assert ConceptEvaluator().matches(context, ret_val) == expected
def test_concept_is_returned_when_no_body(): def test_i_can_evaluate_concept():
context = get_context() context = get_context()
concept = Concept(name="one").init_key() concept = Concept(name="foo",
where="1",
pre="2",
post="3").set_prop("a", "4").set_prop("b", "5")
evaluator = ConceptEvaluator() evaluator = ConceptEvaluator()
item = get_return_value(concept) item = get_return_value(concept)
@@ -45,13 +48,20 @@ def test_concept_is_returned_when_no_body():
assert result.who == evaluator.name assert result.who == evaluator.name
assert result.status assert result.status
assert result.value == concept assert result.value == Concept(name="foo",
where=1,
pre=2,
post=3).set_prop("a", 4).set_prop("b", 5).init_key()
assert result.parents == [item] assert result.parents == [item]
def test_body_is_evaluated_when_python_body(): def test_body_is_returned_when_defined():
context = get_context() context = get_context()
concept = Concept(name="one", body="1").init_key() 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() evaluator = ConceptEvaluator()
item = get_return_value(concept) item = get_return_value(concept)
@@ -59,136 +69,23 @@ def test_body_is_evaluated_when_python_body():
assert result.who == evaluator.name assert result.who == evaluator.name
assert result.status assert result.status
assert result.value == 1 assert result.value == "I have a value"
assert result.parents == [item] assert result.parents == [item]
def test_body_is_evaluated_when_concept_body(): def test_i_cannot_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 = get_context()
concept_one = Concept(name="one").init_key() context.obj = Concept("other").set_prop("foo", "'some_other_value'")
context.sheerka.add_in_cache(concept_one) concept = Concept(name="foo")
concept_un = Concept(name="un", body="one").init_key()
evaluator = ConceptEvaluator() item = get_return_value(concept)
item = get_return_value(concept_un) result = ConceptEvaluator().eval(context, item)
result = evaluator.eval(context, item)
assert result.who == evaluator.name assert not result.status
assert result.status assert context.sheerka.isinstance(result.value, BuiltinConcepts.NOT_FOR_ME)
assert result.value == concept_one
assert result.parents == [item]
def test_body_is_evaluated_when_concept_body_with_a_body():
"""
The concept refers to another concept which has a body
:return:
"""
context = get_context()
concept_one = Concept(name="one", body="1").init_key()
context.sheerka.add_in_cache(concept_one)
concept_un = Concept(name="un", body="one").init_key()
evaluator = ConceptEvaluator()
item = get_return_value(concept_un)
result = evaluator.eval(context, item)
assert result.who == evaluator.name
assert result.status
assert result.value == 1
assert result.parents == [item]
def test_i_can_evaluate_longer_chains():
context = get_context()
context.sheerka.add_in_cache(Concept(name="a", body="'a'").init_key())
context.sheerka.add_in_cache(Concept(name="b", body="a").init_key())
context.sheerka.add_in_cache(Concept(name="c", body="b").init_key())
concept_d = context.sheerka.add_in_cache(Concept(name="d", body="c").init_key())
evaluator = ConceptEvaluator()
item = get_return_value(concept_d)
result = evaluator.eval(context, item)
assert result.status
assert result.value == 'a'
def test_i_can_evaluate_longer_chains_2():
context = get_context()
concept_a = context.sheerka.add_in_cache(Concept(name="a").init_key())
context.sheerka.add_in_cache(Concept(name="b", body="a").init_key())
context.sheerka.add_in_cache(Concept(name="c", body="b").init_key())
concept_d = context.sheerka.add_in_cache(Concept(name="d", body="c").init_key())
evaluator = ConceptEvaluator()
item = get_return_value(concept_d)
result = evaluator.eval(context, item)
assert result.status
assert result.value == concept_a
def test_i_can_recognize_concept_properties():
"""
The concept 'plus' has some properties.
Let's check if they are recognized as concepts
:return:
"""
context = get_context()
concept_one = context.sheerka.add_in_cache(Concept(name="one").init_key())
concept_two = context.sheerka.add_in_cache(Concept(name="two").init_key())
concept_plus = context.sheerka.add_in_cache(Concept(name="a plus b")
.set_prop("a", "one")
.set_prop("b", "two").init_key())
evaluator = ConceptEvaluator()
item = get_return_value(concept_plus)
result = evaluator.eval(context, item)
assert result.status
assert context.sheerka.isinstance(result.value, concept_plus)
assert result.value.props["a"].value == concept_one
assert result.value.props["b"].value == concept_two
def test_i_can_recognize_concept_properties_with_body():
"""
The concept 'plus' has some properties.
Let's check if they are recognized as concepts with Python code
:return:
"""
context = get_context()
context.sheerka.add_in_cache(Concept(name="one", body="1").init_key())
context.sheerka.add_in_cache(Concept(name="two", body="2").init_key())
concept_plus = context.sheerka.add_in_cache(Concept(name="a plus b")
.set_prop("a", "one")
.set_prop("b", "two").init_key())
evaluator = ConceptEvaluator()
item = get_return_value(concept_plus)
result = evaluator.eval(context, item)
assert result.status
assert context.sheerka.isinstance(result.value, concept_plus)
assert result.value.props["a"].value == 1
assert result.value.props["b"].value == 2
def test_i_can_recognize_concept_properties_with_body_when_concept_has_a_body():
context = get_context()
context.sheerka.add_in_cache(Concept(name="one", body="1").init_key())
context.sheerka.add_in_cache(Concept(name="two", body="2").init_key())
concept_plus = context.sheerka.add_in_cache(Concept(name="a plus b", body="a + b")
.set_prop("a", "one")
.set_prop("b", "two").init_key())
evaluator = ConceptEvaluator()
item = get_return_value(concept_plus)
result = evaluator.eval(context, item)
assert result.status
assert result.value == 3
def test_i_cannot_recognize_a_concept_if_one_of_the_prop_is_unknown(): def test_i_cannot_recognize_a_concept_if_one_of_the_prop_is_unknown():
@@ -203,7 +100,19 @@ def test_i_cannot_recognize_a_concept_if_one_of_the_prop_is_unknown():
result = evaluator.eval(context, item) result = evaluator.eval(context, item)
assert not result.status assert not result.status
assert context.sheerka.isinstance(result.value, BuiltinConcepts.PROPERTY_EVAL_ERROR) assert context.sheerka.isinstance(result.value, BuiltinConcepts.CONCEPT_EVAL_ERROR)
assert result.value.property_name == "b" assert result.value.property_name == "b"
assert context.sheerka.isinstance(result.value.error, BuiltinConcepts.TOO_MANY_ERRORS) assert context.sheerka.isinstance(result.value.error, BuiltinConcepts.TOO_MANY_ERRORS)
assert result.value.concept == concept_plus 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
+54 -6
View File
@@ -1,6 +1,6 @@
import pytest import pytest
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
from core.sheerka import Sheerka, ExecutionContext from core.sheerka import Sheerka, ExecutionContext
from core.concept import Concept from core.concept import Concept
from evaluators.PythonEvaluator import PythonEvaluator from evaluators.PythonEvaluator import PythonEvaluator
@@ -39,31 +39,79 @@ def test_i_can_eval(text, expected):
assert evaluated.value == expected assert evaluated.value == expected
def test_i_can_eval_expression_with_variables(): def test_i_can_eval_expression_that_references_concepts():
context = get_context()
context.sheerka.add_in_cache(Concept("foo"))
parsed = PythonParser().parse(context, "foo")
evaluated = PythonEvaluator().eval(context, parsed)
assert evaluated.status
assert evaluated.value == Concept("foo").init_key()
def test_i_can_eval_expression_that_references_concepts_with_body():
""" """
I can test expression with variables I can test expression with variables
:return: :return:
""" """
context = get_context() context = get_context()
context.sheerka.add_in_cache(Concept("foo", body="2")) context.sheerka.add_in_cache(Concept("foo", body="2"))
parsed = PythonParser().parse(context, "foo + 2")
parsed = PythonParser().parse(context, "foo")
evaluated = PythonEvaluator().eval(context, parsed) evaluated = PythonEvaluator().eval(context, parsed)
assert evaluated.status assert evaluated.status
assert evaluated.value == 4 assert evaluated.value == 2
def test_i_can_eval_module_with_variables(): def test_i_can_eval_module_with_that_references_concepts():
"""
I can test modules with variables
:return:
"""
context = get_context()
context.sheerka.add_in_cache(Concept("foo"))
parsed = PythonParser().parse(context, "def a(b):\n return b\na(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():
""" """
I can test modules with variables I can test modules with variables
:return: :return:
""" """
context = get_context() context = get_context()
context.sheerka.add_in_cache(Concept("foo", body="2")) context.sheerka.add_in_cache(Concept("foo", body="2"))
parsed = PythonParser().parse(context, "def a(b):\n return b\na(foo)")
parsed = PythonParser().parse(context, "def a(b):\n return b\na(foo)")
evaluated = PythonEvaluator().eval(context, parsed) evaluated = PythonEvaluator().eval(context, parsed)
assert evaluated.status assert evaluated.status
assert evaluated.value == 2 assert evaluated.value == 2
def test_i_can_eval_concept_with_props():
context = get_context()
context.sheerka.add_in_cache(Concept("foo").set_prop("prop", "'a'"))
parsed = PythonParser().parse(context, "foo")
evaluated = PythonEvaluator().eval(context, parsed)
assert evaluated.status
assert evaluated.value == Concept("foo").set_prop("prop", "a").init_key() # evaluated version of foo
def test_i_cannot_eval_when_body_references_unknown_concept():
context = get_context()
context.sheerka.add_in_cache(Concept("foo", body="bar"))
parsed = PythonParser().parse(context, "foo")
evaluated = PythonEvaluator().eval(context, parsed)
assert not evaluated.status
assert context.sheerka.isinstance(evaluated.value, BuiltinConcepts.ERROR)
+2 -3
View File
@@ -46,12 +46,11 @@ def test_i_can_use_expect_when_only_errors_1():
sheerka = get_sheerka() sheerka = get_sheerka()
items = [ items = [
ReturnValueConcept("who", False, None), ReturnValueConcept("who", False, sheerka.new(BuiltinConcepts.ERROR)),
] ]
res = core.builtin_helpers.expect_one(get_context(sheerka), items) res = core.builtin_helpers.expect_one(get_context(sheerka), items)
assert not res.status assert not res.status
assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_ERRORS) assert res.value, items[0]
assert res.value.body == items
assert res.parents == items assert res.parents == items
+225 -338
View File
@@ -30,6 +30,32 @@ def init_test():
os.chdir(current_pwd) os.chdir(current_pwd)
def get_sheerka(use_dict=True, skip_builtins_in_db=True):
root = "mem://" if use_dict else root_folder
sheerka = Sheerka(skip_builtins_in_db=skip_builtins_in_db)
sheerka.initialize(root)
return sheerka
def get_context(sheerka):
return ExecutionContext("test", "xxx", sheerka)
def get_default_concept():
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.set_prop("a", "value1")
concept.set_prop("b", "value2")
return concept
class ConceptWithGetValue(Concept): class ConceptWithGetValue(Concept):
def get_value(self): def get_value(self):
return self.get_prop("my_prop") return self.get_prop("my_prop")
@@ -260,7 +286,7 @@ def test_instances_are_different_when_asking_for_new():
def test_i_get_the_same_instance_when_is_unique_is_true(): def test_i_get_the_same_instance_when_is_unique_is_true():
sheerka = get_sheerka() sheerka = get_sheerka()
concept = get_unique_concept() concept = Concept(name="unique", is_unique=True)
sheerka.create_new_concept(get_context(sheerka), concept) sheerka.create_new_concept(get_context(sheerka), concept)
new1 = sheerka.new(concept.key, a=10, b="value") new1 = sheerka.new(concept.key, a=10, b="value")
@@ -320,389 +346,250 @@ def test_list_of_concept_is_sorted_by_id():
assert concepts[0].id < concepts[-1].id assert concepts[0].id < concepts[-1].id
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @pytest.mark.parametrize("body, expected", [
# (None, None),
# E V A L U A T I O N S ("", ""),
# ("1", 1),
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@pytest.mark.parametrize("text, expected", [
("1+1", 2), ("1+1", 2),
("sheerka.test()", 'I have access to Sheerka !') ("'one'", "one"),
("'one' + 'two'", "onetwo"),
("True", True),
("1 > 2", False),
]) ])
def test_i_can_eval_python_expressions_with_no_variable(text, expected): def test_i_can_evaluate_a_concept_with_simple_body(body, expected):
sheerka = get_sheerka() sheerka = get_sheerka()
res = sheerka.evaluate_user_input(text) concept = Concept("foo", body=body).init_key()
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert len(res) == 1 assert evaluated.key == concept.key
assert res[0].status assert evaluated.body == expected
assert res[0].value == expected 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
def test_i_can_eval_concept_with_python_body(): @pytest.mark.parametrize("expr, expected", [
sheerka = get_sheerka() ("", ""),
concept = Concept(name="one", body="1") ("1", 1),
sheerka.add_in_cache(concept) ("1+1", 2),
("'one'", "one"),
text = "one" ("'one' + 'two'", "onetwo"),
res = sheerka.evaluate_user_input(text) ("True", True),
assert len(res) == 1 ("1 > 2", False),
assert res[0].status ])
assert res[0].value == 1 def test_i_can_evaluate_the_other_metadata(expr, expected):
"""
I only test WHERE, it's the same for the others
def test_i_can_eval_concept_with_concept_body(): :param expr:
sheerka = get_sheerka() :param expected:
concept_one = Concept(name="one") :return:
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 sheerka.isinstance(return_value, concept_one)
def test_i_can_eval_concept_with_no_body():
sheerka = 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():
sheerka = 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():
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 = get_default_concept()
expected.metadata.id = "1001"
expected.metadata.desc = None
expected.init_key()
sheerka = get_sheerka() sheerka = get_sheerka()
res = sheerka.evaluate_user_input(text)
assert len(res) == 1 concept = Concept("foo", where=expr).init_key()
assert res[0].status evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert sheerka.isinstance(res[0].value, BuiltinConcepts.NEW_CONCEPT)
concept_saved = res[0].value.body assert evaluated.key == concept.key
assert evaluated.body is None
for prop in PROPERTIES_TO_SERIALIZE: assert evaluated.metadata.pre is None
assert getattr(concept_saved.metadata, prop) == getattr(expected.metadata, prop) assert evaluated.metadata.post is None
assert evaluated.metadata.where == expected
assert concept_saved.key in sheerka.concepts_cache assert evaluated.props == {}
assert sheerka.sdp.io.exists( assert evaluated.metadata.is_evaluated
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(): @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(expr, expected):
sheerka = get_sheerka()
concept = Concept("foo").set_prop("a", expr).init_key()
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.key == concept.key
assert evaluated.body 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_props_are_evaluated_before_body():
sheerka = get_sheerka()
concept = Concept("foo", body="a+1").set_prop("a", "10").init_key()
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.key == concept.key
assert evaluated.body == 11
def test_i_can_evaluate_when_another_concept_is_referenced():
sheerka = get_sheerka()
concept_a = Concept("a")
sheerka.add_in_cache(concept_a)
concept = Concept("foo", body="a").init_key()
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.key == concept.key
assert sheerka.isinstance(evaluated.body, concept_a)
assert id(evaluated.body) != id(concept_a)
assert evaluated.metadata.is_evaluated
def test_i_can_evaluate_when_the_referenced_concept_has_a_body():
sheerka = get_sheerka()
concept_a = Concept("a", body="1")
sheerka.add_in_cache(concept_a)
concept = Concept("foo", body="a").init_key()
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.key == concept.key
assert evaluated.body == 1
assert not concept_a.metadata.is_evaluated #
def test_i_can_evaluate_concept_of_concept_when_the_leaf_has_a_body():
sheerka = get_sheerka()
sheerka.add_in_cache(Concept(name="a", body="'a'").init_key())
sheerka.add_in_cache(Concept(name="b", body="a").init_key())
sheerka.add_in_cache(Concept(name="c", body="b").init_key())
concept_d = sheerka.add_in_cache(Concept(name="d", body="c").init_key())
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept_d)
assert evaluated.key == concept_d.key
assert evaluated.body == 'a'
def test_i_can_evaluate_concept_of_concept_does_not_have_a_body():
sheerka = get_sheerka()
concept_a = sheerka.add_in_cache(Concept(name="a").init_key())
sheerka.add_in_cache(Concept(name="b", body="a").init_key())
sheerka.add_in_cache(Concept(name="c", body="b").init_key())
concept_d = sheerka.add_in_cache(Concept(name="d", body="c").init_key())
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept_d)
assert evaluated.key == concept_d.key
assert sheerka.isinstance(evaluated.body, concept_a)
def test_i_can_evaluate_concept_when_properties_reference_others_concepts():
sheerka = get_sheerka()
concept_a = sheerka.add_in_cache(Concept(name="a").init_key())
concept = Concept("foo", body="a").set_prop("a", "a").init_key()
evaluated = sheerka.evaluate_concept(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_2():
""" """
In this test, we test that the properties of 'concept a xx b' (which are 'a' and 'b') Same test,
are correctly detected, thanks to the source code 'a plus b' in its body but the name of the property and the name of the concept are different
:return: :return:
""" """
sheerka = get_sheerka() sheerka = get_sheerka()
concept_a = sheerka.add_in_cache(Concept(name="a").init_key())
# concept 'a plus b' is known concept = Concept("foo", body="concept_a").set_prop("concept_a", "a").init_key()
concept_a_plus_b = Concept(name="a plus b").set_prop("a").set_prop("b") evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
sheerka.add_in_cache(concept_a_plus_b)
res = sheerka.evaluate_user_input("def concept a xx b as a plus b") assert evaluated.key == concept.key
expected = Concept(name="a xx b", body="a plus b").set_prop("a").set_prop("b").init_key() assert evaluated.body == concept_a
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.concepts_cache
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(): def test_i_can_evaluate_concept_when_properties_reference_others_concepts_with_body():
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 = get_sheerka() sheerka = get_sheerka()
sheerka.evaluate_user_input(text) sheerka.add_in_cache(Concept(name="a", body="1").init_key())
res = sheerka.evaluate_user_input(text) sheerka.add_in_cache(Concept(name="b", body="2").init_key())
assert len(res) == 1 concept = Concept("foo", body="propA + propB").set_prop("propA", "a").set_prop("propB", "b").init_key()
assert not res[0].status evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert sheerka.isinstance(res[0].value, BuiltinConcepts.CONCEPT_ALREADY_DEFINED)
assert evaluated.key == concept.key
assert evaluated.body == 3
def test_i_can_disable_an_evaluator(): def test_i_can_reference_sheerka():
sheerka = get_sheerka()
concept = Concept(name="one", body="1")
sheerka.add_in_cache(concept)
text = "one"
p = next(e for e in sheerka.evaluators if e.__name__ == "PythonEvaluator")
p.enabled = False # not that you disable the class, not the instance
res = sheerka.evaluate_user_input(text)
assert len(res) == 1
assert res[0].status
assert sheerka.isinstance(res[0].value, BuiltinConcepts.PARSER_RESULT)
p.enabled = True # put back for the remaining unit tests
@pytest.mark.parametrize("text", [
"",
" ",
"\n",
])
def test_i_can_eval_a_empty_input(text):
sheerka = get_sheerka() sheerka = get_sheerka()
res = sheerka.evaluate_user_input(text) concept = Concept("foo", body="sheerka.test()").init_key()
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert len(res) == 1 assert evaluated.key == concept.key
assert res[0].status assert evaluated.body == sheerka.test()
assert sheerka.isinstance(res[0].value, BuiltinConcepts.NOP)
def test_i_can_eval_concept_with_variable(): def test_properties_values_takes_precedence_over_the_outside_world():
sheerka = get_sheerka() sheerka = get_sheerka()
concept_hello = Concept(name="hello a").set_prop("a") sheerka.add_in_cache(Concept(name="a", body="'concept_a'").init_key())
concept_foo = Concept(name="foo") sheerka.add_in_cache(Concept(name="b", body="'concept_b'").init_key())
sheerka.add_in_cache(concept_hello)
sheerka.add_in_cache(concept_foo)
res = sheerka.evaluate_user_input("hello foo") concept = Concept("foo", body="a").init_key()
return_value = res[0].value evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert len(res) == 1 assert evaluated.key == concept.key
assert res[0].status assert evaluated.body == 'concept_a' # this test was already done
assert sheerka.isinstance(return_value, concept_hello)
assert return_value.props["a"].value == concept_foo # so check this one.
concept = Concept("foo", body="a").set_prop("a", "'property_a'").init_key()
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.key == concept.key
assert evaluated.body == 'property_a'
# or this one.
concept = Concept("foo", body="a").set_prop("a", "b").init_key()
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.key == concept.key
assert evaluated.body == 'concept_b'
def test_i_can_eval_concept_with_variable_and_python_as_body(): def test_i_can_reference_sub_property_of_a_property():
sheerka = get_sheerka() sheerka = get_sheerka()
sheerka.add_in_cache(Concept(name="hello a", body="'hello ' + a").set_prop("a")) sheerka.add_in_cache(Concept(name="concept_a").set_prop("subProp", "'sub_a'").init_key())
sheerka.add_in_cache(Concept(name="foo", body="'foo'"))
res = sheerka.evaluate_user_input("hello foo") concept = Concept("foo", body="a.props['subProp'].value").set_prop("a", "concept_a").init_key()
assert len(res) == 1 evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert res[0].status assert evaluated.key == concept.key
assert res[0].value, "hello foo" assert evaluated.body == 'sub_a'
def test_i_can_eval_duplicate_concepts_with_same_value(): def test_i_cannot_evaluate_concept_if_property_is_in_error():
sheerka = get_sheerka() sheerka = get_sheerka()
sheerka.add_in_cache(Concept(name="hello a", body="'hello ' + a").set_prop("a")) concept = Concept(name="concept_a").set_prop("subProp", "undef_concept").init_key()
sheerka.add_in_cache(Concept(name="hello foo", body="'hello foo'")) evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
sheerka.add_in_cache(Concept(name="foo", body="'foo'")) assert sheerka.isinstance(evaluated, BuiltinConcepts.CONCEPT_EVAL_ERROR)
res = sheerka.evaluate_user_input("hello foo")
assert len(res) == 1
assert res[0].status
assert res[0].value, "hello foo"
assert res[0].who == sheerka.get_evaluator_name(MultipleSameSuccessEvaluator.NAME)
def test_i_cannot_manage_duplicate_concepts_when_the_values_are_different(): def test_key_is_initialized_by_evaluation():
sheerka = get_sheerka() sheerka = get_sheerka()
sheerka.add_in_cache(Concept(name="hello a", body="'hello ' + a").set_prop("a")) concept = Concept("foo")
sheerka.add_in_cache(Concept(name="hello foo", body="'hello foo'")) evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
sheerka.add_in_cache(Concept(name="foo", body="'another value'"))
res = sheerka.evaluate_user_input("hello foo") assert evaluated.key == concept.init_key().key
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)
assert sorted_values[0].value == "hello another value"
assert sorted_values[1].value == "hello foo"
def test_i_can_manage_concepts_with_the_same_key_when_values_are_the_same(): def test_builtin_error_concept_are_errors():
# only test a random one, it will be the same for the others
sheerka = get_sheerka() sheerka = get_sheerka()
context = get_context(sheerka) assert not sheerka.is_success(sheerka.new(BuiltinConcepts.TOO_MANY_SUCCESS))
sheerka.create_new_concept(context, Concept(name="hello a", body="'hello ' + a").set_prop("a"))
sheerka.create_new_concept(context, Concept(name="hello b", body="'hello ' + b").set_prop("b"))
res = sheerka.evaluate_user_input("hello 'foo'")
assert len(res) == 1
assert res[0].status
assert res[0].value == "hello foo"
assert res[0].who == sheerka.get_evaluator_name(MultipleSameSuccessEvaluator.NAME)
def test_i_can_create_concepts_with_python_code_as_body():
sheerka = get_sheerka()
context = 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, list)
def test_i_can_create_concept_with_bnf_definition():
sheerka = 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(
ConceptMatch("a", rule_name="a"),
Optional(Sequence(StrMatch("plus"), ConceptMatch("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():
sheerka = 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():
sheerka = 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.props["a"] == Property("a", sheerka.new(concept_a.key, body="one"))
def test_i_can_eval_bnf_definitions_from_separate_instances():
"""
Same test then before,
but make sure that the BNF are correctly persisted and loaded
"""
sheerka = get_sheerka(False)
concept_a = sheerka.evaluate_user_input("def concept a from bnf 'one' 'two'")[0].body.body
res = 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 = 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 = 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)
def get_sheerka(use_dict=True, skip_builtins_in_db=True):
root = "mem://" if use_dict else root_folder
sheerka = Sheerka(skip_builtins_in_db=skip_builtins_in_db)
sheerka.initialize(root)
return sheerka
def get_context(sheerka):
return ExecutionContext("test", "xxx", sheerka)
def get_default_concept():
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.set_prop("a", "value1")
concept.set_prop("b", "value2")
return concept
def get_unique_concept():
return Concept(name="unique", is_unique=True)
+408
View File
@@ -0,0 +1,408 @@
import pytest
import os
from os import path
import shutil
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept
from core.concept import Concept, PROPERTIES_TO_SERIALIZE, Property
from core.sheerka import Sheerka, ExecutionContext
from evaluators.MutipleSameSuccessEvaluator import MultipleSameSuccessEvaluator
from parsers.ConceptLexerParser import Sequence, ZeroOrMore, StrMatch, OrderedChoice, Optional, ConceptMatch, \
ConceptLexerParser
from sdp.sheerkaDataProvider import SheerkaDataProvider
tests_root = path.abspath("../build/tests")
root_folder = "init_folder"
@pytest.fixture(autouse=True)
def init_test():
if path.exists(tests_root):
shutil.rmtree(tests_root)
if not path.exists(tests_root):
os.makedirs(tests_root)
current_pwd = os.getcwd()
os.chdir(tests_root)
yield None
os.chdir(current_pwd)
@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(text, expected):
sheerka = 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():
sheerka = 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 == 1
def test_i_can_eval_concept_with_concept_body():
sheerka = 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 sheerka.isinstance(return_value, concept_one)
def test_i_can_eval_concept_with_no_body():
sheerka = 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():
sheerka = 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():
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 = get_default_concept()
expected.metadata.id = "1001"
expected.metadata.desc = None
expected.init_key()
sheerka = 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.concepts_cache
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():
"""
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 = get_sheerka()
# concept 'a plus b' is known
concept_a_plus_b = Concept(name="a plus b").set_prop("a").set_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").set_prop("a").set_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.concepts_cache
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():
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 = 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)
def test_i_can_disable_an_evaluator():
sheerka = get_sheerka()
concept = Concept(name="one", body="1")
sheerka.add_in_cache(concept)
text = "one"
p = next(e for e in sheerka.evaluators if e.__name__ == "PythonEvaluator")
p.enabled = False # not that you disable the class, not the instance
res = sheerka.evaluate_user_input(text)
assert len(res) == 1
assert res[0].status
assert sheerka.isinstance(res[0].value, BuiltinConcepts.PARSER_RESULT)
p.enabled = True # put back for the remaining unit tests
@pytest.mark.parametrize("text", [
"",
" ",
"\n",
])
def test_i_can_eval_a_empty_input(text):
sheerka = 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():
sheerka = get_sheerka()
concept_hello = Concept(name="hello a").set_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():
sheerka = get_sheerka()
sheerka.add_in_cache(Concept(name="hello a", body="'hello ' + a").set_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 res[0].value, "hello foo"
def test_i_can_eval_duplicate_concepts_with_same_value():
sheerka = get_sheerka()
sheerka.add_in_cache(Concept(name="hello a", body="'hello ' + a").set_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, "hello foo"
assert res[0].who == sheerka.get_evaluator_name(MultipleSameSuccessEvaluator.NAME)
def test_i_cannot_manage_duplicate_concepts_when_the_values_are_different():
sheerka = get_sheerka()
sheerka.add_in_cache(Concept(name="hello a", body="'hello ' + a").set_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)
assert sorted_values[0].value == "hello another value"
assert sorted_values[1].value == "hello foo"
def test_i_can_manage_concepts_with_the_same_key_when_values_are_the_same():
sheerka = get_sheerka()
context = get_context(sheerka)
sheerka.create_new_concept(context, Concept(name="hello a", body="'hello ' + a").set_prop("a"))
sheerka.create_new_concept(context, Concept(name="hello b", body="'hello ' + b").set_prop("b"))
res = sheerka.evaluate_user_input("hello 'foo'")
assert len(res) == 1
assert res[0].status
assert res[0].value == "hello foo"
assert res[0].who == sheerka.get_evaluator_name(MultipleSameSuccessEvaluator.NAME)
def test_i_can_create_concepts_with_python_code_as_body():
sheerka = get_sheerka()
context = 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, list)
def test_i_can_create_concept_with_bnf_definition():
sheerka = 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(
ConceptMatch("a", rule_name="a"),
Optional(Sequence(StrMatch("plus"), ConceptMatch("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():
sheerka = 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():
sheerka = 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.props["a"] == Property("a", sheerka.new(concept_a.key, body="one"))
def test_i_can_eval_bnf_definitions_from_separate_instances():
"""
Same test then before,
but make sure that the BNF are correctly persisted and loaded
"""
sheerka = get_sheerka(False)
concept_a = sheerka.evaluate_user_input("def concept a from bnf 'one' 'two'")[0].body.body
res = 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 = 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 = 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)
def get_sheerka(use_dict=True, skip_builtins_in_db=True):
root = "mem://" if use_dict else root_folder
sheerka = Sheerka(skip_builtins_in_db=skip_builtins_in_db)
sheerka.initialize(root)
return sheerka
def get_context(sheerka):
return ExecutionContext("test", "xxx", sheerka)
def get_default_concept():
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.set_prop("a", "value1")
concept.set_prop("b", "value2")
return concept
+3 -1
View File
@@ -3,7 +3,7 @@ from core.tokenizer import Tokenizer, Token, TokenKind, LexerError, Keywords
def test_i_can_tokenize(): def test_i_can_tokenize():
source = "+*-/{}[]() ,;:.?\n\n\r\r\r\nidentifier_0\t \t10.15 10 'string\n' \"another string\"=|&" source = "+*-/{}[]() ,;:.?\n\n\r\r\r\nidentifier_0\t \t10.15 10 'string\n' \"another string\"=|&<>"
tokens = list(Tokenizer(source)) tokens = list(Tokenizer(source))
assert tokens[0] == Token(TokenKind.PLUS, "+", 0, 1, 1) assert tokens[0] == Token(TokenKind.PLUS, "+", 0, 1, 1)
assert tokens[1] == Token(TokenKind.STAR, "*", 1, 1, 2) assert tokens[1] == Token(TokenKind.STAR, "*", 1, 1, 2)
@@ -37,6 +37,8 @@ def test_i_can_tokenize():
assert tokens[29] == Token(TokenKind.EQUALS, '=', 76, 6, 18) assert tokens[29] == Token(TokenKind.EQUALS, '=', 76, 6, 18)
assert tokens[30] == Token(TokenKind.VBAR, '|', 77, 6, 19) assert tokens[30] == Token(TokenKind.VBAR, '|', 77, 6, 19)
assert tokens[31] == Token(TokenKind.AMPER, '&', 78, 6, 20) assert tokens[31] == Token(TokenKind.AMPER, '&', 78, 6, 20)
assert tokens[32] == Token(TokenKind.LESS, '<', 79, 6, 21)
assert tokens[33] == Token(TokenKind.GREATER, '>', 80, 6, 22)
@pytest.mark.parametrize("text, expected", [ @pytest.mark.parametrize("text, expected", [