Refactored Concept class for better separation of metadata, compiled and values

This commit is contained in:
2020-01-17 17:27:54 +01:00
parent 3789ef25d1
commit a7b239c167
27 changed files with 614 additions and 349 deletions
+4 -1
View File
@@ -86,7 +86,7 @@ class CallNodeConcept(NodeConcept):
super().__init__(BuiltinConcepts.IDENTIFIER_NODE, "Call", parent)
def get_args_names(self, sheerka):
return sheerka.values(self.get_prop("args"))
return sheerka.get_values(self.get_prop("args"))
def python_to_concept(python_node):
@@ -105,6 +105,7 @@ def python_to_concept(python_node):
continue
value = getattr(node, field)
concept.def_prop(field)
if isinstance(value, list):
lst = ListConcept().init_key()
for i in value:
@@ -114,6 +115,8 @@ def python_to_concept(python_node):
concept.set_prop(field, _transform(value, NodeParent(concept, field)))
else:
concept.set_prop(field, value)
concept.metadata.is_evaluated = True
return concept
return _transform(python_node, None)
+1 -1
View File
@@ -84,7 +84,7 @@ class UnreferencedNamesVisitor(ConceptNodeVisitor):
if node.get_node_type() == "FunctionDef":
# variable defined as a function parameter
args = node.get_prop("args")
args_values = list(self.sheerka.values(args.get_prop("args")))
args_values = list(self.sheerka.get_values(args.get_prop("args")))
if variable_name in args_values:
return True
+32 -15
View File
@@ -1,6 +1,6 @@
from enum import Enum
from core.concept import Concept
from core.concept import Concept, ConceptParts
class BuiltinConcepts(Enum):
@@ -97,8 +97,10 @@ It's mainly to ease the usage
class UserInputConcept(Concept):
def __init__(self, text=None, user_name=None):
super().__init__(BuiltinConcepts.USER_INPUT, True, False, BuiltinConcepts.USER_INPUT, text)
super().__init__(BuiltinConcepts.USER_INPUT, True, False, BuiltinConcepts.USER_INPUT)
self.set_metadata_value(ConceptParts.BODY, text)
self.set_prop("user_name", user_name)
self.metadata.is_evaluated = True
@property
def text(self):
@@ -114,7 +116,9 @@ class UserInputConcept(Concept):
class ErrorConcept(Concept):
def __init__(self, error=None):
super().__init__(BuiltinConcepts.ERROR, True, False, BuiltinConcepts.ERROR, error)
super().__init__(BuiltinConcepts.ERROR, True, False, BuiltinConcepts.ERROR)
self.set_metadata_value(ConceptParts.BODY, error)
self.metadata.is_evaluated = True
def __repr__(self):
return f"({self.id}){self.name}: {self.body}"
@@ -127,11 +131,13 @@ class ReturnValueConcept(Concept):
"""
def __init__(self, who=None, status=None, value=None, message=None, parents=None):
super().__init__(BuiltinConcepts.RETURN_VALUE, True, False, BuiltinConcepts.RETURN_VALUE, value)
super().__init__(BuiltinConcepts.RETURN_VALUE, True, False, BuiltinConcepts.RETURN_VALUE)
self.set_metadata_value(ConceptParts.BODY, value)
self.set_prop("who", who)
self.set_prop("status", status)
self.set_prop("message", message)
self.set_prop("parents", parents)
self.metadata.is_evaluated = True
@property
def who(self):
@@ -155,7 +161,7 @@ class ReturnValueConcept(Concept):
@value.setter
def value(self, value):
self.metadata.body = value
self.set_metadata_value(ConceptParts.BODY, value)
@property
def message(self):
@@ -200,8 +206,10 @@ class UnknownPropertyConcept(Concept):
"""
def __init__(self, property_name=None, concept=None):
super().__init__(BuiltinConcepts.UNKNOWN_PROPERTY, True, False, BuiltinConcepts.UNKNOWN_PROPERTY, property_name)
super().__init__(BuiltinConcepts.UNKNOWN_PROPERTY, True, False, BuiltinConcepts.UNKNOWN_PROPERTY)
self.set_metadata_value(ConceptParts.BODY, property_name)
self.set_prop("concept", concept)
self.metadata.is_evaluated = True
def __repr__(self):
return f"UnknownProperty(property={self.property_name}, concept={self.concept})"
@@ -221,10 +229,12 @@ class ParserResultConcept(Concept):
"""
def __init__(self, parser=None, source=None, value=None, try_parsed=None):
super().__init__(BuiltinConcepts.PARSER_RESULT, True, False, BuiltinConcepts.PARSER_RESULT, value)
super().__init__(BuiltinConcepts.PARSER_RESULT, True, False, BuiltinConcepts.PARSER_RESULT)
self.set_metadata_value(ConceptParts.BODY, value)
self.set_prop("parser", parser)
self.set_prop("source", source)
self.set_prop("try_parsed", try_parsed) # in case of error, what was found before the error
self.metadata.is_evaluated = True
def __repr__(self):
text = f"ParserResult(parser={self.props['parser'].value}"
@@ -273,9 +283,10 @@ class InvalidReturnValueConcept(Concept):
BuiltinConcepts.INVALID_RETURN_VALUE,
True,
False,
BuiltinConcepts.INVALID_RETURN_VALUE,
return_value)
BuiltinConcepts.INVALID_RETURN_VALUE)
self.set_metadata_value(ConceptParts.BODY, return_value)
self.set_prop("evaluator", evaluator)
self.metadata.is_evaluated = True
class ConceptEvalError(Concept):
@@ -283,10 +294,11 @@ class ConceptEvalError(Concept):
super().__init__(BuiltinConcepts.CONCEPT_EVAL_ERROR,
True,
False,
BuiltinConcepts.CONCEPT_EVAL_ERROR,
error)
BuiltinConcepts.CONCEPT_EVAL_ERROR)
self.set_metadata_value(ConceptParts.BODY, error)
self.set_prop("concept", concept)
self.set_prop("property_name", property_name)
self.metadata.is_evaluated = True
def __repr__(self):
return f"ConceptEvalError(error={self.error}, concept={self.concept}, property={self.property_name})"
@@ -306,7 +318,9 @@ class ConceptEvalError(Concept):
class EnumerationConcept(Concept):
def __init__(self, iteration=None):
super().__init__(BuiltinConcepts.ENUMERATION, True, False, BuiltinConcepts.ENUMERATION, iteration)
super().__init__(BuiltinConcepts.ENUMERATION, True, False, BuiltinConcepts.ENUMERATION)
self.set_metadata_value(ConceptParts.BODY, iteration)
self.metadata.is_evaluated = True
def __iter__(self):
return iter(self.body)
@@ -314,7 +328,9 @@ class EnumerationConcept(Concept):
class ListConcept(Concept):
def __init__(self, items=None):
super().__init__(BuiltinConcepts.LIST, True, False, BuiltinConcepts.LIST, items or [])
super().__init__(BuiltinConcepts.LIST, True, False, BuiltinConcepts.LIST)
self.set_metadata_value(ConceptParts.BODY, items or [])
self.metadata.is_evaluated = True
def append(self, obj):
self.body.append(obj)
@@ -340,9 +356,10 @@ class ConceptAlreadyInSet(Concept):
super().__init__(BuiltinConcepts.CONCEPT_ALREADY_IN_SET,
True,
False,
BuiltinConcepts.CONCEPT_ALREADY_IN_SET,
concept)
BuiltinConcepts.CONCEPT_ALREADY_IN_SET)
self.set_metadata_value(ConceptParts.BODY, concept)
self.set_prop("concept_set", concept_set)
self.metadata.is_evaluated = True
def __repr__(self):
return f"ConceptAlreadyInSet(concept={self.concept}, concept_set={self.concept_set})"
+4 -3
View File
@@ -121,10 +121,11 @@ def get_names(sheerka, concept_node):
def extract_predicates(sheerka, expression, variables_to_include, variables_to_exclude):
"""
from expression, tries to find all the predicates referencing a variable, and the variable only
from a given expression and a variable (or list of variables)
tries to find out all the predicates referencing the(se) variable(s), and the(se) variable(s) solely
for example
exp : isinstance(a, int) and isinstance(b, str)
will return 'isinstance(a, int)' if variable_name == 'a'
exp : isinstance(a, int) and isinstance(b, str)
will return 'isinstance(a, int)' if variable_name == 'a'
:param sheerka:
:param expression:
:param variables_to_include:
+126 -29
View File
@@ -1,5 +1,6 @@
import hashlib
from dataclasses import dataclass
from collections import namedtuple
from dataclasses import dataclass, field
from enum import Enum
from core.sheerka_logger import get_logger
@@ -10,7 +11,7 @@ PROPERTIES_FOR_DIGEST = ("name", "key",
"definition", "definition_type",
"is_builtin", "is_unique",
"where", "pre", "post", "body",
"desc")
"desc", "props")
PROPERTIES_TO_SERIALIZE = PROPERTIES_FOR_DIGEST + tuple(["id"])
PROPERTIES_FOR_NEW = ("where", "pre", "post", "body", "desc")
VARIABLE_PREFIX = "__var__"
@@ -44,9 +45,13 @@ class ConceptMetadata:
definition_type: str # definition can be done with something else than regex
desc: str # possible description for the concept
id: str # unique identifier for a concept. The id will never be modified (but the key can)
props: list # list properties, with their default values
is_evaluated: bool = False # True is the concept is evaluated by sheerka.eval_concept()
simplec = namedtuple("concept", "name body") # for simple concept (tests purposes only)
class Concept:
"""
Default concept object
@@ -65,7 +70,8 @@ class Concept:
definition=None,
definition_type=None,
desc=None,
id=None):
id=None,
props=None):
metadata = ConceptMetadata(
str(name) if name else None,
@@ -79,12 +85,14 @@ class Concept:
definition,
definition_type,
desc,
id
id,
props or []
)
self.metadata = metadata
self.props = {} # list of Property for this concept
self.cached_asts = {} # cached ast for the where, pre, post and body parts
self.compiled = {} # cached ast for the where, pre, post and body parts
self.values = {} # values of metadata once resolved
self.props = {} # resolved properties of this concept
self.bnf = None
self.log = get_logger("core." + self.__class__.__name__)
self.init_log = get_logger("init.core." + self.__class__.__name__)
@@ -93,10 +101,17 @@ class Concept:
return f"({self.metadata.id}){self.metadata.name}"
def __eq__(self, other):
if isinstance(other, simplec):
return self.name == other.name and self.body == other.body
if id(self) == id(other):
return True
if not isinstance(other, Concept):
return False
# check the attributes
# check the metadata
for prop in PROPERTIES_TO_SERIALIZE:
# print(prop) # use full to know which id does not match
my_value = getattr(self.metadata, prop)
@@ -119,9 +134,19 @@ class Concept:
if my_value != other_value:
return False
# check the props (Concept variables)
for var_name, p in self.props.items():
if p != other.props[var_name]:
# checks the values
if len(self.values) != len(other.values):
return False
for metadata in self.values:
if self.get_metadata_value(metadata) != other.get_metadata_value(metadata):
return False
if len(self.props) != len(other.props):
return False
for prop in self.props:
if self.get_prop(prop) != other.get_prop(prop):
return False
return True
@@ -138,6 +163,33 @@ class Concept:
name = self.name if 'metadata' in vars(self) else 'Concept'
raise AttributeError(f"'{name}' concept has no attribute '{item}'")
def def_prop(self, prop_name: str, default_value=None):
"""
Adds a property to the metadata
:param prop_name:
:param default_value:
:return:
"""
assert default_value is None or isinstance(default_value, str) # default properties will have to be evaluated
self.metadata.props.append((prop_name, default_value))
self.props[prop_name] = Property(prop_name, None) # do not set the default value
# why not setting props to the default values ?
# Because it may not be the real values, as metadata.props need to be evaluated
return self
def def_prop_by_index(self, index: int, value):
"""
Re-assign a value to a property (mainly used by ExactConceptParser)
:param index:
:param value:
:return:
"""
assert value is None or isinstance(value, str) # default properties will have to be evaluated
prop = self.metadata.props[index]
self.metadata.props[index] = (prop[0], value)
return self
@property
def name(self):
return self.metadata.name
@@ -165,7 +217,7 @@ class Concept:
if tokens is None:
tokens = list(Tokenizer(self.metadata.name))
variables = list(self.props.keys()) if len(core.utils.strip_tokens(tokens, True)) > 1 else []
variables = [p[0] for p in self.metadata.props] if len(core.utils.strip_tokens(tokens, True)) > 1 else []
key = ""
first = True
@@ -175,8 +227,8 @@ class Concept:
if token.type == TokenKind.WHITESPACE:
continue
if not first:
key += " " # spaces are normalized
if variables is not None and token.value in variables:
key += " " # spaces are normalized
if token.value in variables:
key += VARIABLE_PREFIX + str(variables.index(token.value))
else:
key += token.value[1:-1] if token.type == TokenKind.STRING else token.value
@@ -187,7 +239,7 @@ class Concept:
@property
def body(self):
return self.metadata.body
return self.values[ConceptParts.BODY] if ConceptParts.BODY in self.values else None
def add_codes(self, codes):
"""
@@ -204,7 +256,7 @@ class Concept:
return
for key in codes:
self.cached_asts[key] = codes[key]
self.compiled[key] = codes[key]
return self
@@ -224,7 +276,6 @@ class Concept:
props_to_use = props_to_use or PROPERTIES_TO_SERIALIZE
props_as_dict = dict((prop, getattr(self.metadata, prop)) for prop in props_to_use)
props_as_dict["props"] = [(p, self.props[p].value) for p in self.props]
return props_as_dict
def from_dict(self, as_dict):
@@ -235,10 +286,11 @@ class Concept:
"""
for prop in PROPERTIES_TO_SERIALIZE:
if prop in as_dict:
setattr(self.metadata, prop, as_dict[prop])
if "props" in as_dict:
for n, v in as_dict["props"]:
self.set_prop(n, v)
if prop == "props":
for name, value in as_dict[prop]:
self.def_prop(name, value)
else:
setattr(self.metadata, prop, as_dict[prop])
return self
def update_from(self, other):
@@ -252,24 +304,69 @@ class Concept:
if other is None:
return self
if id(other) == id(self):
return self
# update metadata
self.from_dict(other.to_dict())
# for prop in self.props_to_serialize:
# setattr(self, prop, getattr(other, prop))
# update values
for k, v in other.values.items():
self.values[k] = v
# update properties
for k, v in other.props.items():
self.set_prop(k, v.value)
return self
def set_prop(self, prop_name: str, prop_value=None):
self.props[prop_name] = Property(prop_name, prop_value) # Python 3.x order is kept in dictionaries
return self
def set_prop_by_index(self, index: int, prop_value):
prop_name = list(self.props.keys())[index]
def set_prop(self, prop_name: str, prop_value):
"""Directly sets a value to a property"""
self.props[prop_name] = Property(prop_name, prop_value)
return self
def get_prop(self, prop_name: str):
return self.props[prop_name].value
def set_metadata_value(self, metadata: ConceptParts, value):
"""
Set the resolved value of a metadata (not the metadata itself)
:param metadata:
:param value:
:return:
"""
self.values[metadata] = value
def get_metadata_value(self, metadata: ConceptParts):
"""
Gets the resolved value of a metadata
:param metadata:
:return:
"""
return self.values[metadata]
def auto_init(self):
"""
Sometimes (for tests purposes)
You don't need the full process of evaluation to to get the values of the concept
Directly use the values of the metadata
:return:
"""
if self.metadata.is_evaluated:
return self
for metadata in ConceptParts:
value = getattr(self.metadata, metadata.value)
if value is not None:
self.values[metadata] = value
for prop, value in self.metadata.props:
self.set_prop(prop, value)
self.metadata.is_evaluated = True
return self
class Property:
"""
@@ -303,6 +400,6 @@ class DoNotResolve:
For example, if you want to set a value to the BODY that will not change when
when the concept will be evaluated,
set concept.cached_asts[BODY] to DoNotResolve(value)
set concept.compiled[BODY] to DoNotResolve(value)
"""
value: object
+44 -37
View File
@@ -571,40 +571,41 @@ class Sheerka(Concept):
"""
steps = [BuiltinConcepts.BEFORE_PARSING, BuiltinConcepts.PARSING, BuiltinConcepts.AFTER_PARSING]
for part_key in ConceptParts:
if part_key in concept.cached_asts:
if part_key in concept.compiled:
continue
source = getattr(concept.metadata, part_key.value)
if source is None or not isinstance(source, str) or source == "":
# the only sources that I am sure to parse are strings
# I refuse empty strings for performance matters, I don't want to handle useless NOPConcepts
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 AST for {part_key}") as sub_context:
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.ret(context.who, True, self.new(BuiltinConcepts.USER_INPUT, body=source))
res = self.execute(sub_context, to_parse, steps, logger)
concept.cached_asts[part_key] = res
concept.compiled[part_key] = res
sub_context.add_values(return_values=res)
for prop in concept.props:
if prop in concept.cached_asts:
for prop, default_value in concept.metadata.props:
if prop in concept.compiled:
continue
value = concept.props[prop].value
if value:
if isinstance(value, Concept):
concept.cached_asts[prop] = value
else:
to_parse = self.ret(
context.who,
True,
self.new(BuiltinConcepts.USER_INPUT, body=value))
with context.push(desc=f"Initializing AST for property {prop}") as sub_context:
sub_context.log_new(logger)
res = self.execute(context, to_parse, steps)
concept.cached_asts[prop] = res
sub_context.add_values(return_values=res)
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.ret(context.who, True, self.new(BuiltinConcepts.USER_INPUT, body=default_value))
res = self.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.concepts_cache:
@@ -613,7 +614,7 @@ class Sheerka(Concept):
# TODO : manage when there are multiple entries
pass
else:
self.concepts_cache[concept.key].cached_asts = concept.cached_asts
self.concepts_cache[concept.key].compiled = concept.compiled
def evaluate_concept(self, context, concept: Concept, logger=None):
"""
@@ -706,8 +707,8 @@ class Sheerka(Concept):
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.cached_asts):
prop_ast = concept.cached_asts[prop_name]
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):
resolved = _resolve_list(context.sheerka, prop_ast, prop_name, None)
@@ -719,13 +720,13 @@ class Sheerka(Concept):
concept.set_prop(prop_name, resolved)
else:
part_key = ConceptParts(metadata_to_eval)
if part_key in concept.cached_asts and concept.cached_asts[part_key] is not None:
metadata_ast = concept.cached_asts[part_key]
if part_key in concept.compiled and concept.compiled[part_key] is not None:
metadata_ast = concept.compiled[part_key]
resolved = _resolve(metadata_ast, part_key, concept)
if context.sheerka.isinstance(resolved, BuiltinConcepts.CONCEPT_EVAL_ERROR):
return resolved
else:
setattr(concept.metadata, metadata_to_eval, resolved)
concept.values[part_key] = resolved
#
# TODO : Validate the POST condition
@@ -789,7 +790,8 @@ class Sheerka(Concept):
unknown_concept = Concept()
template = self.concepts_cache[str(BuiltinConcepts.UNKNOWN_CONCEPT)]
unknown_concept.update_from(template)
unknown_concept.metadata.body = concept_key
unknown_concept.set_metadata_value(ConceptParts.BODY, concept_key)
unknown_concept.metadata.is_evaluated = True
return unknown_concept
def new(self, concept_key, **kwargs):
@@ -812,13 +814,13 @@ class Sheerka(Concept):
concept_key != BuiltinConcepts.UNKNOWN_CONCEPT:
return template
if not isinstance(template, list):
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)
# 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
def new_from_template(self, template, key, **kwargs):
# manage singleton
if template.metadata.is_unique:
@@ -828,18 +830,23 @@ class Sheerka(Concept):
concept = self.builtin_cache[key]() if key in self.builtin_cache else Concept()
concept.update_from(template)
# update the properties
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:
setattr(concept.metadata, k, v)
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):
@@ -880,7 +887,7 @@ class Sheerka(Concept):
return self.value(body_to_use)
def values(self, objs):
def get_values(self, objs):
if not (isinstance(objs, list) or
self.isinstance(objs, BuiltinConcepts.LIST) or
self.isinstance(objs, BuiltinConcepts.ENUMERATION)):
+13 -4
View File
@@ -26,6 +26,9 @@ class SheerkaTransformType(Enum):
Node = 5
Exception = 6
def __repr__(self):
return self.__class__.__name__ + "." + self.name
class SheerkaTransform:
@@ -117,18 +120,24 @@ class SheerkaTransform:
# transform metadata
for prop in PROPERTIES_TO_SERIALIZE:
value = self.to_dict(getattr(obj.metadata, prop))
value = getattr(obj.metadata, prop)
ref_value = getattr(ref.metadata, prop)
if value != ref_value:
to_dict[prop] = value
to_dict["meta." + prop] = self.to_dict(value)
# transform value
for metadata, value in obj.values.items():
ref_value = ref.values[metadata] if metadata in ref.values else None
if value != ref_value:
to_dict[metadata.value] = self.to_dict(value)
# transform properties
for prop in obj.props:
value = self.to_dict(obj.props[prop].value)
value = obj.props[prop].value
if prop not in ref.props or value != ref.props[prop].value:
if "props" not in to_dict:
to_dict["props"] = []
to_dict["props"].append((prop, value))
to_dict["props"].append((prop, self.to_dict(value)))
return to_dict
+2 -3
View File
@@ -73,16 +73,15 @@ class AddConceptEvaluator(OneReturnValueEvaluator):
# add props order by appearance when possible
for token in def_concept_node.name.tokens:
if token.value in props_found:
concept.set_prop(token.value, None)
concept.def_prop(token.value, None)
# add the remaining properties
for p in props_found:
if p not in concept.props:
concept.set_prop(p, None)
concept.def_prop(p, None)
# finish initialisation
concept.init_key(def_concept_node.name.tokens)
#concept.add_codes(def_concept_node.get_asts())
if not isinstance(def_concept_node.definition, NotInitializedNode) and \
sheerka.is_success(def_concept_node.definition):
concept.bnf = def_concept_node.definition.value.value
+1 -1
View File
@@ -47,7 +47,7 @@ class ConceptEvaluator(OneReturnValueEvaluator):
evaluated,
parents=[return_value])
if not self.return_body or ConceptParts.BODY not in evaluated.cached_asts:
if not self.return_body or ConceptParts.BODY not in evaluated.compiled:
return sheerka.ret(self.name, True, evaluated, parents=[return_value])
else:
return sheerka.ret(self.name, True, evaluated.body, parents=[return_value])
+6 -6
View File
@@ -864,17 +864,17 @@ class ConceptLexerParser(BaseParser):
Adds a new entry,
makes a list if the property already exists
"""
if prop_name not in _concept.cached_asts or _concept.cached_asts[prop_name] is None:
if prop_name not in _concept.compiled or _concept.compiled[prop_name] is None:
# new entry
_concept.cached_asts[prop_name] = value
_concept.compiled[prop_name] = value
else:
# make a list if there was a value
previous_value = _concept.cached_asts[prop_name]
previous_value = _concept.compiled[prop_name]
if isinstance(previous_value, list):
previous_value.append(value)
else:
new_value = [previous_value, value]
_concept.cached_asts[prop_name] = new_value
_concept.compiled[prop_name] = new_value
def _look_for_concept_match(_underlying):
if isinstance(_underlying.parsing_expression, ConceptExpression):
@@ -913,9 +913,9 @@ class ConceptLexerParser(BaseParser):
key = (template.key, template.id) if template.id else template.key
concept = sheerka.new(key)
if init_empty_body and concept.body is None:
if init_empty_body and concept.metadata.body is None:
value = _get_underlying_value(underlying)
concept.cached_asts[ConceptParts.BODY] = value
concept.compiled[ConceptParts.BODY] = value
if underlying.parsing_expression.rule_name:
_add_prop(concept, underlying.parsing_expression.rule_name, value)
+2 -2
View File
@@ -54,7 +54,7 @@ class ConceptsWithConceptsParser(BaseParser):
if isinstance(node, ConceptNode):
prop_name = list(concept.props.keys())[index]
concept.cached_asts[prop_name] = node.concept
concept.compiled[prop_name] = node.concept
context.log(
self.verbose_log,
f"Setting property '{prop_name}='{node.concept}'.",
@@ -64,7 +64,7 @@ class ConceptsWithConceptsParser(BaseParser):
prop_name = list(concept.props.keys())[index]
sheerka = context.sheerka
value = sheerka.new(BuiltinConcepts.PARSER_RESULT, parser=self, source=node.source, body=node.node)
concept.cached_asts[prop_name] = [context.sheerka.ret(self.name, True, value)]
concept.compiled[prop_name] = [context.sheerka.ret(self.name, True, value)]
context.log(
self.verbose_log,
f"Setting property '{prop_name}'='Python({node.source})'.",
+1 -1
View File
@@ -55,7 +55,7 @@ class ExactConceptParser(BaseParser):
for i, token in enumerate(combination):
if token.startswith(VARIABLE_PREFIX):
index = int(token[len(VARIABLE_PREFIX):])
concept.set_prop_by_index(index, words[i])
concept.def_prop_by_index(index, words[i])
if self.verbose_log.isEnabledFor(logging.DEBUG):
prop_name = list(concept.props.keys())[index]
context.log(
+13 -11
View File
@@ -21,7 +21,7 @@ def get_context():
return ExecutionContext("test", Event(), sheerka)
def get_concept(name, where=None, pre=None, post=None, body=None, definition=None):
def get_def_concept(name, where=None, pre=None, post=None, body=None, definition=None):
concept = DefConceptNode([], name=NameNode(list(Tokenizer(name))))
if body:
@@ -87,7 +87,7 @@ def test_i_can_match(ret_val, expected):
def test_that_the_source_is_correctly_set():
context = get_context()
def_concept_return_value = get_concept(
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 )",
@@ -132,21 +132,22 @@ def test_that_the_source_is_correctly_set():
#
# created_concept = evaluated.body.body
#
# assert ConceptParts.WHERE in created_concept.cached_asts
# assert ConceptParts.PRE in created_concept.cached_asts
# assert ConceptParts.BODY in created_concept.cached_asts
# assert ConceptParts.POST not in created_concept.cached_asts
# 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():
def test_that_the_new_concept_is_correctly_saved_in_db():
context = get_context()
def_concept_return_value = get_concept(
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)
@@ -161,10 +162,11 @@ def test_that_the_new_concept_is_correctly_saved():
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.props) == 1
assert len(from_db.metadata.props) == 1
assert from_db.metadata.props[0] == ("a", None)
assert "a" in from_db.props
assert from_db.cached_asts == {} # ast is not saved in db
assert from_db.compiled == {} # ast is not saved in db
def test_i_can_get_props_from_python_node():
@@ -175,7 +177,7 @@ def test_i_can_get_props_from_python_node():
def test_i_can_get_props_from_another_concept():
concept = Concept("hello").set_prop("a").set_prop("b")
concept = Concept("hello").def_prop("a").def_prop("b")
ret_val = ReturnValueConcept(who="some_parser",
status=True,
value=ParserResultConcept(value=concept))
+11 -9
View File
@@ -1,10 +1,9 @@
import pytest
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
from core.concept import Concept
from core.concept import Concept, ConceptParts
from core.sheerka import Sheerka, ExecutionContext
from evaluators.ConceptEvaluator import ConceptEvaluator
from parsers.BaseParser import BaseParser
from parsers.ExactConceptParser import ExactConceptParser
from sdp.sheerkaDataProvider import Event
@@ -41,7 +40,7 @@ def test_i_can_evaluate_concept():
concept = Concept(name="foo",
where="1",
pre="2",
post="3").set_prop("a", "4").set_prop("b", "5")
post="3").def_prop("a", "4").def_prop("b", "5")
evaluator = ConceptEvaluator()
item = get_return_value(concept)
@@ -49,10 +48,13 @@ def test_i_can_evaluate_concept():
assert result.who == evaluator.name
assert result.status
assert result.value == Concept(name="foo",
where=1,
pre=2,
post=3).set_prop("a", 4).set_prop("b", 5).init_key()
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]
@@ -111,8 +113,8 @@ 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")
.set_prop("a", "one")
.set_prop("b", "two").init_key())
.def_prop("a", "one")
.def_prop("b", "two").init_key())
evaluator = ConceptEvaluator()
item = get_return_value(concept_plus)
+10 -7
View File
@@ -59,19 +59,21 @@ def get_context():
def get_expected(concept, text=None):
c = Concept(name=concept.name)
c.cached_asts[ConceptParts.BODY] = DoNotResolve(text or concept.name)
c.compiled[ConceptParts.BODY] = DoNotResolve(text or concept.name)
c.init_key()
return c
def cbody(concept):
"""cbody stands for compiled body"""
return concept.cached_asts[ConceptParts.BODY]
if not ConceptParts.BODY in concept.compiled:
return None
return concept.compiled[ConceptParts.BODY]
def cprop(concept, prop_name):
"""cbody stands for compiled property"""
return concept.cached_asts[prop_name]
return concept.compiled[prop_name]
def init(concepts, grammar):
@@ -402,14 +404,15 @@ def test_i_can_use_a_reference_with_a_body():
assert context.sheerka.isinstance(res[0].value, BuiltinConcepts.PARSER_RESULT)
assert res[0].value.body == [cnode("foo", 0, 2, "one two")]
concept_found_1 = res[0].value.body[0].concept
assert concept_found_1.body == "'foo'"
assert concept_found_1.metadata.body == "'foo'"
assert cbody(concept_found_1) is None
assert res[1].status
assert context.sheerka.isinstance(res[1].value, BuiltinConcepts.PARSER_RESULT)
assert res[1].value.body == [cnode("bar", 0, 2, "one two")]
concept_found_2 = res[1].value.body[0].concept
# the body and the prop['foo'] are the same concept 'foo'
assert cbody(concept_found_2) == foo
# the body and the prop['foo'] are the same concept 'foo'
assert id(cprop(concept_found_2, "foo")) == id(cbody(concept_found_2))
@@ -513,7 +516,7 @@ def test_i_can_parse_when_reference_has_a_body():
assert res.status
assert res.value.body == [cnode("foo", 0, 0, "twenty")]
concept_found = res.value.body[0].concept
assert concept_found.body == "'one'"
assert concept_found.metadata.body == "'one'"
def test_i_can_parse_multiple_results():
@@ -1089,7 +1092,7 @@ def test_i_get_multiple_props_when_zero_or_more():
assert return_value == [cnode("foo", 0, 4, "one one one")]
concept_found = return_value[0].concept
assert cbody(concept_found) == DoNotResolve("one one one")
assert len(concept_found.cached_asts["one"]) == 3
assert len(concept_found.compiled["one"]) == 3
assert cprop(concept_found, "one")[0] == get_expected(one)
assert cprop(concept_found, "one")[1] == get_expected(one)
assert cprop(concept_found, "one")[2] == get_expected(one)
+15 -15
View File
@@ -86,7 +86,7 @@ def test_not_interested(text, interested):
def test_i_can_parse_composition_of_concepts():
foo = Concept("foo")
bar = Concept("bar")
plus = Concept("a plus b").set_prop("a").set_prop("b")
plus = Concept("a plus b").def_prop("a").def_prop("b")
context, parser, result, wrapper, return_value = execute([foo, bar, plus], [foo, " plus ", bar])
@@ -96,8 +96,8 @@ def test_i_can_parse_composition_of_concepts():
assert wrapper.source == "foo plus bar"
assert context.sheerka.isinstance(return_value, plus)
assert return_value.cached_asts["a"] == foo
assert return_value.cached_asts["b"] == bar
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)
@@ -107,7 +107,7 @@ def test_i_can_parse_composition_of_concepts():
def test_i_can_parse_when_composition_of_source_code():
plus = Concept("a plus b", body="a + b").set_prop("a").set_prop("b")
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 = execute([plus], [left, " plus ", right])
@@ -120,8 +120,8 @@ def test_i_can_parse_when_composition_of_source_code():
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.cached_asts["a"] == [ReturnValueConcept(parser.name, True, left_parser_result)]
assert return_value.cached_asts["b"] == [ReturnValueConcept(parser.name, True, right_parser_result)]
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)
@@ -132,7 +132,7 @@ def test_i_can_parse_when_composition_of_source_code():
def test_i_can_parse_when_mix_of_concept_and_code():
plus = Concept("a plus b").set_prop("a").set_prop("b")
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 = execute([plus, foo], [foo, " plus ", code])
@@ -144,8 +144,8 @@ def test_i_can_parse_when_mix_of_concept_and_code():
assert context.sheerka.isinstance(return_value, plus)
code_parser_result = ParserResultConcept(parser=parser, source="1+1", value=code)
assert return_value.cached_asts["a"] == foo
assert return_value.cached_asts["b"] == [ReturnValueConcept(parser.name, True, code_parser_result)]
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)
@@ -157,8 +157,8 @@ def test_i_can_parse_when_mix_of_concept_and_code():
def test_i_can_parse_when_multiple_concepts_are_recognized():
foo = Concept("foo")
bar = Concept("bar")
plus_1 = Concept("a plus b", body="body1").set_prop("a").set_prop("b")
plus_2 = Concept("a plus b", body="body2").set_prop("a").set_prop("b")
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 = init([foo, bar, plus_1, plus_2], [foo, " plus ", bar])
parser = ConceptsWithConceptsParser()
@@ -174,8 +174,8 @@ def test_i_can_parse_when_multiple_concepts_are_recognized():
assert res.who == wrapper.parser.name
assert wrapper.source == "foo plus bar"
assert context.sheerka.isinstance(return_value, plus_1)
assert return_value.cached_asts["a"] == foo
assert return_value.cached_asts["b"] == bar
assert return_value.compiled["a"] == foo
assert return_value.compiled["b"] == bar
res = result[1]
wrapper = res.value
@@ -184,8 +184,8 @@ def test_i_can_parse_when_multiple_concepts_are_recognized():
assert res.who == wrapper.parser.name
assert wrapper.source == "foo plus bar"
assert context.sheerka.isinstance(return_value, plus_2)
assert return_value.cached_asts["a"] == foo
assert return_value.cached_asts["b"] == bar
assert return_value.compiled["a"] == foo
assert return_value.compiled["b"] == bar
def test_i_cannot_parse_when_unknown_concept():
+3 -3
View File
@@ -23,13 +23,13 @@ eval_requested = ReturnValueConcept("some_name", True, Concept(key=BuiltinConcep
def test_i_can_match_and_eval():
context = get_context()
to_eval1 = ReturnValueConcept("some_name", True, Concept(name="2", body="to eval"))
to_eval2 = ReturnValueConcept("some_name", True, Concept(name="3", body="also to eval"))
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")),
ReturnValueConcept("some_name", False, Concept(name="1", body="'not to eval'")),
to_eval1,
to_eval2,
eval_requested
+14 -6
View File
@@ -6,6 +6,14 @@ from parsers.ExactConceptParser import ExactConceptParser
from sdp.sheerkaDataProvider import Event
def metadata_prop(concept, prop_name):
for name, value in concept.metadata.props:
if name == prop_name:
return value
return None
def test_i_can_compute_combinations():
parser = ExactConceptParser()
res = parser.combinations(["foo", "bar", "baz"])
@@ -58,7 +66,7 @@ def test_i_can_recognize_concepts_defined_several_times():
assert results[0].status
assert results[0].value.value.name == "hello a"
assert results[0].value.value.props["a"].value == "world"
assert metadata_prop(results[0].value.value, "a") == "world"
assert results[1].status
assert results[1].value.value.name == "hello world"
@@ -75,8 +83,8 @@ def test_i_can_recognize_a_concept_with_variables():
assert results[0].status
concept_found = results[0].value.value
assert concept_found.key == concept.key
assert concept_found.props["a"].value == "10"
assert concept_found.props["b"].value == "5"
assert metadata_prop(concept_found, "a") == "10"
assert metadata_prop(concept_found, "b") == "5"
def test_i_can_recognize_a_concept_with_duplicate_variables():
@@ -90,8 +98,8 @@ def test_i_can_recognize_a_concept_with_duplicate_variables():
assert results[0].status
concept_found = results[0].value.value
assert concept_found.key == concept.key
assert concept_found.props["a"].value == "10"
assert concept_found.props["b"].value == "5"
assert metadata_prop(concept_found, "a") == "10"
assert metadata_prop(concept_found, "b") == "5"
def test_i_can_manage_unknown_concept():
@@ -138,6 +146,6 @@ def get_concept(name, variables):
c = Concept(name=name)
if variables:
for v in variables:
c.props[v] = Property(v, None)
c.def_prop(v)
c.init_key()
return c
+1 -1
View File
@@ -90,7 +90,7 @@ def test_concept_is_returned_when_only_one_in_the_list():
assert wrapper.parser == evaluator
assert wrapper.source == "foo"
assert return_value == Concept("foo").init_key()
assert return_value.cached_asts[ConceptParts.BODY] == DoNotResolve("foo")
assert return_value.compiled[ConceptParts.BODY] == DoNotResolve("foo")
assert result.parents == [ret_val]
+2 -2
View File
@@ -136,12 +136,12 @@ def test_i_can_parse_when_multiple_concepts_are_matching():
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.body == "bar1"
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.body == "bar2"
assert ret_val[1].value.value[1].concept.metadata.body == "bar2"
def test_i_can_parse_when_source_code():
+25 -25
View File
@@ -20,8 +20,8 @@ def test_i_can_match_and_eval():
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")),
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))
]
@@ -30,7 +30,7 @@ def test_i_can_match_and_eval():
evaluated = evaluator.eval(context, return_values)
assert evaluated.status
assert evaluated.value == Concept(name="1", body="value") # the first concept is returned
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():
@@ -40,8 +40,8 @@ def test_i_can_match_and_eval_when_no_body():
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")),
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="1")),
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))
]
@@ -50,7 +50,7 @@ def test_i_can_match_and_eval_when_no_body():
evaluated = evaluator.eval(context, return_values)
assert evaluated.status
assert evaluated.value == Concept(name="1")
assert evaluated.value == Concept(name="1").auto_init()
def test_i_can_match_and_eval_when_value_is_not_a_concept():
@@ -82,9 +82,9 @@ def test_i_can_match_and_eval_when_at_least_one_value_is_a_concept_concept_evalu
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")),
ReturnValueConcept(BaseEvaluator.PREFIX + "Concept", True, Concept(name="1", body="value")),
ReturnValueConcept(BaseEvaluator.PREFIX + "Python", True, Concept(name="3", body="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))
]
@@ -94,7 +94,7 @@ def test_i_can_match_and_eval_when_at_least_one_value_is_a_concept_concept_evalu
evaluated = evaluator.eval(context, return_values)
assert evaluated.status
assert evaluated.value == Concept(name="1", body="value") # the concept is returned, not the value
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():
@@ -106,9 +106,9 @@ def test_i_can_match_and_eval_when_at_least_one_value_is_a_concept_python_evalua
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")),
ReturnValueConcept(BaseEvaluator.PREFIX + "Python", True, Concept(name="1", body="value")),
ReturnValueConcept(BaseEvaluator.PREFIX + "other", True, Concept(name="3", body="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))
]
@@ -118,7 +118,7 @@ def test_i_can_match_and_eval_when_at_least_one_value_is_a_concept_python_evalua
evaluated = evaluator.eval(context, return_values)
assert evaluated.status
assert evaluated.value == Concept(name="1", body="value") # the concept is returned, not the value
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():
@@ -128,8 +128,8 @@ def test_i_can_match_even_if_the_value_are_not_the_same_but_eval_will_fail():
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="value2")),
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))
]
@@ -145,8 +145,8 @@ def test_i_can_match_even_if_the_value_are_not_the_same_but_eval_will_fail_when_
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")),
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="2")),
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))
]
@@ -160,8 +160,8 @@ def test_i_can_match_if_no_parser():
sheerka = context.sheerka
return_values = [
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="1", body="value")),
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="2", body="value")),
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))
]
@@ -175,8 +175,8 @@ def test_i_cannot_match_if_not_reduced_requested():
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")),
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))
]
@@ -192,7 +192,7 @@ def test_i_cannot_match_if_only_one_successful_evaluator():
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")),
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))
]
@@ -207,8 +207,8 @@ def test_i_cannot_match_if_at_least_one_parser_is_successful():
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")),
ReturnValueConcept(BaseEvaluator.PREFIX + "some_name", True, Concept(name="2", body="value")),
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))
]
+17 -14
View File
@@ -1,6 +1,6 @@
import pytest
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept, UserInputConcept
from core.concept import Concept
from core.sheerka import Sheerka, ExecutionContext
from evaluators.PrepareEvalEvaluator import PrepareEvalEvaluator
@@ -13,18 +13,21 @@ def get_context():
return ExecutionContext("test", Event(), sheerka)
r = ReturnValueConcept
@pytest.mark.parametrize("ret_val, expected", [
(ReturnValueConcept("some_name", True, Concept(key=BuiltinConcepts.USER_INPUT, body="eval 1 + 1")), True),
(ReturnValueConcept("some_name", True, Concept(key=BuiltinConcepts.USER_INPUT, body=" eval 1 + 1")), True),
(ReturnValueConcept("some_name", True, Concept(key=BuiltinConcepts.USER_INPUT, body="eval")), False),
(ReturnValueConcept("some_name", True, Concept(key=BuiltinConcepts.USER_INPUT, body="1+1")), False),
(ReturnValueConcept("some_name", True, Concept(key=BuiltinConcepts.USER_INPUT, body="")), False),
(ReturnValueConcept("some_name", True, Concept(key=BuiltinConcepts.USER_INPUT, body="eval ")), False),
(ReturnValueConcept("some_name", True, Concept(key=BuiltinConcepts.USER_INPUT, body=[])), False),
(ReturnValueConcept("some_name", True, Concept("foo")), False),
(ReturnValueConcept("some_name", True, "not a concept"), False),
(ReturnValueConcept("some_name", False, Concept(key=BuiltinConcepts.USER_INPUT, body="eval 1 + 1")), False),
(ReturnValueConcept("some_name", False, Concept(key=BuiltinConcepts.USER_INPUT, body=" eval 1 + 1")), False),
(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(ret_val, expected):
context = get_context()
@@ -32,8 +35,8 @@ def test_i_can_match(ret_val, expected):
@pytest.mark.parametrize("ret_val, expected", [
(ReturnValueConcept("some_name", True, Concept(key=BuiltinConcepts.USER_INPUT, body="eval 1 + 1")), "1 + 1"),
(ReturnValueConcept("some_name", True, Concept(key=BuiltinConcepts.USER_INPUT, body=" eval 1 + 1")), "1 + 1"),
(r("name", True, UserInputConcept("eval 1 + 1")), "1 + 1"),
(r("name", True, UserInputConcept(" eval 1 + 1")), "1 + 1"),
])
def test_i_can_eval(ret_val, expected):
context = get_context()
+4 -4
View File
@@ -73,8 +73,8 @@ def test_i_can_eval_when_same_success():
context = get_context()
return_values = [
r("evaluators.a", value=Concept("c1", body="1")),
r("evaluators.a", value=Concept("c2", body="1")),
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
]
@@ -90,8 +90,8 @@ def test_i_can_eval_when_same_success():
def test_other_success_are_not_reduced():
context = get_context()
value1 = r("evaluators.a", value=Concept("c1", body="1"))
value2 = r("evaluators.a", value=Concept("c2", body="2"))
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,
+92 -12
View File
@@ -1,9 +1,18 @@
import pytest
from core.concept import Concept
from core.concept import Concept, ConceptParts
@pytest.mark.parametrize("name, variables, expected", [
@pytest.mark.parametrize("name, properties, expected", [
("foo", [], "foo"),
("foo(bar)", [], "foo ( bar )"),
("foo a", ["a"], "foo __var__0"),
("a foo b", ["a", "b"], "__var__0 foo __var__1"),
("a foo b", ["b", "a"], "__var__1 foo __var__0"),
("foo", ["foo"], "foo"),
("foo a", ["foo"], "__var__0 a"),
("foo a b", ["a"], "foo __var__0 b"),
("'foo'", [], "foo"),
("my name is a", ["a"], "my name is __var__0"),
("a b c d", ["b", "c"], "a __var__0 __var__1 d"),
("a 'b c' d", ["b", "c"], "a b c d"),
@@ -11,17 +20,17 @@ from core.concept import Concept
("a b a c", ["a", "b"], "__var__0 __var__1 __var__0 c"),
("a b a c", ["b", "a"], "__var__1 __var__0 __var__1 c"),
])
def test_i_can_get_concept_key(name, variables, expected):
def test_i_can_compute_the_key(name, properties, expected):
concept = Concept(name)
for v in variables:
concept.set_prop(v, None)
for prop in properties:
concept.metadata.props.append((prop, None))
concept.init_key()
assert concept.metadata.key == expected
assert concept.key == expected
def test_key_does_not_use_variable_when_definition_is_set():
concept = Concept("plus").set_prop('plus')
concept = Concept("plus").def_prop('plus')
concept.init_key()
assert concept.metadata.key == "plus"
@@ -45,7 +54,7 @@ def test_i_can_serialize():
definition_type="def type",
desc="this this the desc",
id="123456"
).set_prop("a", 10).set_prop("b", None)
).def_prop("a", "10").def_prop("b", None)
to_dict = concept.to_dict()
assert to_dict == {
@@ -60,7 +69,7 @@ def test_i_can_serialize():
'name': 'concept_name',
'post': 'definition of the post',
'pre': 'definition of the pre',
'props': [('a', 10), ('b', None)],
'props': [('a', "10"), ('b', None)],
'where': 'definition of the where'
}
@@ -83,7 +92,7 @@ def test_i_can_deserialize():
'name': 'concept_name',
'post': 'definition of the post',
'pre': 'definition of the pre',
'props': [('a', 10), ('b', None)],
'props': [('a', "10"), ('b', None)],
'where': 'definition of the where'
}
@@ -102,7 +111,50 @@ def test_i_can_deserialize():
definition_type="def type",
desc="this this the desc",
id="123456"
).set_prop("a", 10).set_prop("b", None)
).def_prop("a", "10").def_prop("b", None)
def test_i_can_deserialize_props_coming_from_sdp():
from_dict = {
'props': [['a', "10"], ['b', None]], # JSON transform set into list
}
concept = Concept().from_dict(from_dict)
assert concept == Concept().def_prop("a", "10").def_prop("b", None)
def test_i_can_compare_concepts():
concept_a = Concept(
name="concept_name",
is_builtin=True,
is_unique=True,
key="concept_key",
body="definition of the body",
where="definition of the where",
pre="definition of the pre",
post="definition of the post",
definition="bnf definition",
definition_type="def type",
desc="this this the desc",
id="123456"
).def_prop("a", "10").def_prop("b", None)
concept_b = Concept(
name="concept_name",
is_builtin=True,
is_unique=True,
key="concept_key",
body="definition of the body",
where="definition of the where",
pre="definition of the pre",
post="definition of the post",
definition="bnf definition",
definition_type="def type",
desc="this this the desc",
id="123456"
).def_prop("a", "10").def_prop("b", None)
assert concept_a == concept_b
def test_i_can_compare_concept_with_circular_reference():
@@ -128,3 +180,31 @@ def test_i_can_compare_concept_with_sophisticated_circular_reference_in_other_me
foo.metadata.pre = baz
assert foo != bar
def test_i_can_update_from():
template = Concept(
name="concept_name",
is_builtin=True,
is_unique=True,
key="concept_key",
body="definition of the body",
where="definition of the where",
pre="definition of the pre",
post="definition of the post",
definition="bnf definition",
definition_type="def type",
desc="this this the desc",
id="123456"
).def_prop("a", "10").def_prop("b", None)
template.values[ConceptParts.BODY] = "value in body"
template.values[ConceptParts.WHERE] = "value in where"
template.values[ConceptParts.PRE] = "value in pre"
template.values[ConceptParts.POST] = "value in post"
template.set_prop("a", 10)
template.set_prop("b", 20)
concept = Concept().update_from(template)
assert concept == template
+63 -62
View File
@@ -5,7 +5,7 @@ import shutil
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept, UserInputConcept, ConceptAlreadyInSet, \
ParserResultConcept
from core.concept import Concept, PROPERTIES_TO_SERIALIZE, Property, ConceptParts, DoNotResolve
from core.concept import Concept, PROPERTIES_TO_SERIALIZE, Property, ConceptParts, DoNotResolve, simplec
from core.sheerka import Sheerka, ExecutionContext
from parsers.PythonParser import PythonNode
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event
@@ -49,8 +49,8 @@ def get_default_concept():
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")
concept.def_prop("a", "value1")
concept.def_prop("b", "value2")
return concept
@@ -238,7 +238,7 @@ def test_i_can_get_the_correct_concept_using_the_id_when_same_key_when_no_cache(
result = sheerka.get(concept1.key, res2.body.body.id)
assert result.name == "a + b"
assert result.body == "a+b"
assert result.metadata.body == "a+b"
def test_i_can_get_the_correct_concept_using_the_id__when_same_key_when_cache():
@@ -254,7 +254,7 @@ def test_i_can_get_the_correct_concept_using_the_id__when_same_key_when_cache():
result = sheerka.get(concept1.key, res2.body.body.id)
assert result.name == "a + b"
assert result.body == "a+b"
assert result.metadata.body == "a+b"
def test_i_cannot_get_the_correct_concept_id_the_id_is_wrong():
@@ -283,11 +283,12 @@ def test_i_cannot_get_when_key_is_none():
def test_unknown_concept_is_return_when_the_concept_is_not_found():
sheerka = get_sheerka()
loaded = sheerka.get("fake_key")
loaded = sheerka.get("concept_that_does_not_exist")
assert loaded is not None
assert sheerka.isinstance(loaded, BuiltinConcepts.UNKNOWN_CONCEPT)
assert loaded.body == "fake_key"
assert loaded.body == "concept_that_does_not_exist"
assert loaded.metadata.is_evaluated
def test_i_can_instantiate_a_builtin_concept_when_it_has_its_own_class():
@@ -335,10 +336,10 @@ def test_i_can_instantiate_with_the_name_and_the_id():
assert len(concepts) == 2
foo1 = sheerka.new(("foo", "1001"))
assert foo1.body == "foo1"
assert foo1.metadata.body == "foo1"
foo2 = sheerka.new(("foo", "1002"))
assert foo2.body == "foo2"
assert foo2.metadata.body == "foo2"
def test_instances_are_different_when_asking_for_new():
@@ -403,7 +404,7 @@ def test_concept_id_is_irrelevant_when_only_one_concept():
new = sheerka.new(("foo", "invalid_id"))
assert sheerka.isinstance(new, "foo")
assert new.body == "foo1"
assert new.metadata.body == "foo1"
def test_i_cannot_instantiate_when_properties_are_not_recognized():
@@ -439,6 +440,12 @@ def test_i_cannot_instantiate_when_properties_are_not_recognized():
def test_i_can_get_value(concept, reduce_simple_list, expected):
sheerka = 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
@@ -462,16 +469,18 @@ def test_list_of_concept_is_sorted_by_id():
def test_i_can_evaluate_a_concept_with_simple_body(body, expected):
sheerka = get_sheerka()
concept = Concept("foo", body=body).init_key()
concept = Concept("foo", body=body)
evaluated = sheerka.evaluate_concept(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", [
@@ -493,20 +502,22 @@ def test_i_can_evaluate_the_other_metadata(expr, expected):
sheerka = get_sheerka()
concept = Concept("foo", where=expr).init_key()
concept = Concept("foo", where=expr)
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.key == concept.key
assert evaluated.body is None
assert evaluated.metadata.body is None
assert evaluated.metadata.pre is None
assert evaluated.metadata.post is None
assert evaluated.metadata.where == expected
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),
(None, None),
("", ""),
("1", 1),
("1+1", 2),
@@ -518,11 +529,11 @@ def test_i_can_evaluate_the_other_metadata(expr, expected):
def test_i_can_evaluate_a_concept_with_prop(expr, expected):
sheerka = get_sheerka()
concept = Concept("foo").set_prop("a", expr).init_key()
concept = Concept("foo").def_prop("a", expr)
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.pre is None
assert evaluated.metadata.post is None
assert evaluated.metadata.where is None
@@ -533,7 +544,7 @@ def test_i_can_evaluate_a_concept_with_prop(expr, expected):
def test_i_can_evaluate_metadata_using_do_not_resolve():
sheerka = get_sheerka()
concept = Concept("foo")
concept.cached_asts[ConceptParts.BODY] = DoNotResolve("do not resolve")
concept.compiled[ConceptParts.BODY] = DoNotResolve("do not resolve")
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
@@ -543,8 +554,8 @@ def test_i_can_evaluate_metadata_using_do_not_resolve():
def test_i_can_evaluate_property_using_do_not_resolve():
sheerka = get_sheerka()
concept = Concept("foo").set_prop("a")
concept.cached_asts["a"] = DoNotResolve("do not resolve")
concept = Concept("foo").def_prop("a")
concept.compiled["a"] = DoNotResolve("do not resolve")
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
@@ -555,8 +566,8 @@ def test_i_can_evaluate_property_using_do_not_resolve():
def test_original_value_is_overridden_when_using_do_no_resolve():
sheerka = get_sheerka()
concept = Concept("foo", body="original value").set_prop("a", "original value")
concept.cached_asts["a"] = DoNotResolve("do not resolve")
concept.cached_asts[ConceptParts.BODY] = DoNotResolve("do not resolve")
concept.compiled["a"] = DoNotResolve("do not resolve")
concept.compiled[ConceptParts.BODY] = DoNotResolve("do not resolve")
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
@@ -568,7 +579,7 @@ def test_original_value_is_overridden_when_using_do_no_resolve():
def test_props_are_evaluated_before_body():
sheerka = get_sheerka()
concept = Concept("foo", body="a+1").set_prop("a", "10").init_key()
concept = Concept("foo", body="a+1").def_prop("a", "10").init_key()
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.key == concept.key
@@ -583,8 +594,7 @@ def test_i_can_evaluate_when_another_concept_is_referenced():
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 evaluated == simplec("foo", simplec("a", None))
assert id(evaluated.body) != id(concept_a)
assert evaluated.metadata.is_evaluated
assert evaluated.body.metadata.is_evaluated
@@ -595,11 +605,11 @@ def test_i_can_evaluate_when_the_referenced_concept_has_a_body():
concept_a = Concept("a", body="1")
sheerka.add_in_cache(concept_a)
concept = Concept("foo", body="a").init_key()
concept = Concept("foo", body="a")
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.key == concept.key
assert evaluated.body == Concept("a", body=1).init_key()
assert evaluated.body == simplec("a", 1)
assert not concept_a.metadata.is_evaluated
assert evaluated.metadata.is_evaluated
@@ -614,13 +624,8 @@ def test_i_can_evaluate_concept_of_concept_when_the_leaf_has_a_body():
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept_d)
assert evaluated.key == concept_d.key
assert evaluated.body == Concept(
name="c",
body=Concept(
name="b",
body=Concept(
name="a",
body="a").init_key()).init_key()).init_key()
expected = simplec("c", simplec("b", simplec("a", "a")))
assert evaluated.body == expected
assert sheerka.value(evaluated) == 'a'
assert evaluated.metadata.is_evaluated
@@ -635,12 +640,8 @@ def test_i_can_evaluate_concept_of_concept_does_not_have_a_body():
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept_d)
assert evaluated.key == concept_d.key
assert evaluated.body == Concept(
name="c",
body=Concept(
name="b",
body=Concept(
name="a").init_key()).init_key()).init_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
@@ -649,7 +650,7 @@ 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()
concept = Concept("foo", body="a").def_prop("a", "a").init_key()
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
# first prop a is evaluated to concept_a
@@ -667,7 +668,7 @@ def test_i_can_evaluate_concept_when_properties_reference_others_concepts_2():
sheerka = get_sheerka()
concept_a = sheerka.add_in_cache(Concept(name="a"))
concept = Concept("foo", body="concept_a").set_prop("concept_a", "a")
concept = Concept("foo", body="concept_a").def_prop("concept_a", "a")
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.key == concept.key
@@ -679,7 +680,7 @@ def test_i_can_evaluate_concept_when_properties_reference_others_concepts_with_b
sheerka.add_in_cache(Concept(name="a", body="1"))
sheerka.add_in_cache(Concept(name="b", body="2"))
concept = Concept("foo", body="propA + propB").set_prop("propA", "a").set_prop("propB", "b")
concept = Concept("foo", body="propA + propB").def_prop("propA", "a").def_prop("propB", "b")
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.key == concept.key
@@ -690,24 +691,25 @@ def test_i_can_evaluate_concept_when_properties_is_a_concept():
sheerka = get_sheerka()
concept_a = sheerka.add_in_cache(Concept(name="a", body="'a'").init_key())
concept = Concept("foo").set_prop("a", concept_a)
concept = Concept("foo").def_prop("a")
concept.compiled["a"] = concept_a
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.key == concept.key
assert evaluated.get_prop("a") == Concept(name="a", body="a").init_key()
assert evaluated.get_prop("a") == simplec("a", "a")
def test_i_can_evaluate_when_property_asts_is_a_list():
sheerka = get_sheerka()
foo = Concept("foo", body="1")
concept = Concept("to_eval").set_prop("prop")
concept.cached_asts["prop"] = [foo, DoNotResolve("1")]
concept = Concept("to_eval").def_prop("prop")
concept.compiled["prop"] = [foo, DoNotResolve("1")]
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
props = evaluated.get_prop("prop")
assert len(props) == 2
assert props[0] == Concept("foo", body=1).init_key()
assert props[0] == simplec("foo", 1)
assert props[1] == "1"
@@ -717,14 +719,14 @@ def test_i_can_evaluate_when_compiled_is_set_up_with_return_value():
python_node = PythonNode("1 +1 ")
parser_result = ParserResultConcept(parser="who", value=python_node)
concept = Concept("to_eval").set_prop("prop")
concept.cached_asts["prop"] = [ReturnValueConcept("who", True, parser_result)]
concept = Concept("to_eval").def_prop("prop")
concept.compiled["prop"] = [ReturnValueConcept("who", True, parser_result)]
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.get_prop("prop") == 2
# also works when only one return value
concept = Concept("to_eval").set_prop("prop")
concept.cached_asts["prop"] = ReturnValueConcept("who", True, parser_result)
concept = Concept("to_eval").def_prop("prop")
concept.compiled["prop"] = ReturnValueConcept("who", True, parser_result)
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.get_prop("prop") == 2
@@ -747,19 +749,19 @@ def test_properties_values_takes_precedence_over_the_outside_world():
concept = Concept("foo", body="a")
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.key == concept.key
assert evaluated.body == Concept(name="a", body="concept_a").init_key() # this test was already done
assert evaluated.body == simplec("a", "concept_a") # this test was already done
# so check this one.
concept = Concept("foo", body="a").set_prop("a", "'property_a'")
concept = Concept("foo", body="a").def_prop("a", "'property_a'")
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")
concept = Concept("foo", body="a").def_prop("a", "b")
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.key == concept.key
assert evaluated.body == Concept(name="b", body="concept_b").init_key()
assert evaluated.body == simplec(name="b", body="concept_b")
def test_properties_values_takes_precedence():
@@ -767,7 +769,7 @@ def test_properties_values_takes_precedence():
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").set_prop("a", "'prop_a'")
concept = Concept("foo", body="a + b").def_prop("a", "'prop_a'")
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.key == concept.key
assert evaluated.body == 'prop_aconcept_b'
@@ -775,18 +777,17 @@ def test_properties_values_takes_precedence():
def test_i_can_reference_sub_property_of_a_property():
sheerka = get_sheerka()
sheerka.add_in_cache(Concept(name="concept_a").set_prop("subProp", "'sub_a'"))
sheerka.add_in_cache(Concept(name="concept_a").def_prop("subProp", "'sub_a'"))
concept = Concept("foo", body="a.props['subProp'].value").set_prop("a", "concept_a")
concept = Concept("foo", body="a.props['subProp'].value").def_prop("a", "concept_a")
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.key == concept.key
assert evaluated.body == 'sub_a'
assert evaluated == simplec(concept.key, "sub_a")
def test_i_cannot_evaluate_concept_if_property_is_in_error():
sheerka = get_sheerka()
concept = Concept(name="concept_a").set_prop("subProp", "undef_concept")
concept = Concept(name="concept_a").def_prop("subProp", "undef_concept")
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert sheerka.isinstance(evaluated, BuiltinConcepts.CONCEPT_EVAL_ERROR)
+33 -32
View File
@@ -5,7 +5,7 @@ from os import path
import pytest
from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept, PROPERTIES_TO_SERIALIZE, Property
from core.concept import Concept, PROPERTIES_TO_SERIALIZE, Property, simplec
from core.sheerka import Sheerka, ExecutionContext
from evaluators.MutipleSameSuccessEvaluator import MultipleSameSuccessEvaluator
from parsers.ConceptLexerParser import Sequence, StrMatch, OrderedChoice, Optional, ConceptExpression
@@ -50,8 +50,8 @@ def get_default_concept():
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")
concept.def_prop("a", "value1")
concept.def_prop("b", "value2")
return concept
@@ -79,7 +79,7 @@ def test_i_can_eval_concept_with_python_body():
res = sheerka.evaluate_user_input(text)
assert len(res) == 1
assert res[0].status
assert res[0].value == Concept(name="one", body=1).init_key() # by default, the concept is returned
assert res[0].value == simplec("one", 1) # by default, the concept is returned
def test_i_can_eval_concept_with_concept_body():
@@ -93,7 +93,7 @@ def test_i_can_eval_concept_with_concept_body():
return_value = res[0].value
assert len(res) == 1
assert res[0].status
assert return_value == Concept(name="un", body=Concept(name="one").init_key()).init_key()
assert return_value == simplec("un", simplec("one", None))
def test_i_can_eval_concept_with_no_body():
@@ -137,6 +137,7 @@ as:
expected = get_default_concept()
expected.metadata.id = "1001"
expected.metadata.desc = None
expected.metadata.props = [("a", None), ("b", None)]
expected.init_key()
sheerka = get_sheerka()
@@ -165,11 +166,11 @@ def test_i_can_eval_def_concept_part_when_one_part_is_a_ref_of_another_concept()
sheerka = get_sheerka()
# concept 'a plus b' is known
concept_a_plus_b = Concept(name="a plus b").set_prop("a").set_prop("b")
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").set_prop("a").set_prop("b").init_key()
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
@@ -224,7 +225,7 @@ def test_i_can_eval_a_empty_input(text):
def test_i_can_eval_concept_with_variable():
sheerka = get_sheerka()
concept_hello = Concept(name="hello a").set_prop("a")
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)
@@ -239,7 +240,7 @@ def test_i_can_eval_concept_with_variable():
def test_i_can_eval_concept_with_variable_and_python_as_body():
sheerka = get_sheerka()
hello_a = sheerka.add_in_cache(Concept(name="hello a", body="'hello ' + a").set_prop("a"))
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")
@@ -248,14 +249,14 @@ def test_i_can_eval_concept_with_variable_and_python_as_body():
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 == Concept(name="foo", body="foo").init_key()
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():
sheerka = get_sheerka()
sheerka.add_in_cache(Concept(name="hello a", body="'hello ' + a").set_prop("a"))
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'"))
@@ -269,7 +270,7 @@ def test_i_can_eval_duplicate_concepts_with_same_value():
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 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'"))
@@ -289,8 +290,8 @@ 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"))
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
@@ -453,10 +454,10 @@ def test_i_can_mix_concept_with_python_to_define_numbers(desc, definitions):
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
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():
@@ -472,20 +473,20 @@ def test_i_can_mix_concept_of_concept():
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 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
+75 -43
View File
@@ -1,5 +1,5 @@
from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept
from core.concept import Concept, ConceptParts
from core.sheerka import Sheerka, ExecutionContext, ExecutionContextIdManager
from core.sheerka_transform import SheerkaTransform, OBJ_TYPE_KEY, SheerkaTransformType, OBJ_ID_KEY
from sdp.sheerkaDataProvider import Event
@@ -33,7 +33,14 @@ def test_i_can_transform_an_unknown_concept():
definition="it is a definition",
definition_type="def type",
desc="this this the desc"
).set_prop("a", 10).set_prop("b", foo).set_prop("c", concept_with_sub)
).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)
@@ -41,28 +48,41 @@ def test_i_can_transform_an_unknown_concept():
assert to_dict == {
OBJ_TYPE_KEY: SheerkaTransformType.Concept,
OBJ_ID_KEY: 0,
'name': 'concept_name',
'key': 'concept_key',
'is_builtin': True,
'is_unique': True,
'definition': 'it is a definition',
'definition_type': 'def type',
'desc': 'this this the desc',
'where': [{OBJ_TYPE_KEY: SheerkaTransformType.Concept,
OBJ_ID_KEY: 1,
'body': 'body',
'name': 'foo'}, 1, '1', True, 1.0],
'pre': {OBJ_TYPE_KEY: SheerkaTransformType.Reference, OBJ_ID_KEY: 1},
'body': {
OBJ_TYPE_KEY: SheerkaTransformType.Concept,
OBJ_ID_KEY: 2,
'name': 'concept_with_sub',
'body': {OBJ_TYPE_KEY: SheerkaTransformType.Reference, OBJ_ID_KEY: 1}},
'props': [
('a', 10),
('b', {OBJ_TYPE_KEY: SheerkaTransformType.Reference, OBJ_ID_KEY: 1}),
('c', {OBJ_TYPE_KEY: SheerkaTransformType.Reference, OBJ_ID_KEY: 2})
]
'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})],
}
@@ -73,10 +93,16 @@ def test_i_can_transform_unknown_concept_with_almost_same_value():
st = SheerkaTransform(sheerka)
to_dict = st.to_dict(concept)
assert to_dict == {OBJ_TYPE_KEY: SheerkaTransformType.Concept, OBJ_ID_KEY: 0, 'name': 'foo'}
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():
"""
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 = get_sheerka()
concept = Concept(
@@ -91,7 +117,7 @@ def test_i_can_transform_known_concept_when_the_values_are_the_same():
definition="it is a definition",
definition_type="def type",
desc="this this the desc"
).set_prop("a").set_prop("b")
).def_prop("a").def_prop("b")
sheerka.create_new_concept(get_context(sheerka), concept)
new_concept = sheerka.new(concept.key)
@@ -102,6 +128,12 @@ def test_i_can_transform_known_concept_when_the_values_are_the_same():
def test_i_can_transform_known_concept_when_the_values_are_different():
"""
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 = get_sheerka()
concept = Concept(
@@ -116,7 +148,7 @@ def test_i_can_transform_known_concept_when_the_values_are_different():
definition="it is a definition",
definition_type="def type",
desc="this this the desc"
).set_prop("a").set_prop("b")
).def_prop("a").def_prop("b")
sheerka.create_new_concept(get_context(sheerka), concept)
new_concept = sheerka.new(concept.key, body="another", a=10, pre="another pre")
@@ -135,7 +167,7 @@ def test_i_can_transform_known_concept_when_the_values_are_different():
def test_i_can_transform_concept_with_circular_reference():
sheerka = get_sheerka()
foo = Concept("foo", )
foo = Concept("foo")
bar = Concept("bar", body=foo)
foo.metadata.body = bar
@@ -145,19 +177,19 @@ def test_i_can_transform_concept_with_circular_reference():
assert to_dict == {
OBJ_TYPE_KEY: SheerkaTransformType.Concept,
OBJ_ID_KEY: 0,
'name': 'foo',
'body': {OBJ_TYPE_KEY: SheerkaTransformType.Concept,
OBJ_ID_KEY: 1,
'name': 'bar',
'body': {OBJ_TYPE_KEY: SheerkaTransformType.Reference,
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():
sheerka = get_sheerka()
foo = Concept("foo", )
foo = Concept("foo")
bar = Concept("foo", body=foo)
foo.metadata.body = bar
@@ -167,13 +199,13 @@ def test_i_can_transform_concept_with_circular_reference_2():
assert to_dict == {
OBJ_TYPE_KEY: SheerkaTransformType.Concept,
OBJ_ID_KEY: 0,
'name': 'foo',
'body': {OBJ_TYPE_KEY: SheerkaTransformType.Concept,
OBJ_ID_KEY: 1,
'name': 'foo',
'body': {OBJ_TYPE_KEY: SheerkaTransformType.Reference,
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},
},
}