Refactored to use cached_asts in Concepts, rather than setting up a value directly

This commit is contained in:
2020-01-12 10:28:44 +01:00
parent 73a6d4e6c2
commit 51fa9629d0
9 changed files with 256 additions and 167 deletions
+13
View File
@@ -293,3 +293,16 @@ class Property:
def __hash__(self): def __hash__(self):
return hash((self.name, self.value)) return hash((self.name, self.value))
@dataclass()
class DoNotResolve:
"""
This class is used to that the metadata (or the prop) of the concept must not be evaluated
thru sheerka.execute
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)
"""
value: object
+74 -30
View File
@@ -1,5 +1,5 @@
from core.builtin_concepts import BuiltinConcepts, ErrorConcept, ReturnValueConcept, BuiltinErrors, BuiltinUnique from core.builtin_concepts import BuiltinConcepts, ErrorConcept, ReturnValueConcept, BuiltinErrors, BuiltinUnique
from core.concept import Concept, ConceptParts, PROPERTIES_FOR_NEW from core.concept import Concept, ConceptParts, PROPERTIES_FOR_NEW, DoNotResolve
from parsers.BaseParser import BaseParser from parsers.BaseParser import BaseParser
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event, SheerkaDataProviderDuplicateKeyError from sdp.sheerkaDataProvider import SheerkaDataProvider, Event, SheerkaDataProviderDuplicateKeyError
import core.utils import core.utils
@@ -551,6 +551,9 @@ class Sheerka(Concept):
""" """
steps = [BuiltinConcepts.BEFORE_PARSING, BuiltinConcepts.PARSING, BuiltinConcepts.AFTER_PARSING] steps = [BuiltinConcepts.BEFORE_PARSING, BuiltinConcepts.PARSING, BuiltinConcepts.AFTER_PARSING]
for part_key in ConceptParts: for part_key in ConceptParts:
if part_key in concept.cached_asts:
continue
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
@@ -565,6 +568,9 @@ class Sheerka(Concept):
sub_context.add_values(return_values=res) sub_context.add_values(return_values=res)
for prop in concept.props: for prop in concept.props:
if prop in concept.cached_asts:
continue
value = concept.props[prop].value value = concept.props[prop].value
if value: if value:
if isinstance(value, Concept): if isinstance(value, Concept):
@@ -604,14 +610,65 @@ class Sheerka(Concept):
if concept.metadata.is_evaluated: if concept.metadata.is_evaluated:
return concept return concept
def _resolve(return_value, desc, obj): def _resolve(to_resolve, current_prop, current_concept):
if isinstance(to_resolve, DoNotResolve):
return to_resolve.value
desc = f"Evaluating {current_prop} (concept={current_concept})"
context.log(logger, desc, self.evaluate_concept.__name__) context.log(logger, desc, self.evaluate_concept.__name__)
with context.push(desc=desc, obj=obj) as sub_context: with context.push(desc=desc, obj=current_concept) as sub_context:
sub_context.log_new(logger) sub_context.log_new(logger)
r = self.execute(sub_context, return_value, CONCEPT_EVALUATION_STEPS, logger)
# when it's a concept, evaluate it
if isinstance(to_resolve, Concept):
evaluated = self.evaluate_concept(sub_context, to_resolve)
sub_context.add_values(return_values=evaluated)
if evaluated.key == to_resolve.key:
return evaluated
else:
error = evaluated
# otherwise, execute all return values to find out what is the value
else:
r = self.execute(sub_context, to_resolve, CONCEPT_EVALUATION_STEPS, logger)
one_r = core.builtin_helpers.expect_one(context, r) one_r = core.builtin_helpers.expect_one(context, r)
sub_context.add_values(return_values=one_r) sub_context.add_values(return_values=one_r)
return one_r if one_r.status:
return one_r.value
else:
error = one_r.value
return self.new(BuiltinConcepts.CONCEPT_EVAL_ERROR,
body=error,
concept=concept,
property_name=prop_name)
def _resolve_list(sheerka, list_to_resolve, current_prop, current_concept):
"""When dealing with a list, there are two possibilities"""
# It may be a list of ReturnValueConcept to execute (always the case for metadata)
# or a list of single values (may be the case for properties)
# in this latter case, all values are to be processed one by one and a list should be returned
if len(list_to_resolve) == 0:
return []
if sheerka.isinstance(list_to_resolve[0], BuiltinConcepts.RETURN_VALUE):
return _resolve(list_to_resolve, current_prop, current_concept)
res = []
for to_resolve in list_to_resolve:
# sanity check
if sheerka.isinstance(to_resolve, BuiltinConcepts.RETURN_VALUE):
return self.new(BuiltinConcepts.CONCEPT_EVAL_ERROR,
body="Mix between real values and return values",
concept=concept,
property_name=prop_name)
r = _resolve(to_resolve, current_prop, current_concept)
if sheerka.isinstance(r, BuiltinConcepts.CONCEPT_EVAL_ERROR):
return r
res.append(r)
return res
# WHERE condition should already be validated by the parser. # WHERE condition should already be validated by the parser.
# It's a mandatory condition for the concept before it can be recognized # It's a mandatory condition for the concept before it can be recognized
@@ -620,8 +677,6 @@ class Sheerka(Concept):
# TODO : Validate the PRE condition # TODO : Validate the PRE condition
# #
if len(concept.cached_asts) == 0:
context.log(logger, "concept asts are not initialized. Initializing.", self.evaluate_concept.__name__)
self.initialize_concept_asts(context, concept, logger) self.initialize_concept_asts(context, concept, logger)
# to make sure of the order, it don't use ConceptParts.get_parts() # to make sure of the order, it don't use ConceptParts.get_parts()
@@ -632,35 +687,24 @@ class Sheerka(Concept):
if metadata_to_eval == "props": if metadata_to_eval == "props":
for prop_name in (p for p in concept.props if p in concept.cached_asts): for prop_name in (p for p in concept.props if p in concept.cached_asts):
prop_ast = concept.cached_asts[prop_name] prop_ast = concept.cached_asts[prop_name]
if isinstance(concept.cached_asts[prop_name], Concept):
context.log( if isinstance(prop_ast, list):
logger, f"Evaluation prop={prop_name}, value={prop_ast}", self.evaluate_concept.__name__) resolved = _resolve_list(context.sheerka, prop_ast, prop_name, None)
with context.push(f"Evaluation property '{prop_name}', value='{prop_ast}'") as sub_context:
sub_context.log_new(logger)
evaluated = self.evaluate_concept(sub_context, prop_ast)
sub_context.add_values(return_values=evaluated)
concept.set_prop(prop_name, evaluated)
else: else:
res = _resolve(prop_ast, f"Evaluating property '{prop_name}'", None) resolved = _resolve(prop_ast, prop_name, None)
if res.status: if context.sheerka.isinstance(resolved, BuiltinConcepts.CONCEPT_EVAL_ERROR):
concept.set_prop(prop_name, res.value) return resolved
else: else:
return self.new(BuiltinConcepts.CONCEPT_EVAL_ERROR, concept.set_prop(prop_name, resolved)
body=res.value,
concept=concept,
property_name=prop_name)
else: else:
part_key = ConceptParts(metadata_to_eval) part_key = ConceptParts(metadata_to_eval)
if part_key in concept.cached_asts and concept.cached_asts[part_key] is not None: if part_key in concept.cached_asts and concept.cached_asts[part_key] is not None:
res = _resolve(concept.cached_asts[part_key], f"Evaluating '{part_key}'", concept) metadata_ast = concept.cached_asts[part_key]
if res.status: resolved = _resolve(metadata_ast, part_key, concept)
setattr(concept.metadata, metadata_to_eval, res.value) if context.sheerka.isinstance(resolved, BuiltinConcepts.CONCEPT_EVAL_ERROR):
return resolved
else: else:
return self.new(BuiltinConcepts.CONCEPT_EVAL_ERROR, setattr(concept.metadata, metadata_to_eval, resolved)
body=res.value,
concept=concept,
property_name=part_key)
# #
# TODO : Validate the POST condition # TODO : Validate the POST condition
+1 -3
View File
@@ -107,9 +107,7 @@ class PythonEvaluator(OneReturnValueEvaluator):
sub_context.add_values(return_values=evaluated) sub_context.add_values(return_values=evaluated)
if evaluated.key == concept.key: if evaluated.key == concept.key:
my_locals[name] = evaluated if return_concept else \ my_locals[name] = evaluated if return_concept else context.sheerka.value(evaluated)
evaluated.body if ConceptParts.BODY in evaluated.cached_asts else \
evaluated
if self.locals: if self.locals:
my_locals.update(self.locals) my_locals.update(self.locals)
+10 -8
View File
@@ -9,7 +9,7 @@
from dataclasses import field, dataclass from dataclasses import field, dataclass
from collections import defaultdict from collections import defaultdict
from core.builtin_concepts import BuiltinConcepts from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept from core.concept import Concept, ConceptParts, DoNotResolve
from core.tokenizer import TokenKind, Tokenizer, Token from core.tokenizer import TokenKind, Tokenizer, Token
from parsers.BaseParser import BaseParser, Node, ErrorNode from parsers.BaseParser import BaseParser, Node, ErrorNode
import core.utils import core.utils
@@ -702,6 +702,9 @@ class ConceptLexerParser(BaseParser):
else: else:
to_match = node.concept to_match = node.concept
if to_match not in self.concepts_grammars:
return False
return _is_infinite_recursion(ref_concept, self.concepts_grammars[to_match]) return _is_infinite_recursion(ref_concept, self.concepts_grammars[to_match])
if isinstance(node, OrderedChoice): if isinstance(node, OrderedChoice):
@@ -847,17 +850,17 @@ class ConceptLexerParser(BaseParser):
Adds a new entry, Adds a new entry,
makes a list if the property already exists makes a list if the property already exists
""" """
if prop_name not in _concept.props or _concept.props[prop_name].value is None: if prop_name not in _concept.cached_asts or _concept.cached_asts[prop_name] is None:
# new entry # new entry
_concept.set_prop(prop_name, value) _concept.cached_asts[prop_name] = value
else: else:
# make a list if there was a value # make a list if there was a value
previous_value = _concept.props[prop_name].value previous_value = _concept.cached_asts[prop_name]
if isinstance(previous_value, list): if isinstance(previous_value, list):
previous_value.append(value) previous_value.append(value)
else: else:
new_value = [previous_value, value] new_value = [previous_value, value]
_concept.set_prop(prop_name, new_value) _concept.cached_asts[prop_name] = new_value
def _look_for_concept_match(_underlying): def _look_for_concept_match(_underlying):
if isinstance(_underlying.parsing_expression, ConceptMatch): if isinstance(_underlying.parsing_expression, ConceptMatch):
@@ -881,7 +884,7 @@ class ConceptLexerParser(BaseParser):
result = self.finalize_concept(sheerka, ref_tpl, concept_match_node.children[0], init_empty_body) result = self.finalize_concept(sheerka, ref_tpl, concept_match_node.children[0], init_empty_body)
_underlying_value_cache[id(concept_match_node)] = result _underlying_value_cache[id(concept_match_node)] = result
else: else:
result = _underlying.source result = DoNotResolve(_underlying.source)
return result return result
@@ -898,8 +901,7 @@ class ConceptLexerParser(BaseParser):
concept = sheerka.new(key) concept = sheerka.new(key)
if init_empty_body and concept.body is None: if init_empty_body and concept.body is None:
value = _get_underlying_value(underlying) value = _get_underlying_value(underlying)
concept.metadata.body = value concept.cached_asts[ConceptParts.BODY] = value
concept.metadata.is_evaluated = True
if underlying.parsing_expression.rule_name: if underlying.parsing_expression.rule_name:
_add_prop(concept, underlying.parsing_expression.rule_name, value) _add_prop(concept, underlying.parsing_expression.rule_name, value)
+59 -71
View File
@@ -1,7 +1,7 @@
import pytest import pytest
import core.utils import core.utils
from core.builtin_concepts import BuiltinConcepts from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept from core.concept import Concept, ConceptParts, DoNotResolve
from core.sheerka import Sheerka, ExecutionContext from core.sheerka import Sheerka, ExecutionContext
from core.tokenizer import Tokenizer, TokenKind, Token from core.tokenizer import Tokenizer, TokenKind, Token
from parsers.ConceptLexerParser import ConceptLexerParser, ConceptNode, Sequence, StrMatch, OrderedChoice, Optional, \ from parsers.ConceptLexerParser import ConceptLexerParser, ConceptNode, Sequence, StrMatch, OrderedChoice, Optional, \
@@ -58,7 +58,20 @@ def get_context():
def get_expected(concept, text=None): def get_expected(concept, text=None):
return Concept(name=concept.name, body=text or concept.name).init_key() c = Concept(name=concept.name)
c.cached_asts[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]
def cprop(concept, prop_name):
"""cbody stands for compiled property"""
return concept.cached_asts[prop_name]
def init(concepts, grammar): def init(concepts, grammar):
@@ -359,20 +372,15 @@ def test_i_can_use_reference():
assert context.sheerka.isinstance(res[0].value, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(res[0].value, BuiltinConcepts.PARSER_RESULT)
assert res[0].value.body == [("foo", 0, 2, "one two")] assert res[0].value.body == [("foo", 0, 2, "one two")]
concept_found_1 = res[0].value.body[0].concept concept_found_1 = res[0].value.body[0].concept
assert concept_found_1.metadata.is_evaluated assert cbody(concept_found_1) == DoNotResolve("one two")
assert concept_found_1.body == "one two"
assert res[1].status assert res[1].status
assert context.sheerka.isinstance(res[1].value, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(res[1].value, BuiltinConcepts.PARSER_RESULT)
assert res[1].value.body == [("bar", 0, 2, "one two")] assert res[1].value.body == [("bar", 0, 2, "one two")]
concept_found_2 = res[1].value.body[0].concept concept_found_2 = res[1].value.body[0].concept
assert concept_found_2.metadata.is_evaluated
# the body and the prop['foo'] are the same concept 'foo' # the body and the prop['foo'] are the same concept 'foo'
assert isinstance(concept_found_2.body, Concept) assert cbody(concept_found_2) == get_expected(foo, "one two")
assert concept_found_2.body.key == "foo" assert id(cprop(concept_found_2, "foo")) == id(cbody(concept_found_2))
assert concept_found_2.body.metadata.is_evaluated
assert concept_found_2.body.body == "one two"
assert id(concept_found_2.props["foo"].value) == id(concept_found_2.body)
def test_i_can_use_a_reference_with_a_body(): def test_i_can_use_a_reference_with_a_body():
@@ -394,20 +402,15 @@ def test_i_can_use_a_reference_with_a_body():
assert context.sheerka.isinstance(res[0].value, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(res[0].value, BuiltinConcepts.PARSER_RESULT)
assert res[0].value.body == [("foo", 0, 2, "one two")] assert res[0].value.body == [("foo", 0, 2, "one two")]
concept_found_1 = res[0].value.body[0].concept concept_found_1 = res[0].value.body[0].concept
assert not concept_found_1.metadata.is_evaluated
assert concept_found_1.body == "'foo'" assert concept_found_1.body == "'foo'"
assert res[1].status assert res[1].status
assert context.sheerka.isinstance(res[1].value, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(res[1].value, BuiltinConcepts.PARSER_RESULT)
assert res[1].value.body == [("bar", 0, 2, "one two")] assert res[1].value.body == [("bar", 0, 2, "one two")]
concept_found_2 = res[1].value.body[0].concept concept_found_2 = res[1].value.body[0].concept
assert concept_found_2.metadata.is_evaluated
# the body and the prop['foo'] are the same concept 'foo' # the body and the prop['foo'] are the same concept 'foo'
assert isinstance(concept_found_2.body, Concept) assert cbody(concept_found_2) == foo
assert concept_found_2.body.key == "foo" assert id(cprop(concept_found_2, "foo")) == id(cbody(concept_found_2))
assert not concept_found_2.body.metadata.is_evaluated
assert concept_found_2.body.body == "'foo'"
assert id(concept_found_2.props["foo"].value) == id(concept_found_2.body)
def test_i_can_use_context_reference_with_multiple_levels(): def test_i_can_use_context_reference_with_multiple_levels():
@@ -429,23 +432,23 @@ def test_i_can_use_context_reference_with_multiple_levels():
assert context.sheerka.isinstance(res[0].value, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(res[0].value, BuiltinConcepts.PARSER_RESULT)
assert res[0].value.body == [("foo", 0, 2, "one two")] assert res[0].value.body == [("foo", 0, 2, "one two")]
concept_found_1 = res[0].value.body[0].concept concept_found_1 = res[0].value.body[0].concept
assert concept_found_1.body == "one two" assert cbody(concept_found_1) == DoNotResolve("one two")
assert concept_found_1.metadata.is_evaluated
assert res[1].status assert res[1].status
assert context.sheerka.isinstance(res[1].value, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(res[1].value, BuiltinConcepts.PARSER_RESULT)
assert res[1].value.body == [("bar", 0, 2, "one two")] assert res[1].value.body == [("bar", 0, 2, "one two")]
concept_found_2 = res[1].value.body[0].concept concept_found_2 = res[1].value.body[0].concept
assert concept_found_2.body == get_expected(foo, "one two") assert cbody(concept_found_2) == get_expected(foo, "one two")
assert id(concept_found_2.props["foo"].value) == id(concept_found_2.body) assert id(cprop(concept_found_2, "foo")) == id(cbody(concept_found_2))
assert res[2].status assert res[2].status
assert context.sheerka.isinstance(res[2].value, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(res[2].value, BuiltinConcepts.PARSER_RESULT)
assert res[2].value.body == [("baz", 0, 2, "one two")] assert res[2].value.body == [("baz", 0, 2, "one two")]
concept_found_3 = res[2].value.body[0].concept concept_found_3 = res[2].value.body[0].concept
expected_foo = get_expected(foo, "one two") expected_foo = get_expected(foo, "one two")
assert concept_found_3.body == get_expected(bar, expected_foo).set_prop("foo", expected_foo) assert cbody(concept_found_3) == get_expected(bar, expected_foo)
assert id(concept_found_3.props["bar"].value) == id(concept_found_3.body) assert cprop(concept_found_3, "foo") == expected_foo
assert id(cprop(concept_found_3, "bar")) == id(cbody(concept_found_3))
def test_order_is_not_important_when_using_references(): def test_order_is_not_important_when_using_references():
@@ -476,26 +479,21 @@ def test_i_can_parse_when_reference():
assert res.status assert res.status
assert res.value.body == [("bar", 0, 2, "twenty two")] assert res.value.body == [("bar", 0, 2, "twenty two")]
concept_found = res.value.body[0].concept concept_found = res.value.body[0].concept
assert concept_found.body == "twenty two" assert cbody(concept_found) == DoNotResolve("twenty two")
assert concept_found.metadata.is_evaluated assert cprop(concept_found, "foo") == get_expected(foo, "twenty")
assert concept_found.get_prop("foo") == get_expected(foo, "twenty")
assert concept_found.get_prop("foo").metadata.is_evaluated
res = parser.parse(context, "thirty one") res = parser.parse(context, "thirty one")
assert res.status assert res.status
assert res.value.body == [("bar", 0, 2, "thirty one")] assert res.value.body == [("bar", 0, 2, "thirty one")]
concept_found = res.value.body[0].concept concept_found = res.value.body[0].concept
assert concept_found.body == "thirty one" assert cbody(concept_found) == DoNotResolve("thirty one")
assert concept_found.metadata.is_evaluated assert cprop(concept_found, "foo") == get_expected(foo, "thirty")
assert concept_found.get_prop("foo") == get_expected(foo, "thirty")
assert concept_found.get_prop("foo").metadata.is_evaluated
res = parser.parse(context, "twenty") res = parser.parse(context, "twenty")
assert res.status assert res.status
assert res.value.body == [("foo", 0, 0, "twenty")] assert res.value.body == [("foo", 0, 0, "twenty")]
concept_found = res.value.body[0].concept concept_found = res.value.body[0].concept
assert concept_found.body == "twenty" assert cbody(concept_found) == DoNotResolve("twenty")
assert concept_found.metadata.is_evaluated
def test_i_can_parse_when_reference_has_a_body(): def test_i_can_parse_when_reference_has_a_body():
@@ -508,17 +506,14 @@ def test_i_can_parse_when_reference_has_a_body():
assert res.status assert res.status
assert res.value.body == [("bar", 0, 2, "twenty two")] assert res.value.body == [("bar", 0, 2, "twenty two")]
concept_found = res.value.body[0].concept concept_found = res.value.body[0].concept
assert concept_found.body == "twenty two" assert cbody(concept_found) == DoNotResolve("twenty two")
assert concept_found.metadata.is_evaluated assert cprop(concept_found, "foo") == foo
assert concept_found.get_prop("foo") == get_expected(foo, "'one'")
assert not concept_found.get_prop("foo").metadata.is_evaluated
res = parser.parse(context, "twenty") res = parser.parse(context, "twenty")
assert res.status assert res.status
assert res.value.body == [("foo", 0, 0, "twenty")] assert res.value.body == [("foo", 0, 0, "twenty")]
concept_found = res.value.body[0].concept concept_found = res.value.body[0].concept
assert concept_found.body == "'one'" assert concept_found.body == "'one'"
assert not concept_found.metadata.is_evaluated
def test_i_can_parse_multiple_results(): def test_i_can_parse_multiple_results():
@@ -536,16 +531,14 @@ def test_i_can_parse_multiple_results():
assert context.sheerka.isinstance(res[0].value, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(res[0].value, BuiltinConcepts.PARSER_RESULT)
assert res[0].value.body == [("bar", 0, 2, "one two")] assert res[0].value.body == [("bar", 0, 2, "one two")]
concept_found_0 = res[0].value.body[0].concept concept_found_0 = res[0].value.body[0].concept
assert concept_found_0.body == "one two" assert cbody(concept_found_0) == DoNotResolve("one two")
assert concept_found_0.metadata.is_evaluated
assert len(concept_found_0.props) == 0 assert len(concept_found_0.props) == 0
assert res[1].status assert res[1].status
assert context.sheerka.isinstance(res[1].value, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(res[1].value, BuiltinConcepts.PARSER_RESULT)
assert res[1].value.body == [("foo", 0, 2, "one two")] assert res[1].value.body == [("foo", 0, 2, "one two")]
concept_found_1 = res[1].value.body[0].concept concept_found_1 = res[1].value.body[0].concept
assert concept_found_1.body == "one two" assert cbody(concept_found_1) == DoNotResolve("one two")
assert concept_found_1.metadata.is_evaluated
assert len(concept_found_1.props) == 0 assert len(concept_found_1.props) == 0
@@ -617,10 +610,8 @@ def test_i_can_parse_concept_reference_that_is_not_in_grammar():
assert res.status assert res.status
assert res.value.body == [("foo", 0, 2, "twenty two")] assert res.value.body == [("foo", 0, 2, "twenty two")]
concept_found = res.value.body[0].concept concept_found = res.value.body[0].concept
assert concept_found.body == "twenty two" assert cbody(concept_found) == DoNotResolve("twenty two")
assert concept_found.metadata.is_evaluated assert cprop(concept_found, "two") == get_expected(two, "two")
assert concept_found.get_prop("two") == get_expected(two, "two")
assert concept_found.get_prop("two").metadata.is_evaluated
res = parser.parse(context, "twenty one") res = parser.parse(context, "twenty one")
assert res.status assert res.status
@@ -638,8 +629,7 @@ def test_i_can_parse_zero_or_more():
assert return_value[0].underlying == u(grammar[foo], 0, 2, [u("one", 0, 0), u("one", 2, 2)]) assert return_value[0].underlying == u(grammar[foo], 0, 2, [u("one", 0, 0), u("one", 2, 2)])
concept_found = return_value[0].concept concept_found = return_value[0].concept
assert concept_found.body == "one one" assert cbody(concept_found) == DoNotResolve("one one")
assert concept_found.metadata.is_evaluated
def test_i_can_parse_sequence_and_zero_or_more(): def test_i_can_parse_sequence_and_zero_or_more():
@@ -1064,8 +1054,8 @@ def test_i_can_get_the_inner_concept_when_possible():
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
assert return_value == [("foo", 0, 0, "one")] assert return_value == [("foo", 0, 0, "one")]
concept_found = return_value[0].concept concept_found = return_value[0].concept
assert concept_found.body == get_expected(one, "one") assert cbody(concept_found) == get_expected(one, "one")
assert concept_found.get_prop("one") == concept_found.body assert id(cprop(concept_found, "one")) == id(cbody(concept_found))
def test_i_can_get_the_inner_concept_when_possible_with_rule_name(): def test_i_can_get_the_inner_concept_when_possible_with_rule_name():
@@ -1081,11 +1071,11 @@ def test_i_can_get_the_inner_concept_when_possible_with_rule_name():
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
assert return_value == [("foo", 0, 0, "one")] assert return_value == [("foo", 0, 0, "one")]
concept_found = return_value[0].concept concept_found = return_value[0].concept
assert concept_found.body == get_expected(one, "one") assert cbody(concept_found) == get_expected(one, "one")
assert id(concept_found.get_prop("one")) == id(concept_found.body) assert id(cprop(concept_found, "one")) == id(cbody(concept_found))
assert id(concept_found.get_prop("zero")) == id(concept_found.body) assert id(cprop(concept_found, "zero")) == id(cbody(concept_found))
assert id(concept_found.get_prop("opt")) == id(concept_found.body) assert id(cprop(concept_found, "opt")) == id(cbody(concept_found))
assert id(concept_found.get_prop("seq")) == id(concept_found.body) assert id(cprop(concept_found, "seq")) == id(cbody(concept_found))
def test_i_get_multiple_props_when_zero_or_more(): def test_i_get_multiple_props_when_zero_or_more():
@@ -1098,15 +1088,14 @@ def test_i_get_multiple_props_when_zero_or_more():
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
assert return_value == [("foo", 0, 4, "one one one")] assert return_value == [("foo", 0, 4, "one one one")]
concept_found = return_value[0].concept concept_found = return_value[0].concept
assert concept_found.body == "one one one" assert cbody(concept_found) == DoNotResolve("one one one")
assert len(concept_found.props) == 1 assert len(concept_found.cached_asts["one"]) == 3
assert len(concept_found.get_prop("one")) == 3 assert cprop(concept_found, "one")[0] == get_expected(one)
assert concept_found.get_prop("one")[0] == get_expected(one) assert cprop(concept_found, "one")[1] == get_expected(one)
assert concept_found.get_prop("one")[1] == get_expected(one) assert cprop(concept_found, "one")[2] == get_expected(one)
assert concept_found.get_prop("one")[2] == get_expected(one) assert id(cprop(concept_found, "one")[0]) != id(cprop(concept_found, "one")[1])
assert id(concept_found.get_prop("one")[0]) != id(concept_found.get_prop("one")[1]) assert id(cprop(concept_found, "one")[1]) != id(cprop(concept_found, "one")[2])
assert id(concept_found.get_prop("one")[1]) != id(concept_found.get_prop("one")[2]) assert id(cprop(concept_found, "one")[2]) != id(cprop(concept_found, "one")[0])
assert id(concept_found.get_prop("one")[2]) != id(concept_found.get_prop("one")[0])
def test_i_get_multiple_props_when_zero_or_more_and_different_values(): def test_i_get_multiple_props_when_zero_or_more_and_different_values():
@@ -1119,13 +1108,12 @@ def test_i_get_multiple_props_when_zero_or_more_and_different_values():
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
assert return_value == [("foo", "one ok un ok uno ok")] assert return_value == [("foo", "one ok un ok uno ok")]
concept_found = return_value[0].concept concept_found = return_value[0].concept
assert concept_found.get_prop("one")[0] == get_expected(one, "one") assert cprop(concept_found, "one")[0] == get_expected(one, "one")
assert concept_found.get_prop("one")[1] == get_expected(one, "un") assert cprop(concept_found, "one")[1] == get_expected(one, "un")
assert concept_found.get_prop("one")[2] == get_expected(one, "uno") assert cprop(concept_found, "one")[2] == get_expected(one, "uno")
assert concept_found.get_prop("seq")[0] == "one ok" assert cprop(concept_found, "seq")[0] == DoNotResolve("one ok")
assert concept_found.get_prop("seq")[1] == "un ok" assert cprop(concept_found, "seq")[1] == DoNotResolve("un ok")
assert concept_found.get_prop("seq")[2] == "uno ok" assert cprop(concept_found, "seq")[2] == DoNotResolve("uno ok")
# #
+3 -3
View File
@@ -1,7 +1,7 @@
import pytest import pytest
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
from core.concept import Concept from core.concept import Concept, ConceptParts, DoNotResolve
from core.sheerka import Sheerka, ExecutionContext from core.sheerka import Sheerka, ExecutionContext
from evaluators.ConceptNodeEvaluator import ConceptNodeEvaluator from evaluators.ConceptNodeEvaluator import ConceptNodeEvaluator
from parsers.ConceptLexerParser import ConceptNode, ConceptLexerParser, Sequence, TerminalNode, \ from parsers.ConceptLexerParser import ConceptNode, ConceptLexerParser, Sequence, TerminalNode, \
@@ -73,7 +73,7 @@ def test_concept_is_returned_when_only_one_in_the_list():
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
assert wrapper.parser == evaluator assert wrapper.parser == evaluator
assert wrapper.source == "foo" assert wrapper.source == "foo"
assert return_value == Concept("foo", body="foo").init_key() assert return_value == Concept("foo").init_key()
assert return_value.metadata.is_evaluated assert return_value.cached_asts[ConceptParts.BODY] == DoNotResolve("foo")
assert result.parents == [ret_val] assert result.parents == [ret_val]
+1 -1
View File
@@ -84,7 +84,7 @@ def test_i_can_eval_module_with_that_references_concepts():
context = get_context() context = get_context()
context.sheerka.add_in_cache(Concept("foo")) context.sheerka.add_in_cache(Concept("foo"))
parsed = PythonParser().parse(context, "def a(b):\n return b\na(foo)") parsed = PythonParser().parse(context, "def a(b):\n return b\na(c:foo:)")
evaluated = PythonEvaluator().eval(context, parsed) evaluated = PythonEvaluator().eval(context, parsed)
assert evaluated.status assert evaluated.status
+63 -1
View File
@@ -4,7 +4,7 @@ from os import path
import shutil import shutil
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept, UserInputConcept, ConceptAlreadyInSet from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept, UserInputConcept, ConceptAlreadyInSet
from core.concept import Concept, PROPERTIES_TO_SERIALIZE, Property from core.concept import Concept, PROPERTIES_TO_SERIALIZE, Property, ConceptParts, DoNotResolve
from core.sheerka import Sheerka, ExecutionContext from core.sheerka import Sheerka, ExecutionContext
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event from sdp.sheerkaDataProvider import SheerkaDataProvider, Event
@@ -528,6 +528,41 @@ def test_i_can_evaluate_a_concept_with_prop(expr, expected):
assert evaluated.metadata.is_evaluated assert evaluated.metadata.is_evaluated
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")
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.body == "do not resolve"
assert evaluated.metadata.is_evaluated
def test_i_can_evaluate_property_using_do_not_resolve():
sheerka = get_sheerka()
concept = Concept("foo").set_prop("a")
concept.cached_asts["a"] = DoNotResolve("do not resolve")
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.get_prop("a") == "do not resolve"
assert evaluated.metadata.is_evaluated
def test_original_value_is_overridden_when_using_do_no_resolve():
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")
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
assert evaluated.body == "do not resolve"
assert evaluated.get_prop("a") == "do not resolve"
assert evaluated.metadata.is_evaluated
def test_props_are_evaluated_before_body(): def test_props_are_evaluated_before_body():
sheerka = get_sheerka() sheerka = get_sheerka()
@@ -615,6 +650,8 @@ def test_i_can_evaluate_concept_when_properties_reference_others_concepts():
concept = Concept("foo", body="a").set_prop("a", "a").init_key() concept = Concept("foo", body="a").set_prop("a", "a").init_key()
evaluated = sheerka.evaluate_concept(get_context(sheerka), concept) evaluated = sheerka.evaluate_concept(get_context(sheerka), concept)
# first prop a is evaluated to concept_a
# then body is evaluated to prop a -> concept_a
assert evaluated.key == concept.key assert evaluated.key == concept.key
assert evaluated.body == concept_a assert evaluated.body == concept_a
@@ -647,6 +684,31 @@ def test_i_can_evaluate_concept_when_properties_reference_others_concepts_with_b
assert evaluated.body == 3 assert evaluated.body == 3
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)
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()
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")]
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[1] == "1"
def test_i_can_reference_sheerka(): def test_i_can_reference_sheerka():
sheerka = get_sheerka() sheerka = get_sheerka()
+28 -46
View File
@@ -413,14 +413,37 @@ def test_i_can_eval_a_mix_with_bnf_and_python():
assert res[0].body == 22 assert res[0].body == 22
def test_i_can_eval_a_mix_with_bnf_and_python_when_rule_name(): @pytest.mark.parametrize("desc, definitions", [
("Simple form", [
"def concept one as 1",
"def concept two as 2",
"def concept twenties from bnf 'twenty' (one|two)=unit as 20 + unit"
]),
("When twenty is a concept", [
"def concept one as 1",
"def concept two as 2",
"def concept twenty as 20",
"def concept twenties from bnf twenty (one|two)=unit as twenty + unit"
]),
("When digit is a concept", [
"def concept one as 1",
"def concept two as 2",
"def concept twenty as 20",
"def concept digit from bnf one|two",
"def concept twenties from bnf twenty digit as twenty + digit"
]),
])
def test_i_can_mix_concept_with_python_to_define_numbers(desc, definitions):
sheerka = get_sheerka() sheerka = get_sheerka()
sheerka.evaluate_user_input("def concept one as 1") for definition in definitions:
sheerka.evaluate_user_input("def concept two as 2") sheerka.evaluate_user_input(definition)
sheerka.evaluate_user_input("def concept twenties from bnf 'twenty' (one|two)=unit as 20 + unit")
assert sheerka.evaluate_user_input("eval twenty one")[0].body == 21 res = sheerka.evaluate_user_input("twenty one")
assert len(res) == 1
assert res[0].status
assert sheerka.isinstance(res[0].body, "twenties")
assert res[0].body.body == 21
res = sheerka.evaluate_user_input("twenty one + 1") res = sheerka.evaluate_user_input("twenty one + 1")
assert len(res) == 1 assert len(res) == 1
@@ -438,47 +461,6 @@ def test_i_can_eval_a_mix_with_bnf_and_python_when_rule_name():
assert res[0].body == 22 assert res[0].body == 22
def test_i_can_eval_a_mix_with_bnf_and_python_when_rule_name_2():
sheerka = get_sheerka()
sheerka.evaluate_user_input("def concept one as 1")
sheerka.evaluate_user_input("def concept two as 2")
sheerka.evaluate_user_input("def concept twenty as 20")
sheerka.evaluate_user_input("def concept twenties from bnf twenty (one | two)=unit as twenty + unit")
assert sheerka.evaluate_user_input("eval twenty one")[0].body == 21
res = sheerka.evaluate_user_input("twenty one + 1")
assert len(res) == 1
assert res[0].status
assert res[0].body == 22
res = sheerka.evaluate_user_input("twenty one + twenty two")
assert len(res) == 1
assert res[0].status
assert res[0].body == 43
res = sheerka.evaluate_user_input("twenty one + one")
assert len(res) == 1
assert res[0].status
assert res[0].body == 22
def test_i_can_eval_a_more_complicated_mix_with_bnf_and_python():
sheerka = get_sheerka()
sheerka.evaluate_user_input("def concept one as 1")
sheerka.evaluate_user_input("def concept two as 2")
sheerka.evaluate_user_input("def concept twenties from bnf 'twenty' (one|two)=unit as 20 + unit")
assert sheerka.evaluate_user_input("eval twenty one")[0].body == 21
res = sheerka.evaluate_user_input("twenty one + twenty two")
assert len(res) == 1
assert res[0].status
assert res[0].body == 43
def test_i_can_say_that_a_concept_isa_another_concept(): def test_i_can_say_that_a_concept_isa_another_concept():
sheerka = get_sheerka() sheerka = get_sheerka()
sheerka.evaluate_user_input("def concept foo") sheerka.evaluate_user_input("def concept foo")