Fixed #109 : Mix python and concept. List comprehension
Fixed #110 : SheerkaDebugManager: add list_debug_settings Fixed #111 : SheerkaDebugManager: Implement ListDebugLogger Fixed #112 : SyaNodeParser: rewrite this parser Fixed #113 : Sheerka.: Add enable_parser_caching to disable parsers caching Fixed #114 : SyaNodeParser : Implement fast cache to resolve unrecognized tokens requests Fixed #115 : BnfNodeParser : Implement fast cache to resolve unrecognized tokens requests Fixed #116 : SequenceNodeParser : Implement fast cache to resolve unrecognized tokens requests Fixed #117 : ResolveMultiplePluralAmbiguityEvaluator: Resolve Multiple plural ambiguity
This commit is contained in:
@@ -13,12 +13,14 @@ def concept deactivate debug as set_debug(False) auto_eval True
|
|||||||
def concept debug on as set_debug(True) auto_eval True
|
def concept debug on as set_debug(True) auto_eval True
|
||||||
def concept debug off as set_debug(False) auto_eval True
|
def concept debug off as set_debug(False) auto_eval True
|
||||||
|
|
||||||
def concept activate debug on x as debug_var(x) auto_eval True
|
def concept activate debug on x as set_debug_var(x) auto_eval True
|
||||||
def concept debug x as debug_var(x) auto_eval True
|
def concept activate debug on x id=y as set_debug_var(x, y) auto_eval True
|
||||||
|
def concept debug x as set_debug_var(x) auto_eval True
|
||||||
|
|
||||||
def concept debug var x as debug_var(variable=x) auto_eval True
|
def concept debug var x as set_debug_var(x) auto_eval True
|
||||||
def concept debug variable x as debug_var(variable=x) auto_eval True
|
def concept debug variable x as set_debug_var(variable=x) auto_eval True
|
||||||
def concept debug method x as debug_var(method=x) auto_eval True
|
def concept debug method x as set_debug_var(method=x) auto_eval True
|
||||||
|
def concept debug service x as set_debug_var(service=x) auto_eval True
|
||||||
|
|
||||||
def concept deactivate debug on x as debug_var(x, enabled=False) where x auto_eval True
|
def concept deactivate debug on x as debug_var(x, enabled=False) where x auto_eval True
|
||||||
|
|
||||||
|
|||||||
Vendored
+5
-1
@@ -11,6 +11,7 @@ class FastCache:
|
|||||||
self.cache = {}
|
self.cache = {}
|
||||||
self.lru = []
|
self.lru = []
|
||||||
self.default = default
|
self.default = default
|
||||||
|
self.calls = {}
|
||||||
|
|
||||||
def __contains__(self, item):
|
def __contains__(self, item):
|
||||||
return self.has(item)
|
return self.has(item)
|
||||||
@@ -24,13 +25,16 @@ class FastCache:
|
|||||||
|
|
||||||
self.cache[key] = value
|
self.cache[key] = value
|
||||||
self.lru.append(key)
|
self.lru.append(key)
|
||||||
|
self.calls[key] = 0
|
||||||
|
|
||||||
def has(self, key):
|
def has(self, key):
|
||||||
return key in self.cache
|
return key in self.cache
|
||||||
|
|
||||||
def get(self, key):
|
def get(self, key):
|
||||||
try:
|
try:
|
||||||
return self.cache[key]
|
res = self.cache[key]
|
||||||
|
self.calls[key] += 1
|
||||||
|
return res
|
||||||
except KeyError:
|
except KeyError:
|
||||||
if self.default:
|
if self.default:
|
||||||
value = self.default(key)
|
value = self.default(key)
|
||||||
|
|||||||
@@ -140,7 +140,10 @@ class ParserResultConcept(Concept):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
text = f"ParserResult(parser={self.parser}"
|
text = f"ParserResult(parser={self.parser}"
|
||||||
text += f", source='{self.source}')" if self.source else f", body='{self.value}')"
|
# text += f", source='{self.source}')" if self.source else f", body='{self.value}')"
|
||||||
|
from core.builtin_helpers import debug_nodes
|
||||||
|
value = debug_nodes(self.value) if isinstance(self.value, list) else self.value
|
||||||
|
text += f", source='{self.source}', '{value=}')"
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
@@ -252,6 +255,7 @@ class ListConcept(Concept):
|
|||||||
|
|
||||||
class FilteredConcept(Concept):
|
class FilteredConcept(Concept):
|
||||||
ALL_ATTRIBUTES = ["filtered", "iterable", "predicate", "reason"]
|
ALL_ATTRIBUTES = ["filtered", "iterable", "predicate", "reason"]
|
||||||
|
|
||||||
# To explain the reason why it's filtered, you can either
|
# To explain the reason why it's filtered, you can either
|
||||||
# provide the original list (iterable) and the predicate
|
# provide the original list (iterable) and the predicate
|
||||||
# provide the reason (It may be a CONDITION_FAILED concept)
|
# provide the reason (It may be a CONDITION_FAILED concept)
|
||||||
|
|||||||
+61
-11
@@ -5,15 +5,11 @@ from cache.Cache import Cache
|
|||||||
from core.ast_helpers import ast_to_props
|
from core.ast_helpers import ast_to_props
|
||||||
from core.builtin_concepts import BuiltinConcepts
|
from core.builtin_concepts import BuiltinConcepts
|
||||||
from core.concept import Concept, ConceptParts, DEFINITION_TYPE_BNF, concept_part_value
|
from core.concept import Concept, ConceptParts, DEFINITION_TYPE_BNF, concept_part_value
|
||||||
from core.global_symbols import NotInit, NotFound, INIT_AST_PARSERS, DEFAULT_EVALUATORS
|
from core.global_symbols import DEFAULT_EVALUATORS, INIT_AST_PARSERS, NotFound, NotInit
|
||||||
from core.rule import Rule
|
from core.rule import Rule
|
||||||
from core.sheerka.services.SheerkaExecute import ParserInput
|
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||||
from core.tokenizer import Tokenizer, TokenKind
|
from core.tokenizer import TokenKind, Tokenizer
|
||||||
from core.utils import as_bag
|
from core.utils import as_bag
|
||||||
from parsers.BaseExpressionParser import compile_disjunctions, AndNode
|
|
||||||
from parsers.BaseNodeParser import SourceCodeNode, ConceptNode, UnrecognizedTokensNode, SourceCodeWithConceptNode, \
|
|
||||||
RuleNode, LexerNode
|
|
||||||
from parsers.BaseParser import ParsingError
|
|
||||||
|
|
||||||
PARSE_STEPS = [BuiltinConcepts.BEFORE_PARSING, BuiltinConcepts.PARSING, BuiltinConcepts.AFTER_PARSING]
|
PARSE_STEPS = [BuiltinConcepts.BEFORE_PARSING, BuiltinConcepts.PARSING, BuiltinConcepts.AFTER_PARSING]
|
||||||
EVAL_ONLY_STEPS = [BuiltinConcepts.BEFORE_EVALUATION, BuiltinConcepts.EVALUATION, BuiltinConcepts.AFTER_EVALUATION]
|
EVAL_ONLY_STEPS = [BuiltinConcepts.BEFORE_EVALUATION, BuiltinConcepts.EVALUATION, BuiltinConcepts.AFTER_EVALUATION]
|
||||||
@@ -226,9 +222,9 @@ def resolve_ambiguity(context, concepts):
|
|||||||
remaining_concepts.extend(by_complexity[complexity])
|
remaining_concepts.extend(by_complexity[complexity])
|
||||||
else:
|
else:
|
||||||
for c in by_complexity[complexity]:
|
for c in by_complexity[complexity]:
|
||||||
|
from core.sheerka.services.SheerkaEvaluateConcept import EvaluationHints
|
||||||
evaluated = context.sheerka.evaluate_concept(context, c,
|
evaluated = context.sheerka.evaluate_concept(context, c,
|
||||||
eval_body=False,
|
hints=EvaluationHints(eval_body=False, expression_only=True),
|
||||||
validation_only=True,
|
|
||||||
metadata=[ConceptParts.PRE, ConceptParts.WHERE])
|
metadata=[ConceptParts.PRE, ConceptParts.WHERE])
|
||||||
if context.sheerka.is_success(evaluated) or evaluated.key == c.key:
|
if context.sheerka.is_success(evaluated) or evaluated.key == c.key:
|
||||||
remaining_concepts.append(c)
|
remaining_concepts.append(c)
|
||||||
@@ -255,6 +251,9 @@ def get_condition_complexity(context, condition):
|
|||||||
|
|
||||||
# # count the number of conjunctions
|
# # count the number of conjunctions
|
||||||
from parsers.LogicalOperatorParser import LogicalOperatorParser
|
from parsers.LogicalOperatorParser import LogicalOperatorParser
|
||||||
|
from parsers.BaseExpressionParser import compile_disjunctions
|
||||||
|
from parsers.BaseExpressionParser import AndNode
|
||||||
|
|
||||||
parser = LogicalOperatorParser()
|
parser = LogicalOperatorParser()
|
||||||
res = parser.parse(context, ParserInput(condition))
|
res = parser.parse(context, ParserInput(condition))
|
||||||
if not res.status:
|
if not res.status:
|
||||||
@@ -314,6 +313,9 @@ def only_parsers_results(context, return_values):
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from parsers.BaseNodeParser import UnrecognizedTokensNode
|
||||||
|
from parsers.BaseParser import ParsingError
|
||||||
|
|
||||||
if not isinstance(return_values, list):
|
if not isinstance(return_values, list):
|
||||||
return return_values
|
return return_values
|
||||||
|
|
||||||
@@ -479,6 +481,7 @@ def get_lexer_nodes(return_values, start, tokens):
|
|||||||
:return: list of list (list of concept node sequence)
|
:return: list of list (list of concept node sequence)
|
||||||
"""
|
"""
|
||||||
from evaluators.BaseEvaluator import BaseEvaluator
|
from evaluators.BaseEvaluator import BaseEvaluator
|
||||||
|
from parsers.BaseNodeParser import ConceptNode, LexerNode, RuleNode, SourceCodeNode
|
||||||
|
|
||||||
lexer_nodes = []
|
lexer_nodes = []
|
||||||
for ret_val in return_values:
|
for ret_val in return_values:
|
||||||
@@ -546,6 +549,7 @@ def get_lexer_nodes_using_positions(return_values, positions):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from evaluators.BaseEvaluator import BaseEvaluator
|
from evaluators.BaseEvaluator import BaseEvaluator
|
||||||
|
from parsers.BaseNodeParser import ConceptNode, LexerNode, RuleNode, SourceCodeNode
|
||||||
|
|
||||||
lexer_nodes = []
|
lexer_nodes = []
|
||||||
for ret_val, position in zip(return_values, positions):
|
for ret_val, position in zip(return_values, positions):
|
||||||
@@ -615,8 +619,8 @@ def ensure_evaluated(context, concept, eval_body=True, metadata=None):
|
|||||||
:param metadata:
|
:param metadata:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
from core.sheerka.services.SheerkaEvaluateConcept import SheerkaEvaluateConcept, EvaluationHints
|
||||||
if concept.get_hints().is_evaluated:
|
if concept.get_hints().is_evaluated:
|
||||||
from core.sheerka.services.SheerkaEvaluateConcept import SheerkaEvaluateConcept
|
|
||||||
return SheerkaEvaluateConcept.apply_ret(concept,
|
return SheerkaEvaluateConcept.apply_ret(concept,
|
||||||
eval_body or context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED))
|
eval_body or context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED))
|
||||||
|
|
||||||
@@ -628,7 +632,10 @@ def ensure_evaluated(context, concept, eval_body=True, metadata=None):
|
|||||||
(var_name not in concept.values() or concept.get_value(var_name) == NotInit):
|
(var_name not in concept.values() or concept.get_value(var_name) == NotInit):
|
||||||
return concept
|
return concept
|
||||||
|
|
||||||
evaluated = context.sheerka.evaluate_concept(context, concept, eval_body=eval_body, metadata=metadata)
|
evaluated = context.sheerka.evaluate_concept(context,
|
||||||
|
concept,
|
||||||
|
hints=EvaluationHints(eval_body=eval_body),
|
||||||
|
metadata=metadata)
|
||||||
return evaluated
|
return evaluated
|
||||||
|
|
||||||
|
|
||||||
@@ -663,6 +670,9 @@ def update_compiled(context, concept, errors, parsers=None):
|
|||||||
:param parsers: to customize the parsers to use
|
:param parsers: to customize the parsers to use
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from parsers.BaseNodeParser import ConceptNode, SourceCodeNode, SourceCodeWithConceptNode, UnrecognizedTokensNode
|
||||||
|
|
||||||
sheerka = context.sheerka
|
sheerka = context.sheerka
|
||||||
parsers = parsers or PARSERS
|
parsers = parsers or PARSERS
|
||||||
|
|
||||||
@@ -676,6 +686,15 @@ def update_compiled(context, concept, errors, parsers=None):
|
|||||||
if isinstance(v, Concept):
|
if isinstance(v, Concept):
|
||||||
_validate_concept(v)
|
_validate_concept(v)
|
||||||
|
|
||||||
|
elif isinstance(v, ConceptNode):
|
||||||
|
_validate_concept(v.concept)
|
||||||
|
c.get_compiled()[k] = v.concept
|
||||||
|
|
||||||
|
elif isinstance(v, SourceCodeNode):
|
||||||
|
if not v.return_value:
|
||||||
|
raise NotImplementedError("SourceCodeNode")
|
||||||
|
c.get_compiled()[k] = [v.return_value]
|
||||||
|
|
||||||
elif isinstance(v, SourceCodeWithConceptNode):
|
elif isinstance(v, SourceCodeWithConceptNode):
|
||||||
if v.return_value:
|
if v.return_value:
|
||||||
res = v.return_value
|
res = v.return_value
|
||||||
@@ -939,7 +958,8 @@ def get_possible_variables_from_concept(context, concept):
|
|||||||
return set()
|
return set()
|
||||||
|
|
||||||
concept_name = [t.str_value for t in Tokenizer(concept.name, yield_eof=False)]
|
concept_name = [t.str_value for t in Tokenizer(concept.name, yield_eof=False)]
|
||||||
names = [v_value or v_name for v_name, v_value in concept.get_metadata().variables if v_name in concept_name]
|
names = [v_value.strip() or v_name for v_name, v_value in concept.get_metadata().variables if
|
||||||
|
v_name in concept_name]
|
||||||
possible_vars = filter(lambda x: context.sheerka.is_not_a_concept_name(x), names)
|
possible_vars = filter(lambda x: context.sheerka.is_not_a_concept_name(x), names)
|
||||||
to_keep = set()
|
to_keep = set()
|
||||||
for var in possible_vars:
|
for var in possible_vars:
|
||||||
@@ -961,6 +981,36 @@ def is_only_successful(sheerka, return_value):
|
|||||||
sheerka.isinstance(return_value.body, BuiltinConcepts.ONLY_SUCCESSFUL)
|
sheerka.isinstance(return_value.body, BuiltinConcepts.ONLY_SUCCESSFUL)
|
||||||
|
|
||||||
|
|
||||||
|
def debug_nodes(nodes):
|
||||||
|
from parsers.BaseNodeParser import UnrecognizedTokensNode
|
||||||
|
|
||||||
|
res = []
|
||||||
|
for node in nodes:
|
||||||
|
if isinstance(node, UnrecognizedTokensNode):
|
||||||
|
res.append(node.source)
|
||||||
|
elif hasattr(node, "get_concept"):
|
||||||
|
concept = node.get_concept()
|
||||||
|
res.append(concept)
|
||||||
|
else:
|
||||||
|
res.append(node)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def get_new_variables_definitions(concept):
|
||||||
|
"""
|
||||||
|
Return a new set of variable definition, where the default value are initialized with what was compiled
|
||||||
|
"""
|
||||||
|
new_variables = []
|
||||||
|
for var_name, var_default_value in concept.get_metadata().variables:
|
||||||
|
if var_name in concept.get_metadata().parameters and hasattr(concept.get_compiled()[var_name], "source"):
|
||||||
|
new_variables.append((var_name, concept.get_compiled()[var_name].source))
|
||||||
|
else:
|
||||||
|
new_variables.append((var_name, var_default_value))
|
||||||
|
|
||||||
|
return new_variables
|
||||||
|
|
||||||
|
|
||||||
class CreateObjectIdentifiers:
|
class CreateObjectIdentifiers:
|
||||||
"""
|
"""
|
||||||
Class that creates unique identifiers for Concept or Rule objects
|
Class that creates unique identifiers for Concept or Rule objects
|
||||||
|
|||||||
+1
-1
@@ -44,7 +44,7 @@ def concept_part_value(c):
|
|||||||
class ConceptHints:
|
class ConceptHints:
|
||||||
is_evaluated: bool = False # True is the concept is evaluated by sheerka.eval_concept()
|
is_evaluated: bool = False # True is the concept is evaluated by sheerka.eval_concept()
|
||||||
need_validation: bool = False # True if the properties of the concept need to be validated
|
need_validation: bool = False # True if the properties of the concept need to be validated
|
||||||
recognized_by: str = None # recognized by its name, by its id or its key
|
recognized_by: str = None # RECOGNIZED_BY_ID, RECOGNIZED_BY_NAME, RECOGNIZED_BY_KEY (from Sheerka.py)
|
||||||
use_copy: bool = False # Do not modify, make a copy first
|
use_copy: bool = False # Do not modify, make a copy first
|
||||||
is_instance: bool = True # False if we think we recognize the definition of a concept, not its usage
|
is_instance: bool = True # False if we think we recognize the definition of a concept, not its usage
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from core.builtin_concepts import BuiltinConcepts, ParserResultConcept
|
|||||||
from core.concept import Concept, get_concept_attrs
|
from core.concept import Concept, get_concept_attrs
|
||||||
from core.global_symbols import EVENT_CONTEXT_DISPOSED, NO_MATCH
|
from core.global_symbols import EVENT_CONTEXT_DISPOSED, NO_MATCH
|
||||||
from core.sheerka.services.SheerkaMemory import SheerkaMemory
|
from core.sheerka.services.SheerkaMemory import SheerkaMemory
|
||||||
from core.utils import CONSOLE_COLORS_MAP as CCM, CONSOLE_COLUMNS
|
from core.utils import CONSOLE_COLUMNS
|
||||||
from sdp.sheerkaDataProvider import Event
|
from sdp.sheerkaDataProvider import Event
|
||||||
|
|
||||||
pp = pprint.PrettyPrinter(indent=2, width=CONSOLE_COLUMNS)
|
pp = pprint.PrettyPrinter(indent=2, width=CONSOLE_COLUMNS)
|
||||||
@@ -334,37 +334,8 @@ class ExecutionContext:
|
|||||||
to_str = self.return_value_to_str(r)
|
to_str = self.return_value_to_str(r)
|
||||||
self._logger.debug(f"[{self._id:2}]" + self._tab + "-> " + to_str)
|
self._logger.debug(f"[{self._id:2}]" + self._tab + "-> " + to_str)
|
||||||
|
|
||||||
def get_debugger(self, who, method_name, new_debug_id=True):
|
def get_debugger(self, who, method_name, new_debug_id=True, forced_debug_id=None):
|
||||||
return self.sheerka.get_debugger(self, who, method_name, new_debug_id)
|
return self.sheerka.get_debugger(self, who, method_name, new_debug_id, forced_debug_id)
|
||||||
|
|
||||||
# TODO: TO REMOVE
|
|
||||||
def debug(self, who, method_name, variable_name, text, is_error=False):
|
|
||||||
activated = self.sheerka.debug_activated_for(who)
|
|
||||||
if activated:
|
|
||||||
str_text = pp.pformat(text)
|
|
||||||
color = 'red' if is_error else 'green'
|
|
||||||
if "\n" not in str(str_text):
|
|
||||||
self.sheerka.debug(
|
|
||||||
f"[{self._id:3}] {CCM[color]}{who}.{method_name}.{variable_name}: {CCM['reset']}{str_text}")
|
|
||||||
else:
|
|
||||||
self.sheerka.debug(f"[{self._id:3}] {CCM[color]}{who}.{method_name}.{variable_name}: {CCM['reset']}")
|
|
||||||
self.sheerka.debug(str_text)
|
|
||||||
|
|
||||||
# TODO: TO REMOVE
|
|
||||||
def debug_entering(self, who, method_name, **kwargs):
|
|
||||||
if self.sheerka.debug_activated_for(who):
|
|
||||||
str_text = pp.pformat(kwargs)
|
|
||||||
if "\n" not in str(str_text):
|
|
||||||
self.sheerka.debug(
|
|
||||||
f"[{self._id:3}] {CCM['blue']}Entering {who}.{method_name} with {CCM['reset']}{str_text}")
|
|
||||||
else:
|
|
||||||
self.sheerka.debug(f"[{self._id:3}] {CCM['blue']}Entering {who}.{method_name}:{CCM['reset']}")
|
|
||||||
self.sheerka.debug(f"[{self._id:3}] {str_text}")
|
|
||||||
|
|
||||||
# TODO: TO REMOVE
|
|
||||||
def debug_log(self, who, text):
|
|
||||||
if self.sheerka.debug_activated_for(who):
|
|
||||||
self.sheerka.debug(f"[{self._id:3}] {CCM['blue']}{text}{CCM['reset']}")
|
|
||||||
|
|
||||||
def get_parent(self):
|
def get_parent(self):
|
||||||
return self._parent
|
return self._parent
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import core.utils
|
|||||||
from cache.Cache import Cache
|
from cache.Cache import Cache
|
||||||
from cache.IncCache import IncCache
|
from cache.IncCache import IncCache
|
||||||
from core.builtin_concepts import ErrorConcept, ReturnValueConcept, UnknownConcept
|
from core.builtin_concepts import ErrorConcept, ReturnValueConcept, UnknownConcept
|
||||||
from core.builtin_concepts_ids import BuiltinErrors, BuiltinConcepts
|
from core.builtin_concepts_ids import BuiltinConcepts, BuiltinErrors
|
||||||
from core.concept import Concept, ConceptParts, get_concept_attrs
|
from core.concept import Concept, ConceptParts, get_concept_attrs
|
||||||
from core.global_symbols import EVENT_USER_INPUT_EVALUATED, NotInit, NotFound, ErrorObj, EVENT_ONTOLOGY_CREATED
|
from core.global_symbols import EVENT_ONTOLOGY_CREATED, EVENT_USER_INPUT_EVALUATED, ErrorObj, NotFound, NotInit
|
||||||
from core.profiling import profile
|
from core.profiling import profile
|
||||||
from core.sheerka.ExecutionContext import ExecutionContext
|
from core.sheerka.ExecutionContext import ExecutionContext
|
||||||
from core.sheerka.SheerkaOntologyManager import SheerkaOntologyManager, OntologyAlreadyExists
|
from core.sheerka.SheerkaOntologyManager import OntologyAlreadyExists, SheerkaOntologyManager
|
||||||
from core.sheerka_logger import console_handler
|
from core.sheerka_logger import console_handler
|
||||||
from core.tokenizer import Token, TokenKind
|
from core.tokenizer import Token, TokenKind
|
||||||
from printer.SheerkaPrinter import SheerkaPrinter
|
from printer.SheerkaPrinter import SheerkaPrinter
|
||||||
@@ -119,6 +119,7 @@ class Sheerka(Concept):
|
|||||||
self.enable_process_return_values = True
|
self.enable_process_return_values = True
|
||||||
self.enable_process_rules = True
|
self.enable_process_rules = True
|
||||||
self.enable_commands_backup = True
|
self.enable_commands_backup = True
|
||||||
|
self.enable_parser_caching = True
|
||||||
|
|
||||||
self.methods_with_context = {"test_using_context"} # only the names, the method is defined in sheerka_methods
|
self.methods_with_context = {"test_using_context"} # only the names, the method is defined in sheerka_methods
|
||||||
self.pipe_functions = set()
|
self.pipe_functions = set()
|
||||||
@@ -193,6 +194,7 @@ class Sheerka(Concept):
|
|||||||
self.enable_process_return_values)
|
self.enable_process_return_values)
|
||||||
self.enable_process_rules = kwargs.get("enable_process_rules", self.enable_process_rules)
|
self.enable_process_rules = kwargs.get("enable_process_rules", self.enable_process_rules)
|
||||||
self.enable_commands_backup = kwargs.get("enable_commands_backup", self.enable_commands_backup)
|
self.enable_commands_backup = kwargs.get("enable_commands_backup", self.enable_commands_backup)
|
||||||
|
self.enable_parser_caching = kwargs.get("enable_parser_caching", self.enable_parser_caching)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.during_initialisation = True
|
self.during_initialisation = True
|
||||||
@@ -791,7 +793,7 @@ class Sheerka(Concept):
|
|||||||
Browse obj, looking for error
|
Browse obj, looking for error
|
||||||
:param context:
|
:param context:
|
||||||
:param obj:
|
:param obj:
|
||||||
:param kwargs: if defined, specialize the error
|
:param kwargs: if defined, specialize the error (example __type="PythonEvalError")
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -834,6 +836,19 @@ class Sheerka(Concept):
|
|||||||
errors = self.get_errors(context, obj, **kwargs)
|
errors = self.get_errors(context, obj, **kwargs)
|
||||||
return len(errors) > 0
|
return len(errors) > 0
|
||||||
|
|
||||||
|
def get_error_cause(self, obj):
|
||||||
|
if self.isinstance(obj, BuiltinConcepts.NOT_FOR_ME):
|
||||||
|
res = obj.reason
|
||||||
|
elif self.isinstance(obj, BuiltinConcepts.ERROR):
|
||||||
|
res = obj.body
|
||||||
|
else:
|
||||||
|
res = None
|
||||||
|
|
||||||
|
if isinstance(res, list) and len(res) == 1:
|
||||||
|
return res[0]
|
||||||
|
else:
|
||||||
|
return res
|
||||||
|
|
||||||
def get_evaluator_name(self, name):
|
def get_evaluator_name(self, name):
|
||||||
if self.evaluators_prefix is None:
|
if self.evaluators_prefix is None:
|
||||||
base_evaluator_class = core.utils.get_class("evaluators.BaseEvaluator.BaseEvaluator")
|
base_evaluator_class = core.utils.get_class("evaluators.BaseEvaluator.BaseEvaluator")
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import time
|
|||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
from core.builtin_concepts_ids import BuiltinConcepts, BuiltinContainers
|
from core.builtin_concepts_ids import BuiltinConcepts, BuiltinContainers
|
||||||
from core.builtin_helpers import ensure_concept_or_rule, ensure_concept
|
from core.builtin_helpers import ensure_concept_or_rule
|
||||||
from core.concept import Concept
|
from core.concept import Concept
|
||||||
from core.global_symbols import SHEERKA_BACKUP_FOLDER
|
from core.global_symbols import SHEERKA_BACKUP_FOLDER
|
||||||
from core.sheerka.services.SheerkaExecute import SheerkaExecute
|
from core.sheerka.services.SheerkaExecute import SheerkaExecute
|
||||||
@@ -40,6 +40,7 @@ class SheerkaAdmin(BaseService):
|
|||||||
self.sheerka.bind_service_method(self.NAME, self.in_memory, False)
|
self.sheerka.bind_service_method(self.NAME, self.in_memory, False)
|
||||||
self.sheerka.bind_service_method(self.NAME, self.admin_history, False, as_name="history")
|
self.sheerka.bind_service_method(self.NAME, self.admin_history, False, as_name="history")
|
||||||
self.sheerka.bind_service_method(self.NAME, self.sdp, False)
|
self.sheerka.bind_service_method(self.NAME, self.sdp, False)
|
||||||
|
self.sheerka.bind_service_method(self.NAME, self.set_sheerka, True)
|
||||||
|
|
||||||
def caches_names(self):
|
def caches_names(self):
|
||||||
"""
|
"""
|
||||||
@@ -284,3 +285,13 @@ class SheerkaAdmin(BaseService):
|
|||||||
def admin_history(self, depth=10, start=0):
|
def admin_history(self, depth=10, start=0):
|
||||||
history = self.sheerka.services[SheerkaHistoryManager.NAME].history(depth, start)
|
history = self.sheerka.services[SheerkaHistoryManager.NAME].history(depth, start)
|
||||||
return self.sheerka.new(BuiltinConcepts.TO_LIST, body=history)
|
return self.sheerka.new(BuiltinConcepts.TO_LIST, body=history)
|
||||||
|
|
||||||
|
def set_sheerka(self, context, key, value, service=None):
|
||||||
|
"""
|
||||||
|
@param context:
|
||||||
|
@param key:
|
||||||
|
@param value:
|
||||||
|
@param service:
|
||||||
|
@return:
|
||||||
|
"""
|
||||||
|
return self.sheerka.record_var(context, service or self.sheerka.name, key, value)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from dataclasses import dataclass
|
|||||||
from core.builtin_concepts import BuiltinConcepts
|
from core.builtin_concepts import BuiltinConcepts
|
||||||
from core.builtin_helpers import evaluate_expression
|
from core.builtin_helpers import evaluate_expression
|
||||||
from core.concept import Concept
|
from core.concept import Concept
|
||||||
from core.global_symbols import NotInit, NotFound
|
from core.global_symbols import NotFound, NotInit
|
||||||
from core.sheerka.ExecutionContext import ExecutionContext
|
from core.sheerka.ExecutionContext import ExecutionContext
|
||||||
from core.sheerka.services.sheerka_service import BaseService
|
from core.sheerka.services.sheerka_service import BaseService
|
||||||
from core.utils import CONSOLE_COLORS_MAP as CCM, CONSOLE_COLUMNS, PRIMITIVES_TYPES
|
from core.utils import CONSOLE_COLORS_MAP as CCM, CONSOLE_COLUMNS, PRIMITIVES_TYPES
|
||||||
@@ -100,13 +100,22 @@ class BaseDebugLogger:
|
|||||||
BaseDebugLogger.ids[hint] = 0
|
BaseDebugLogger.ids[hint] = 0
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def __init__(self, debug_manager, context, who, method_name, debug_id):
|
def __init__(self, keep_track, debug_manager, context, service_name, method_name, debug_id):
|
||||||
pass
|
self.debug_manager = debug_manager
|
||||||
|
self.service_name = service_name
|
||||||
|
self.method_name = method_name
|
||||||
|
self.context = context
|
||||||
|
self.debug_id = debug_id
|
||||||
|
|
||||||
|
self.keep_track = keep_track # Does debug_manager need to keep track of this logger ?
|
||||||
|
|
||||||
def debug_entering(self, **kwargs):
|
def debug_entering(self, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def debug_log(self, text, is_error=False):
|
def debug_leaving(self, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def debug_log(self, text, is_error=False, args=None):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def debug_var(self, name, value, is_error=False, hint=None):
|
def debug_var(self, name, value, is_error=False, hint=None):
|
||||||
@@ -122,7 +131,36 @@ class BaseDebugLogger:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def get_enabled_vars(self):
|
def get_enabled_vars(self):
|
||||||
pass
|
"""
|
||||||
|
Returns the list of all enabled variables for this console debugger
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return self.debug_manager.get_enabled_items("vars",
|
||||||
|
self.context,
|
||||||
|
self.service_name,
|
||||||
|
self.method_name,
|
||||||
|
self.debug_id)
|
||||||
|
|
||||||
|
def should_i_log_var(self, name, is_error=False):
|
||||||
|
return is_error or self.debug_manager.compute_debug_var(self.context,
|
||||||
|
self.service_name,
|
||||||
|
self.method_name,
|
||||||
|
name,
|
||||||
|
self.debug_id)
|
||||||
|
|
||||||
|
def should_i_log_rule(self, rule_id, is_error=False):
|
||||||
|
return is_error or self.debug_manager.compute_debug_rule(self.context,
|
||||||
|
self.service_name,
|
||||||
|
self.method_name,
|
||||||
|
rule_id,
|
||||||
|
self.debug_id)
|
||||||
|
|
||||||
|
def should_i_log_concept(self, concept_id, is_error=False):
|
||||||
|
return is_error or self.debug_manager.compute_debug_concept(self.context,
|
||||||
|
self.service_name,
|
||||||
|
self.method_name,
|
||||||
|
concept_id,
|
||||||
|
self.debug_id)
|
||||||
|
|
||||||
|
|
||||||
class NullDebugLogger(BaseDebugLogger):
|
class NullDebugLogger(BaseDebugLogger):
|
||||||
@@ -139,12 +177,7 @@ class NullDebugLogger(BaseDebugLogger):
|
|||||||
class ConsoleDebugLogger(BaseDebugLogger):
|
class ConsoleDebugLogger(BaseDebugLogger):
|
||||||
|
|
||||||
def __init__(self, debug_manager, context, service_name, method_name, debug_id):
|
def __init__(self, debug_manager, context, service_name, method_name, debug_id):
|
||||||
BaseDebugLogger.__init__(self, debug_manager, context, service_name, method_name, debug_id)
|
BaseDebugLogger.__init__(self, False, debug_manager, context, service_name, method_name, debug_id)
|
||||||
self.debug_manager = debug_manager
|
|
||||||
self.service_name = service_name
|
|
||||||
self.method_name = method_name
|
|
||||||
self.context = context
|
|
||||||
self.debug_id = debug_id
|
|
||||||
self.is_highlighted = ""
|
self.is_highlighted = ""
|
||||||
|
|
||||||
def is_enabled(self):
|
def is_enabled(self):
|
||||||
@@ -154,17 +187,6 @@ class ConsoleDebugLogger(BaseDebugLogger):
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_enabled_vars(self):
|
|
||||||
"""
|
|
||||||
Returns the list of all enabled variables for this console debugger
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
return self.debug_manager.get_enabled_items("vars",
|
|
||||||
self.context,
|
|
||||||
self.service_name,
|
|
||||||
self.method_name,
|
|
||||||
self.debug_id)
|
|
||||||
|
|
||||||
def debug_entering(self, **kwargs):
|
def debug_entering(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Log that we start debugging a method (for a specified service and context)
|
Log that we start debugging a method (for a specified service and context)
|
||||||
@@ -181,11 +203,31 @@ class ConsoleDebugLogger(BaseDebugLogger):
|
|||||||
self.debug_manager.debug(self.prefix() + str_text)
|
self.debug_manager.debug(self.prefix() + str_text)
|
||||||
self.debug_manager.debug(self.prefix() + str_vars)
|
self.debug_manager.debug(self.prefix() + str_vars)
|
||||||
|
|
||||||
def debug_log(self, text, is_error=False):
|
def debug_leaving(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Log that we start debugging a method (for a specified service and context)
|
||||||
|
:param kwargs:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super().debug_leaving(**kwargs)
|
||||||
|
|
||||||
|
if not kwargs:
|
||||||
|
return
|
||||||
|
|
||||||
|
str_text = f"{CCM['blue']}Leaving {self.service_name}.{self.method_name} with {CCM['reset']}"
|
||||||
|
str_vars = pp.pformat(kwargs)
|
||||||
|
if "\n" not in str(str_vars):
|
||||||
|
self.debug_manager.debug(self.prefix() + str_text + str_vars)
|
||||||
|
else:
|
||||||
|
self.debug_manager.debug(self.prefix() + str_text)
|
||||||
|
self.debug_manager.debug(self.prefix() + str_vars)
|
||||||
|
|
||||||
|
def debug_log(self, text, is_error=False, args=None):
|
||||||
"""
|
"""
|
||||||
Prints a debug information (not related to a specific variable, concept or rule)
|
Prints a debug information (not related to a specific variable, concept or rule)
|
||||||
:param text:
|
:param text:
|
||||||
:param is_error:
|
:param is_error:
|
||||||
|
:param args:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
color = 'red' if is_error else 'blue'
|
color = 'red' if is_error else 'blue'
|
||||||
@@ -268,8 +310,153 @@ class ConsoleDebugLogger(BaseDebugLogger):
|
|||||||
return f"[{self.context.id:2}][{self.debug_id:2}] {self.is_highlighted}"
|
return f"[{self.context.id:2}][{self.debug_id:2}] {self.is_highlighted}"
|
||||||
|
|
||||||
|
|
||||||
|
class ListDebugLogger(BaseDebugLogger):
|
||||||
|
ITEM_TYPE_ENTERING = "entering"
|
||||||
|
ITEM_TYPE_LEAVING = "leaving"
|
||||||
|
ITEM_TYPE_LOG = "log"
|
||||||
|
ITEM_TYPE_VAR = "var"
|
||||||
|
ITEM_TYPE_RULE = "rule"
|
||||||
|
ITEM_TYPE_CONCEPT = "concept"
|
||||||
|
|
||||||
|
class DebugItem:
|
||||||
|
def __init__(self, item_type, text, is_error=False, args=None):
|
||||||
|
self.type = item_type
|
||||||
|
self.text = text
|
||||||
|
self.is_error = is_error
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return self.text
|
||||||
|
|
||||||
|
def __init__(self, debug_manager, context, service_name, method_name, debug_id):
|
||||||
|
BaseDebugLogger.__init__(self, True, debug_manager, context, service_name, method_name, debug_id)
|
||||||
|
self.items = []
|
||||||
|
|
||||||
|
def is_enabled(self):
|
||||||
|
"""
|
||||||
|
True if the debug is activated for the current service, method and context
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def debug_entering(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Log that we start debugging a method (for a specified service and context)
|
||||||
|
:param kwargs:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
text = f"Entering {self.service_name}.{self.method_name}"
|
||||||
|
self.items.append(ListDebugLogger.DebugItem(ListDebugLogger.ITEM_TYPE_ENTERING, text, False, kwargs))
|
||||||
|
|
||||||
|
def debug_leaving(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Log that we start debugging a method (for a specified service and context)
|
||||||
|
:param kwargs:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super().debug_leaving(**kwargs)
|
||||||
|
|
||||||
|
if not kwargs:
|
||||||
|
return
|
||||||
|
|
||||||
|
text = f"Leaving {self.service_name}.{self.method_name}"
|
||||||
|
self.items.append(ListDebugLogger.DebugItem(ListDebugLogger.ITEM_TYPE_LEAVING, text, False, kwargs))
|
||||||
|
|
||||||
|
def debug_log(self, text, is_error=False, args=None):
|
||||||
|
"""
|
||||||
|
Prints a debug information (not related to a specific variable, concept or rule)
|
||||||
|
:param text:
|
||||||
|
:param is_error:
|
||||||
|
:param args:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.items.append(ListDebugLogger.DebugItem(ListDebugLogger.ITEM_TYPE_LOG, text, is_error, args))
|
||||||
|
|
||||||
|
def debug_var(self, name, value, is_error=False, hint=None):
|
||||||
|
"""
|
||||||
|
Prints the value of a variable
|
||||||
|
:param name:
|
||||||
|
:param value:
|
||||||
|
:param is_error:
|
||||||
|
:param hint:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if not self.should_i_log_var(name, is_error):
|
||||||
|
return
|
||||||
|
|
||||||
|
text = name + hint if hint else name
|
||||||
|
self.items.append(ListDebugLogger.DebugItem(ListDebugLogger.ITEM_TYPE_VAR, text, is_error, value))
|
||||||
|
|
||||||
|
def debug_rule(self, rule, results):
|
||||||
|
"""
|
||||||
|
Prints debug information related to a specific rule id
|
||||||
|
:param rule:
|
||||||
|
:param results:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if not self.should_i_log_rule(rule.id):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.items.append(ListDebugLogger.DebugItem(ListDebugLogger.ITEM_TYPE_RULE, rule.id, False, results))
|
||||||
|
|
||||||
|
def debug_concept(self, concept, text=None, **kwargs):
|
||||||
|
"""
|
||||||
|
Prints debug information related to a specific concept
|
||||||
|
:param concept:
|
||||||
|
:param text:
|
||||||
|
:param kwargs:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if not self.debug_manager.compute_debug_concept(self.context,
|
||||||
|
self.service_name,
|
||||||
|
self.method_name,
|
||||||
|
concept.id,
|
||||||
|
self.debug_id):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.should_i_log_concept(concept.id):
|
||||||
|
return
|
||||||
|
|
||||||
|
concept_id = f"{concept.id}{text}" if text else f"{concept.id}"
|
||||||
|
self.items.append(ListDebugLogger.DebugItem(ListDebugLogger.ITEM_TYPE_CONCEPT, concept_id, False, kwargs))
|
||||||
|
|
||||||
|
|
||||||
|
class TeeDebugLogger(BaseDebugLogger):
|
||||||
|
def __init__(self, debug_manager, context, service_name, method_name, debug_id, loggers):
|
||||||
|
BaseDebugLogger.__init__(self, False, debug_manager, context, service_name, method_name, debug_id)
|
||||||
|
self.loggers = loggers
|
||||||
|
|
||||||
|
def is_enabled(self):
|
||||||
|
"""
|
||||||
|
True if the debug is activated for the current service, method and context
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def debug_entering(self, **kwargs):
|
||||||
|
for logger in self.loggers:
|
||||||
|
logger.debug_entering(kwargs)
|
||||||
|
|
||||||
|
def debug_log(self, text, is_error=False, args=None):
|
||||||
|
for logger in self.loggers:
|
||||||
|
logger.debug_log(text, is_error, args=None)
|
||||||
|
|
||||||
|
def debug_var(self, name, value, is_error=False, hint=None):
|
||||||
|
for logger in self.loggers:
|
||||||
|
logger.debug_var(name, value, is_error, hint)
|
||||||
|
|
||||||
|
def debug_rule(self, rule, results):
|
||||||
|
for logger in self.loggers:
|
||||||
|
logger.debug_rule(rule, results)
|
||||||
|
|
||||||
|
def debug_concept(self, concept, text=None, **kwargs):
|
||||||
|
for logger in self.loggers:
|
||||||
|
logger.debug_concept(concept, text, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class DebugItem:
|
class DebugItem:
|
||||||
|
debug_type: str
|
||||||
item: str
|
item: str
|
||||||
service_name: str
|
service_name: str
|
||||||
method_name: str
|
method_name: str
|
||||||
@@ -280,6 +467,16 @@ class DebugItem:
|
|||||||
|
|
||||||
enabled: bool
|
enabled: bool
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
text = f"type={self.debug_type}"
|
||||||
|
text += f", setting={self.service_name or '*'}.{self.method_name or '*'}.{self.item or '*'}"
|
||||||
|
text += f", context_id={self.context_id}"
|
||||||
|
text += f", debug_id={self.debug_id}"
|
||||||
|
text += f", context_children={self.context_children}"
|
||||||
|
text += f", debug_children={self.debug_children}"
|
||||||
|
text += f" (enabled={self.enabled})"
|
||||||
|
return f"DebugItem({text})"
|
||||||
|
|
||||||
|
|
||||||
class SheerkaDebugManager(BaseService):
|
class SheerkaDebugManager(BaseService):
|
||||||
NAME = "Debug"
|
NAME = "Debug"
|
||||||
@@ -302,7 +499,10 @@ class SheerkaDebugManager(BaseService):
|
|||||||
self.registered_vars = [] # list of all variables that can be debugged
|
self.registered_vars = [] # list of all variables that can be debugged
|
||||||
self.registered_rules = [] # list of all rules that can be debugged
|
self.registered_rules = [] # list of all rules that can be debugged
|
||||||
self.registered_concepts = [] # list of all concept that can be debugged
|
self.registered_concepts = [] # list of all concept that can be debugged
|
||||||
|
self.debug_logger_definition = ConsoleDebugLogger # logger to use
|
||||||
|
self.instantiated_loggers = {}
|
||||||
|
|
||||||
|
# variable that needs to be reset on restart
|
||||||
self.state_vars = [
|
self.state_vars = [
|
||||||
"activated",
|
"activated",
|
||||||
"explicit", # to remove ?
|
"explicit", # to remove ?
|
||||||
@@ -323,25 +523,33 @@ class SheerkaDebugManager(BaseService):
|
|||||||
self.sheerka.bind_service_method(self.NAME, self.set_debug, True)
|
self.sheerka.bind_service_method(self.NAME, self.set_debug, True)
|
||||||
self.sheerka.bind_service_method(self.NAME, self.inspect, False)
|
self.sheerka.bind_service_method(self.NAME, self.inspect, False)
|
||||||
self.sheerka.bind_service_method(self.NAME, self.get_value, False)
|
self.sheerka.bind_service_method(self.NAME, self.get_value, False)
|
||||||
self.sheerka.bind_service_method(self.NAME, self.get_debugger, False)
|
|
||||||
self.sheerka.bind_service_method(self.NAME, self.reset_debug, False)
|
self.sheerka.bind_service_method(self.NAME, self.reset_debug, False)
|
||||||
self.sheerka.bind_service_method(self.NAME, self.set_debug_var, True)
|
self.sheerka.bind_service_method(self.NAME, self.set_debug_var, True)
|
||||||
self.sheerka.bind_service_method(self.NAME, self.set_debug_rule, True)
|
self.sheerka.bind_service_method(self.NAME, self.set_debug_rule, True)
|
||||||
self.sheerka.bind_service_method(self.NAME, self.set_debug_concept, True)
|
self.sheerka.bind_service_method(self.NAME, self.set_debug_concept, True)
|
||||||
self.sheerka.bind_service_method(self.NAME, self.list_debug_vars, True)
|
self.sheerka.bind_service_method(self.NAME, self.list_debug_vars, False)
|
||||||
self.sheerka.bind_service_method(self.NAME, self.list_debug_rules, True)
|
self.sheerka.bind_service_method(self.NAME, self.list_debug_rules, False)
|
||||||
self.sheerka.bind_service_method(self.NAME, self.list_debug_concepts, True)
|
self.sheerka.bind_service_method(self.NAME, self.list_debug_concepts, False)
|
||||||
|
self.sheerka.bind_service_method(self.NAME, self.list_debug_settings, False)
|
||||||
|
|
||||||
self.sheerka.bind_service_method(self.NAME, self.register_debug_vars, True, visible=False)
|
self.sheerka.bind_service_method(self.NAME, self.register_debug_vars, True, visible=False)
|
||||||
self.sheerka.bind_service_method(self.NAME, self.register_debug_rules, True, visible=False)
|
self.sheerka.bind_service_method(self.NAME, self.register_debug_rules, True, visible=False)
|
||||||
self.sheerka.bind_service_method(self.NAME, self.register_debug_concepts, True, visible=False)
|
self.sheerka.bind_service_method(self.NAME, self.register_debug_concepts, True, visible=False)
|
||||||
|
|
||||||
|
self.sheerka.bind_service_method(self.NAME, self.get_debugger, False, visible=False)
|
||||||
|
self.sheerka.bind_service_method(self.NAME, self.get_debugger_logs, False, visible=False)
|
||||||
|
self.sheerka.bind_service_method(self.NAME, self.set_debug_logger_definition, True, visible=False)
|
||||||
|
|
||||||
# self.sheerka.bind_service_method(self.NAME,self.get_debug_settings, False, as_name="debug_settings")
|
# self.sheerka.bind_service_method(self.NAME,self.get_debug_settings, False, as_name="debug_settings")
|
||||||
# register what can be registered
|
# register what can be registered
|
||||||
from parsers.BnfNodeParser import BnfNodeParser
|
from parsers.BnfNodeParser import BnfNodeParser
|
||||||
from evaluators.DefConceptEvaluator import DefConceptEvaluator
|
from evaluators.DefConceptEvaluator import DefConceptEvaluator
|
||||||
from evaluators.PythonEvaluator import PythonEvaluator
|
from evaluators.PythonEvaluator import PythonEvaluator
|
||||||
from parsers.SyaNodeParser import SyaNodeParser
|
from parsers.SyaNodeParser import SyaNodeParser
|
||||||
|
from parsers.SequenceNodeParser import SequenceNodeParser
|
||||||
self.register_debug_vars(BnfNodeParser.NAME, "parse", "result")
|
self.register_debug_vars(BnfNodeParser.NAME, "parse", "result")
|
||||||
|
self.register_debug_vars(BnfNodeParser.NAME, "parse", "stats")
|
||||||
|
self.register_debug_vars(SequenceNodeParser.NAME, "parse", "stats")
|
||||||
self.register_debug_concepts(BnfNodeParser.NAME, "parse", "*")
|
self.register_debug_concepts(BnfNodeParser.NAME, "parse", "*")
|
||||||
self.register_debug_vars(DefConceptEvaluator.NAME, "matches", "*")
|
self.register_debug_vars(DefConceptEvaluator.NAME, "matches", "*")
|
||||||
self.register_debug_vars(DefConceptEvaluator.NAME, "eval", "*")
|
self.register_debug_vars(DefConceptEvaluator.NAME, "eval", "*")
|
||||||
@@ -351,7 +559,8 @@ class SheerkaDebugManager(BaseService):
|
|||||||
self.register_debug_vars(PythonEvaluator.NAME, "eval", "ret")
|
self.register_debug_vars(PythonEvaluator.NAME, "eval", "ret")
|
||||||
self.register_debug_vars("Exceptions", PythonEvaluator.NAME + "-eval", "exception")
|
self.register_debug_vars("Exceptions", PythonEvaluator.NAME + "-eval", "exception")
|
||||||
self.register_debug_vars("Exceptions", PythonEvaluator.NAME + "-eval", "trace")
|
self.register_debug_vars("Exceptions", PythonEvaluator.NAME + "-eval", "trace")
|
||||||
self.register_debug_vars(SyaNodeParser.NAME, "parse", "*")
|
self.register_debug_vars(SyaNodeParser.NAME, "parse", "#[number]")
|
||||||
|
self.register_debug_vars(SyaNodeParser.NAME, "parse", "stats")
|
||||||
self.register_debug_vars(MultipleSuccessEvaluator.NAME, "matches", "return_values")
|
self.register_debug_vars(MultipleSuccessEvaluator.NAME, "matches", "return_values")
|
||||||
|
|
||||||
def initialize_deferred(self, context, is_first_time):
|
def initialize_deferred(self, context, is_first_time):
|
||||||
@@ -498,14 +707,30 @@ class SheerkaDebugManager(BaseService):
|
|||||||
self.sheerka.record_var(context, self.NAME, "activated", self.activated)
|
self.sheerka.record_var(context, self.NAME, "activated", self.activated)
|
||||||
return self.sheerka.ret(SheerkaDebugManager.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
|
return self.sheerka.ret(SheerkaDebugManager.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
|
||||||
|
|
||||||
|
def set_debug_logger_definition(self, logger_definition):
|
||||||
|
"""
|
||||||
|
Logger definition to use. By default it's the ConsoleDebugLogger
|
||||||
|
logger_definition can be a list of debug loggers
|
||||||
|
:param logger_definition:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.debug_logger_definition = logger_definition
|
||||||
|
|
||||||
def debug(self, *args, **kwargs):
|
def debug(self, *args, **kwargs):
|
||||||
print(*args, **kwargs)
|
print(*args, **kwargs)
|
||||||
|
|
||||||
def get_debugger(self, context, who, method_name, new_debug_id=True):
|
def get_debugger(self, context, who, method_name, new_debug_id=True, forced_debug_id=None):
|
||||||
if self.compute_debug(context, who, method_name):
|
if self.compute_debug(context, who, method_name):
|
||||||
debug_id = ConsoleDebugLogger.next_id(context.event.get_digest() + str(context.id)) if new_debug_id \
|
hint = context.event.get_digest() + str(context.id)
|
||||||
else ConsoleDebugLogger.current_id(context.event.get_digest() + str(context.id))
|
if forced_debug_id is not None:
|
||||||
return ConsoleDebugLogger(self, context, who, method_name, debug_id)
|
debug_id = forced_debug_id
|
||||||
|
BaseDebugLogger.ids[hint] = debug_id
|
||||||
|
elif new_debug_id:
|
||||||
|
debug_id = BaseDebugLogger.next_id(hint)
|
||||||
|
else:
|
||||||
|
debug_id = BaseDebugLogger.current_id(hint)
|
||||||
|
|
||||||
|
return self.get_new_debug_logger_instance(context, who, method_name, debug_id)
|
||||||
|
|
||||||
return NullDebugLogger()
|
return NullDebugLogger()
|
||||||
|
|
||||||
@@ -534,7 +759,8 @@ class SheerkaDebugManager(BaseService):
|
|||||||
setting.enabled = enabled
|
setting.enabled = enabled
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
items_container.append(DebugItem(item,
|
items_container.append(DebugItem(item_type,
|
||||||
|
item,
|
||||||
service,
|
service,
|
||||||
method,
|
method,
|
||||||
context_id,
|
context_id,
|
||||||
@@ -919,3 +1145,35 @@ class SheerkaDebugManager(BaseService):
|
|||||||
del res["self"]
|
del res["self"]
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def list_debug_settings(self):
|
||||||
|
settings = self.debug_vars_settings + self.debug_concepts_settings + self.debug_rules_settings
|
||||||
|
return self.sheerka.new(BuiltinConcepts.TO_LIST, body=settings)
|
||||||
|
|
||||||
|
def get_new_debug_logger_instance(self, context, who, method_name, debug_id):
|
||||||
|
if hasattr(self.debug_logger_definition, "__iter__"):
|
||||||
|
loggers = []
|
||||||
|
for logger_type in self.debug_logger_definition:
|
||||||
|
logger = logger_type(self, context, who, method_name, debug_id)
|
||||||
|
if logger.keep_track:
|
||||||
|
key = (who, method_name, context.id, debug_id)
|
||||||
|
self.instantiated_loggers[key] = logger
|
||||||
|
return TeeDebugLogger(self, context, who, method_name, debug_id, loggers)
|
||||||
|
|
||||||
|
logger = self.debug_logger_definition(self, context, who, method_name, debug_id)
|
||||||
|
if logger.keep_track:
|
||||||
|
key = (who, method_name, context.id, debug_id)
|
||||||
|
self.instantiated_loggers[key] = logger
|
||||||
|
return logger
|
||||||
|
|
||||||
|
def get_debugger_logs(self):
|
||||||
|
res = {}
|
||||||
|
for k, v in [(k, v) for k, v in self.instantiated_loggers.items() if isinstance(v, ListDebugLogger)]:
|
||||||
|
key = list(k)
|
||||||
|
if v.items and v.items[0].type == ListDebugLogger.ITEM_TYPE_ENTERING:
|
||||||
|
if "source" in v.items[0].args:
|
||||||
|
key.append(v.items[0].args["source"])
|
||||||
|
|
||||||
|
res[tuple(key)] = v.items
|
||||||
|
|
||||||
|
return res
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from core.builtin_concepts import BuiltinConcepts
|
from core.builtin_concepts import BuiltinConcepts
|
||||||
from core.builtin_helpers import expect_one, only_successful, ensure_concept, is_only_successful, ensure_bnf
|
from core.builtin_helpers import ensure_bnf, ensure_concept, expect_one, is_only_successful, only_successful
|
||||||
from core.concept import Concept, DoNotResolve, ConceptParts, InfiniteRecursionResolved, AllConceptParts, \
|
from core.concept import AllConceptParts, Concept, ConceptParts, DoNotResolve, InfiniteRecursionResolved, \
|
||||||
concept_part_value
|
concept_part_value
|
||||||
from core.global_symbols import NotInit, CURRENT_OBJ, INIT_AST_PARSERS, INIT_AST_QUESTION_PARSERS
|
from core.global_symbols import CURRENT_OBJ, INIT_AST_PARSERS, INIT_AST_QUESTION_PARSERS, NotInit
|
||||||
from core.rule import Rule
|
from core.rule import Rule
|
||||||
from core.sheerka.services.SheerkaEvaluateRules import SheerkaEvaluateRules
|
from core.sheerka.services.SheerkaEvaluateRules import SheerkaEvaluateRules
|
||||||
from core.sheerka.services.SheerkaExecute import ParserInput, SheerkaExecute
|
from core.sheerka.services.SheerkaExecute import ParserInput, SheerkaExecute
|
||||||
from core.sheerka.services.SheerkaRuleManager import PythonConditionExprVisitor
|
from core.sheerka.services.SheerkaRuleManager import PythonConditionExprVisitor
|
||||||
from core.sheerka.services.sheerka_service import BaseService, FailedToCompileError, ChickenAndEggException
|
from core.sheerka.services.sheerka_service import BaseService, ChickenAndEggException, FailedToCompileError
|
||||||
from core.tokenizer import Tokenizer
|
from core.tokenizer import Tokenizer
|
||||||
from core.utils import unstr_concept
|
from core.utils import unstr_concept
|
||||||
from parsers.BaseExpressionParser import TrueifyVisitor
|
from parsers.BaseExpressionParser import TrueifyVisitor
|
||||||
@@ -38,6 +38,13 @@ class WhereClauseDef:
|
|||||||
conditions: list # compiled trueified
|
conditions: list # compiled trueified
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EvaluationHints:
|
||||||
|
eval_body: bool = None # true if the body must be evaluated
|
||||||
|
eval_question: bool = None # true if the eval_question must be set
|
||||||
|
expression_only: bool = None # True if function/methods to forbid functions with side effect
|
||||||
|
|
||||||
|
|
||||||
class SheerkaEvaluateConcept(BaseService):
|
class SheerkaEvaluateConcept(BaseService):
|
||||||
NAME = "EvaluateConcept"
|
NAME = "EvaluateConcept"
|
||||||
|
|
||||||
@@ -48,7 +55,7 @@ class SheerkaEvaluateConcept(BaseService):
|
|||||||
def initialize(self):
|
def initialize(self):
|
||||||
self.sheerka.bind_service_method(self.NAME, self.evaluate_concept, True)
|
self.sheerka.bind_service_method(self.NAME, self.evaluate_concept, True)
|
||||||
self.sheerka.bind_service_method(self.NAME, self.call_concept, True)
|
self.sheerka.bind_service_method(self.NAME, self.call_concept, True)
|
||||||
self.sheerka.bind_service_method(self.NAME, self.call_concept, False, as_name="evaluate_question")
|
self.sheerka.bind_service_method(self.NAME, self.evaluate_question, False)
|
||||||
self.sheerka.bind_service_method(self.NAME, self.set_auto_eval, True)
|
self.sheerka.bind_service_method(self.NAME, self.set_auto_eval, True)
|
||||||
|
|
||||||
def initialize_deferred(self, context, is_first_time):
|
def initialize_deferred(self, context, is_first_time):
|
||||||
@@ -532,6 +539,8 @@ class SheerkaEvaluateConcept(BaseService):
|
|||||||
value = current_concept.get_value(var[0])
|
value = current_concept.get_value(var[0])
|
||||||
if value != NotInit:
|
if value != NotInit:
|
||||||
sub_context.add_to_short_term_memory(var[0], current_concept.get_value(var[0]))
|
sub_context.add_to_short_term_memory(var[0], current_concept.get_value(var[0]))
|
||||||
|
|
||||||
|
# KSI 2021-08-10 It seems that a copy is not needed here, as it's the first thing done ine execute()
|
||||||
use_copy = to_resolve.copy() if isinstance(to_resolve, list) else to_resolve
|
use_copy = to_resolve.copy() if isinstance(to_resolve, list) else to_resolve
|
||||||
r = self.sheerka.execute(sub_context, use_copy, CONCEPT_EVALUATION_STEPS)
|
r = self.sheerka.execute(sub_context, use_copy, CONCEPT_EVALUATION_STEPS)
|
||||||
|
|
||||||
@@ -601,22 +610,30 @@ class SheerkaEvaluateConcept(BaseService):
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def evaluate_concept(self, context, concept: Concept, eval_body=False, validation_only=False, metadata=None):
|
def evaluate_concept(self, context, concept: Concept, hints: EvaluationHints = None, metadata=None):
|
||||||
"""
|
"""
|
||||||
Evaluation a concept
|
Evaluation a concept
|
||||||
ie : resolve its body
|
ie : resolve its body
|
||||||
:param context:
|
:param context:
|
||||||
:param concept:
|
:param concept:
|
||||||
:param eval_body:
|
:param hints:
|
||||||
:param validation_only: When set, the body is never evaluated, whatever eval_body or EVAL_BODY_REQUESTED
|
|
||||||
:param metadata: list of metadata to evaluate ('pre', 'post'...)
|
:param metadata: list of metadata to evaluate ('pre', 'post'...)
|
||||||
:return: value of the evaluation or error
|
:return: value of the evaluation or error
|
||||||
"""
|
"""
|
||||||
|
|
||||||
failed_to_evaluate_body = False
|
failed_to_evaluate_body = False
|
||||||
|
hints = hints or EvaluationHints()
|
||||||
|
|
||||||
if concept.get_hints().is_evaluated:
|
if concept.get_hints().is_evaluated:
|
||||||
return self.apply_ret(concept, eval_body or context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED))
|
return self.apply_ret(concept, hints.eval_body or context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED))
|
||||||
|
|
||||||
|
to_reset = set()
|
||||||
|
if isinstance(hints.eval_body, bool) and not hints.eval_body:
|
||||||
|
to_reset.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
|
||||||
|
if isinstance(hints.eval_question, bool) and not hints.eval_question:
|
||||||
|
to_reset.add(BuiltinConcepts.EVAL_QUESTION_REQUESTED)
|
||||||
|
if isinstance(hints.expression_only, bool) and not hints.expression_only:
|
||||||
|
to_reset.add(BuiltinConcepts.EXPRESSION_ONLY_REQUESTED)
|
||||||
|
|
||||||
# if concept.get_hints().use_copy:
|
# if concept.get_hints().use_copy:
|
||||||
# raise Exception("Use copy")
|
# raise Exception("Use copy")
|
||||||
@@ -635,17 +652,21 @@ class SheerkaEvaluateConcept(BaseService):
|
|||||||
# return concept
|
# return concept
|
||||||
|
|
||||||
desc = f"Evaluating concept {concept}"
|
desc = f"Evaluating concept {concept}"
|
||||||
with context.push(BuiltinConcepts.EVALUATING_CONCEPT, concept, desc=desc) as sub_context:
|
with context.push(BuiltinConcepts.EVALUATING_CONCEPT, concept, desc=desc, reset_hints=to_reset) as sub_context:
|
||||||
sub_context.add_inputs(eval_body=eval_body)
|
sub_context.add_inputs(hints=hints)
|
||||||
if eval_body:
|
|
||||||
# ask for body evaluation
|
# update context with evaluate_concept parameters
|
||||||
|
if hints.eval_body:
|
||||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
|
sub_context.protected_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
|
||||||
|
|
||||||
if validation_only:
|
if hints.eval_question:
|
||||||
|
sub_context.protected_hints.add(BuiltinConcepts.EVAL_QUESTION_REQUESTED)
|
||||||
|
|
||||||
|
if hints.expression_only:
|
||||||
# Never call methods with side effect in this concept or sub concepts
|
# Never call methods with side effect in this concept or sub concepts
|
||||||
sub_context.protected_hints.add(BuiltinConcepts.EXPRESSION_ONLY_REQUESTED)
|
sub_context.protected_hints.add(BuiltinConcepts.EXPRESSION_ONLY_REQUESTED)
|
||||||
|
|
||||||
# auto evaluate commands
|
# update context with evaluate_concept parameters
|
||||||
if context.sheerka.isa(concept, context.sheerka.new(BuiltinConcepts.AUTO_EVAL)):
|
if context.sheerka.isa(concept, context.sheerka.new(BuiltinConcepts.AUTO_EVAL)):
|
||||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
|
sub_context.protected_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
|
||||||
|
|
||||||
@@ -659,10 +680,10 @@ class SheerkaEvaluateConcept(BaseService):
|
|||||||
except ChickenAndEggException as ex:
|
except ChickenAndEggException as ex:
|
||||||
return ex.error
|
return ex.error
|
||||||
|
|
||||||
# to make sure of the order, it don't use ConceptParts.get_parts()
|
# to make sure of the order, it does not use ConceptParts.get_parts()
|
||||||
# variables must be evaluated first, body must be evaluated before where
|
# variables must be evaluated first, body must be evaluated before where
|
||||||
all_metadata_to_eval = metadata or self.compute_metadata_to_eval(sub_context, concept)
|
all_metadata_to_eval = metadata or self.compute_metadata_to_eval(sub_context, concept)
|
||||||
if validation_only and ConceptParts.BODY in all_metadata_to_eval:
|
if hints.expression_only and ConceptParts.BODY in all_metadata_to_eval:
|
||||||
all_metadata_to_eval.remove(ConceptParts.BODY)
|
all_metadata_to_eval.remove(ConceptParts.BODY)
|
||||||
|
|
||||||
for metadata_to_eval in all_metadata_to_eval:
|
for metadata_to_eval in all_metadata_to_eval:
|
||||||
@@ -681,7 +702,7 @@ class SheerkaEvaluateConcept(BaseService):
|
|||||||
var_name,
|
var_name,
|
||||||
concept,
|
concept,
|
||||||
True,
|
True,
|
||||||
not sub_context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED),
|
sub_context.in_context(BuiltinConcepts.EXPRESSION_ONLY_REQUESTED),
|
||||||
w_clause)
|
w_clause)
|
||||||
else:
|
else:
|
||||||
# Do not send the current concept for the properties
|
# Do not send the current concept for the properties
|
||||||
@@ -690,7 +711,7 @@ class SheerkaEvaluateConcept(BaseService):
|
|||||||
var_name,
|
var_name,
|
||||||
concept,
|
concept,
|
||||||
True,
|
True,
|
||||||
not sub_context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED),
|
sub_context.in_context(BuiltinConcepts.EXPRESSION_ONLY_REQUESTED),
|
||||||
w_clause)
|
w_clause)
|
||||||
|
|
||||||
if isinstance(resolved, Concept) and not sub_context.sheerka.is_success(resolved):
|
if isinstance(resolved, Concept) and not sub_context.sheerka.is_success(resolved):
|
||||||
@@ -722,7 +743,7 @@ class SheerkaEvaluateConcept(BaseService):
|
|||||||
part_key,
|
part_key,
|
||||||
concept,
|
concept,
|
||||||
force_concept_eval,
|
force_concept_eval,
|
||||||
not sub_context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED),
|
sub_context.in_context(BuiltinConcepts.EXPRESSION_ONLY_REQUESTED),
|
||||||
None)
|
None)
|
||||||
|
|
||||||
# 'FATAL' error is detected, let's stop
|
# 'FATAL' error is detected, let's stop
|
||||||
@@ -766,10 +787,25 @@ class SheerkaEvaluateConcept(BaseService):
|
|||||||
return self.apply_ret(concept, sub_context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED))
|
return self.apply_ret(concept, sub_context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED))
|
||||||
|
|
||||||
def call_concept(self, context, concept, *args, **kwargs):
|
def call_concept(self, context, concept, *args, **kwargs):
|
||||||
|
return self.call_concept_with_args(context,
|
||||||
|
concept,
|
||||||
|
hints=EvaluationHints(eval_body=True, eval_question=False),
|
||||||
|
*args,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
def evaluate_question(self, context, concept, *args, **kwargs):
|
||||||
|
return self.call_concept_with_args(context,
|
||||||
|
concept,
|
||||||
|
hints=EvaluationHints(eval_body=True, eval_question=True),
|
||||||
|
*args,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
def call_concept_with_args(self, context, concept, hints, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
call the concept using either args or kwargs (not both)
|
call the concept using either args or kwargs (not both)
|
||||||
:param context:
|
:param context:
|
||||||
:param concept:
|
:param concept:
|
||||||
|
:param hints:
|
||||||
:param args:
|
:param args:
|
||||||
:param kwargs:
|
:param kwargs:
|
||||||
:return:
|
:return:
|
||||||
@@ -780,9 +816,13 @@ class SheerkaEvaluateConcept(BaseService):
|
|||||||
concept.get_hints().is_instance = True
|
concept.get_hints().is_instance = True
|
||||||
concept.get_hints().is_evaluated = False # force evaluation
|
concept.get_hints().is_evaluated = False # force evaluation
|
||||||
|
|
||||||
# TODO : update the variables before calling the concept
|
for param_name, arg in zip(concept.get_metadata().parameters, args):
|
||||||
|
concept.get_compiled()[param_name] = DoNotResolve(arg)
|
||||||
|
|
||||||
evaluated = self.evaluate_concept(context, concept)
|
for param_name, param_value in kwargs.items():
|
||||||
|
concept.get_compiled()[param_name] = DoNotResolve(param_value)
|
||||||
|
|
||||||
|
evaluated = self.evaluate_concept(context, concept, hints=hints)
|
||||||
if self.sheerka.has_error(context, evaluated):
|
if self.sheerka.has_error(context, evaluated):
|
||||||
raise ConceptEvalException(evaluated)
|
raise ConceptEvalException(evaluated)
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import core.utils
|
|||||||
from cache.FastCache import FastCache
|
from cache.FastCache import FastCache
|
||||||
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept
|
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept
|
||||||
from core.concept import ConceptParts
|
from core.concept import ConceptParts
|
||||||
from core.global_symbols import NotFound, NO_MATCH, EVENT_CONCEPT_CREATED, EVENT_CONCEPT_MODIFIED, EVENT_CONCEPT_DELETED
|
from core.global_symbols import EVENT_CONCEPT_CREATED, EVENT_CONCEPT_DELETED, EVENT_CONCEPT_MODIFIED, NO_MATCH, NotFound
|
||||||
from core.sheerka.services.sheerka_service import BaseService
|
from core.sheerka.services.sheerka_service import BaseService
|
||||||
from core.tokenizer import Tokenizer, TokenKind, Token, Keywords
|
from core.tokenizer import Keywords, Token, TokenKind, Tokenizer
|
||||||
|
|
||||||
PARSE_STEPS = [BuiltinConcepts.BEFORE_PARSING, BuiltinConcepts.PARSING, BuiltinConcepts.AFTER_PARSING]
|
PARSE_STEPS = [BuiltinConcepts.BEFORE_PARSING, BuiltinConcepts.PARSING, BuiltinConcepts.AFTER_PARSING]
|
||||||
PARSE_AND_EVAL_STEPS = PARSE_STEPS + [BuiltinConcepts.BEFORE_EVALUATION,
|
PARSE_AND_EVAL_STEPS = PARSE_STEPS + [BuiltinConcepts.BEFORE_EVALUATION,
|
||||||
@@ -179,6 +179,24 @@ class ParserInput:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def clone(self):
|
||||||
|
clone = ParserInput(self.text)
|
||||||
|
clone.tokens = self.tokens
|
||||||
|
clone.length = self.length
|
||||||
|
clone.yield_oef = self.yield_oef
|
||||||
|
clone.start = self.start
|
||||||
|
clone.end = self.end
|
||||||
|
clone.sub_text = self.sub_text
|
||||||
|
clone.sub_tokens = self.sub_tokens
|
||||||
|
clone.from_tokens = self.from_tokens
|
||||||
|
clone.pos = self.pos
|
||||||
|
clone.token = self.token
|
||||||
|
|
||||||
|
return clone
|
||||||
|
|
||||||
|
def sub_part(self, start, end, yield_oef=None):
|
||||||
|
return ParserInput(self.text, self.tokens, self.length, start, end, yield_oef)
|
||||||
|
|
||||||
|
|
||||||
class SheerkaExecute(BaseService):
|
class SheerkaExecute(BaseService):
|
||||||
"""
|
"""
|
||||||
@@ -227,6 +245,7 @@ class SheerkaExecute(BaseService):
|
|||||||
self.sheerka.bind_service_method(self.NAME, self.parse_function, False, visible=False)
|
self.sheerka.bind_service_method(self.NAME, self.parse_function, False, visible=False)
|
||||||
self.sheerka.bind_service_method(self.NAME, self.parse_python, False, visible=False)
|
self.sheerka.bind_service_method(self.NAME, self.parse_python, False, visible=False)
|
||||||
self.sheerka.bind_service_method(self.NAME, self.parse_expression, False, visible=False)
|
self.sheerka.bind_service_method(self.NAME, self.parse_expression, False, visible=False)
|
||||||
|
self.sheerka.bind_service_method(self.NAME, self.clear_parser_cache, True)
|
||||||
|
|
||||||
self.reset_registered_evaluators()
|
self.reset_registered_evaluators()
|
||||||
self.reset_registered_parsers()
|
self.reset_registered_parsers()
|
||||||
@@ -274,6 +293,9 @@ class SheerkaExecute(BaseService):
|
|||||||
use_classes=True)
|
use_classes=True)
|
||||||
self.question_parsers = [p.name for p in question_parsers]
|
self.question_parsers = [p.name for p in question_parsers]
|
||||||
|
|
||||||
|
def clear_parser_cache(self):
|
||||||
|
self.parsers_cache.clear()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_grouped(evaluators, use_classes=False):
|
def get_grouped(evaluators, use_classes=False):
|
||||||
"""
|
"""
|
||||||
@@ -442,7 +464,7 @@ class SheerkaExecute(BaseService):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def add_to_parser_cache(self, parsers_key, text, return_value):
|
def add_to_parser_cache(self, parsers_key, text, return_value):
|
||||||
if parsers_key is None:
|
if not self.sheerka.enable_parser_caching or parsers_key is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
key = (parsers_key, text)
|
key = (parsers_key, text)
|
||||||
@@ -517,7 +539,7 @@ class SheerkaExecute(BaseService):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
# 3. Try the cache
|
# 3. Try the cache
|
||||||
if to_process and parsers_key:
|
if self.sheerka.enable_parser_caching and to_process and parsers_key:
|
||||||
processed = []
|
processed = []
|
||||||
for return_value in to_process:
|
for return_value in to_process:
|
||||||
to_parse_as_str = self.get_input_as_text(return_value.body.body) \
|
to_parse_as_str = self.get_input_as_text(return_value.body.body) \
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from core.concept import Concept, DEFINITION_TYPE_BNF
|
|||||||
from core.global_symbols import NotFound
|
from core.global_symbols import NotFound
|
||||||
from core.sheerka.services.SheerkaConceptManager import SheerkaConceptManager
|
from core.sheerka.services.SheerkaConceptManager import SheerkaConceptManager
|
||||||
from core.sheerka.services.sheerka_service import BaseService
|
from core.sheerka.services.sheerka_service import BaseService
|
||||||
from core.tokenizer import Tokenizer, TokenKind
|
from core.tokenizer import TokenKind, Tokenizer
|
||||||
from core.utils import merge_sets
|
from core.utils import merge_sets
|
||||||
|
|
||||||
|
|
||||||
@@ -66,7 +66,8 @@ class SheerkaIsAManager(BaseService):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
concept.set_prop(BuiltinConcepts.ISA, new_concept_set)
|
concept.set_prop(BuiltinConcepts.ISA, new_concept_set)
|
||||||
res = self.add_concept_to_set(context, concept, concept_set)
|
# KSI 2021-08-12. Make sure to use the newly created concept to put in cache
|
||||||
|
res = self.add_concept_to_set(context, res.body.body, concept_set)
|
||||||
return res
|
return res
|
||||||
else:
|
else:
|
||||||
concept.set_prop(BuiltinConcepts.ISA, new_concept_set)
|
concept.set_prop(BuiltinConcepts.ISA, new_concept_set)
|
||||||
@@ -144,15 +145,16 @@ class SheerkaIsAManager(BaseService):
|
|||||||
if not self.isaset(context, sub_concept):
|
if not self.isaset(context, sub_concept):
|
||||||
return self.sheerka.new(BuiltinConcepts.NOT_A_SET, body=concept)
|
return self.sheerka.new(BuiltinConcepts.NOT_A_SET, body=concept)
|
||||||
|
|
||||||
|
# is it a valid concept ?
|
||||||
|
sub_concept = core.builtin_helpers.ensure_evaluated(context, sub_concept)
|
||||||
|
if not self.sheerka.is_success(sub_concept):
|
||||||
|
return sub_concept
|
||||||
|
|
||||||
# first, try to see if sub_concept has it's own group entry
|
# first, try to see if sub_concept has it's own group entry
|
||||||
ids = self.sheerka.om.get(self.CONCEPTS_GROUPS_ENTRY, sub_concept.id)
|
ids = self.sheerka.om.get(self.CONCEPTS_GROUPS_ENTRY, sub_concept.id)
|
||||||
concepts = self._get_concepts(context, ids, True)
|
concepts = self._get_concepts(context, ids, True)
|
||||||
|
|
||||||
# aggregate with en entries from its body
|
# aggregate with en entries from its body
|
||||||
sub_concept = core.builtin_helpers.ensure_evaluated(context, sub_concept)
|
|
||||||
if not self.sheerka.is_success(sub_concept):
|
|
||||||
return sub_concept
|
|
||||||
|
|
||||||
if self.isaset(context, sub_concept.body):
|
if self.isaset(context, sub_concept.body):
|
||||||
other_concepts = _get_set_elements(sub_concept.body)
|
other_concepts = _get_set_elements(sub_concept.body)
|
||||||
if not self.sheerka.is_success(other_concepts):
|
if not self.sheerka.is_success(other_concepts):
|
||||||
@@ -169,6 +171,7 @@ class SheerkaIsAManager(BaseService):
|
|||||||
if (res := self.sheerka.om.get(self.CONCEPTS_IN_GROUPS_ENTRY, concept.id)) is not NotFound:
|
if (res := self.sheerka.om.get(self.CONCEPTS_IN_GROUPS_ENTRY, concept.id)) is not NotFound:
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
# get the elements that are in the set
|
||||||
res = _get_set_elements(concept)
|
res = _get_set_elements(concept)
|
||||||
|
|
||||||
# put in cache
|
# put in cache
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from core.rule import Rule, ACTION_TYPE_PRINT
|
|||||||
from core.sheerka.Sheerka import RECOGNIZED_BY_NAME, RECOGNIZED_BY_ID
|
from core.sheerka.Sheerka import RECOGNIZED_BY_NAME, RECOGNIZED_BY_ID
|
||||||
from core.sheerka.services.sheerka_service import BaseService, FailedToCompileError, UnknownVariableError
|
from core.sheerka.services.sheerka_service import BaseService, FailedToCompileError, UnknownVariableError
|
||||||
from core.tokenizer import Keywords, TokenKind, Token
|
from core.tokenizer import Keywords, TokenKind, Token
|
||||||
from core.utils import merge_dictionaries, merge_sets, get_safe_str_value
|
from core.utils import merge_dicts, merge_sets, get_safe_str_value
|
||||||
from evaluators.PythonEvaluator import PythonEvaluator
|
from evaluators.PythonEvaluator import PythonEvaluator
|
||||||
from parsers.BaseExpressionParser import AndNode, ExpressionVisitor, VariableNode, ComparisonNode, FunctionNode, \
|
from parsers.BaseExpressionParser import AndNode, ExpressionVisitor, VariableNode, ComparisonNode, FunctionNode, \
|
||||||
ComparisonType, NotNode, NameExprNode
|
ComparisonType, NotNode, NameExprNode
|
||||||
@@ -503,8 +503,11 @@ class GetConditionExprVisitor(ExpressionVisitor):
|
|||||||
def get_object_name(self, obj, objects=None):
|
def get_object_name(self, obj, objects=None):
|
||||||
"""
|
"""
|
||||||
object found during the parsing are not serialized
|
object found during the parsing are not serialized
|
||||||
They are kept in a dictionary and this function returns a new name for every new object
|
They are kept in a dictionary.
|
||||||
:return:
|
This function returns a new name for every new object
|
||||||
|
:param obj: object for which a name is to be created
|
||||||
|
:param objects: already created names (it's a dictionary)
|
||||||
|
:return: tuple(name created, dictionary of already created names)
|
||||||
"""
|
"""
|
||||||
if objects is None:
|
if objects is None:
|
||||||
objects = {}
|
objects = {}
|
||||||
@@ -526,9 +529,9 @@ class GetConditionExprVisitor(ExpressionVisitor):
|
|||||||
|
|
||||||
def add_variable(self, target):
|
def add_variable(self, target):
|
||||||
"""
|
"""
|
||||||
Create a new variable
|
Create a new variable name of the object 'target'
|
||||||
:param target:
|
:param target:
|
||||||
:return:
|
:return: generated name
|
||||||
"""
|
"""
|
||||||
var_name = f"__x_{self.var_counter:02}__"
|
var_name = f"__x_{self.var_counter:02}__"
|
||||||
self.var_counter += 1
|
self.var_counter += 1
|
||||||
@@ -881,7 +884,7 @@ class PythonConditionExprVisitorObj:
|
|||||||
|
|
||||||
return PythonConditionExprVisitorObj(get_source(left.text, right.text),
|
return PythonConditionExprVisitorObj(get_source(left.text, right.text),
|
||||||
get_source(left.source, right.source),
|
get_source(left.source, right.source),
|
||||||
merge_dictionaries(left.objects, right.objects),
|
merge_dicts(left.objects, right.objects),
|
||||||
merge_sets(left.variables, right.variables),
|
merge_sets(left.variables, right.variables),
|
||||||
merge_sets(left.not_variables, right.not_variables))
|
merge_sets(left.not_variables, right.not_variables))
|
||||||
|
|
||||||
@@ -903,7 +906,7 @@ class PythonConditionExprVisitorObj:
|
|||||||
|
|
||||||
return PythonConditionExprVisitorObj(get_source(left.text, right.text),
|
return PythonConditionExprVisitorObj(get_source(left.text, right.text),
|
||||||
get_source(left.source, right.source),
|
get_source(left.source, right.source),
|
||||||
merge_dictionaries(left.objects, right.objects),
|
merge_dicts(left.objects, right.objects),
|
||||||
merge_sets(left.variables, right.variables),
|
merge_sets(left.variables, right.variables),
|
||||||
merge_sets(left.not_variables, right.not_variables))
|
merge_sets(left.not_variables, right.not_variables))
|
||||||
|
|
||||||
@@ -929,7 +932,7 @@ class PythonConditionExprVisitorObj:
|
|||||||
|
|
||||||
return PythonConditionExprVisitorObj(text,
|
return PythonConditionExprVisitorObj(text,
|
||||||
get_source(left.source, right.source),
|
get_source(left.source, right.source),
|
||||||
merge_dictionaries(left.objects, right.objects),
|
merge_dicts(left.objects, right.objects),
|
||||||
merge_sets(left.variables, right.variables),
|
merge_sets(left.variables, right.variables),
|
||||||
merge_sets(left.not_variables, right.not_variables))
|
merge_sets(left.not_variables, right.not_variables))
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,12 @@ class Variable(ServiceObj):
|
|||||||
def get_key(self):
|
def get_key(self):
|
||||||
return f"{self.who}|{self.key}"
|
return f"{self.who}|{self.key}"
|
||||||
|
|
||||||
def __str__(self):
|
def __repr__(self):
|
||||||
return f"({self.who}){self.key}={self.value}"
|
return f"({self.who}){self.key}={self.value}"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.who}.{self.key}={self.value}"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class InternalObj:
|
class InternalObj:
|
||||||
@@ -49,7 +52,8 @@ class SheerkaVariableManager(BaseService):
|
|||||||
self.sheerka.name: {"enable_process_return_values",
|
self.sheerka.name: {"enable_process_return_values",
|
||||||
"save_execution_context",
|
"save_execution_context",
|
||||||
"enable_process_rules",
|
"enable_process_rules",
|
||||||
"enable_commands_backup"}
|
"enable_commands_backup",
|
||||||
|
"enable_parser_caching"}
|
||||||
}
|
}
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
@@ -79,7 +83,7 @@ class SheerkaVariableManager(BaseService):
|
|||||||
|
|
||||||
def record_var(self, context, who, key, value):
|
def record_var(self, context, who, key, value):
|
||||||
"""
|
"""
|
||||||
|
Internal set
|
||||||
:param context:
|
:param context:
|
||||||
:param who: entity that owns the key (acts as a namespace)
|
:param who: entity that owns the key (acts as a namespace)
|
||||||
:param key:
|
:param key:
|
||||||
@@ -96,6 +100,9 @@ class SheerkaVariableManager(BaseService):
|
|||||||
setattr(service, key, value)
|
setattr(service, key, value)
|
||||||
|
|
||||||
def load_var(self, who, key):
|
def load_var(self, who, key):
|
||||||
|
"""
|
||||||
|
Internal get
|
||||||
|
"""
|
||||||
variable = self.sheerka.om.get(self.VARIABLES_ENTRY, who + "|" + key)
|
variable = self.sheerka.om.get(self.VARIABLES_ENTRY, who + "|" + key)
|
||||||
if variable is NotFound:
|
if variable is NotFound:
|
||||||
return NotFound
|
return NotFound
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
default_debug_name = "*default*"
|
default_debug_name = "*default*"
|
||||||
debug_activated = set()
|
debug_activated = set()
|
||||||
|
append_file = False
|
||||||
|
items = []
|
||||||
|
|
||||||
|
if not append_file and os.path.exists("debug.txt"):
|
||||||
|
os.remove("debug.txt")
|
||||||
|
|
||||||
|
|
||||||
def my_debug(*args, check_started=None):
|
def my_debug(*args, check_started=None):
|
||||||
@@ -28,8 +35,10 @@ def my_debug(*args, check_started=None):
|
|||||||
if isinstance(arg, list):
|
if isinstance(arg, list):
|
||||||
for item in arg:
|
for item in arg:
|
||||||
f.write(f"{item}\n")
|
f.write(f"{item}\n")
|
||||||
|
items.append(item)
|
||||||
else:
|
else:
|
||||||
f.write(f"{arg}\n")
|
f.write(f"{arg}\n")
|
||||||
|
items.append(args)
|
||||||
|
|
||||||
|
|
||||||
def start_debug(debug_name=default_debug_name, msg=None):
|
def start_debug(debug_name=default_debug_name, msg=None):
|
||||||
|
|||||||
+21
-5
@@ -1,3 +1,4 @@
|
|||||||
|
from contextlib import contextmanager
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
@@ -25,10 +26,10 @@ class TokenKind(Enum):
|
|||||||
STAR = "star"
|
STAR = "star"
|
||||||
SLASH = "slash"
|
SLASH = "slash"
|
||||||
PERCENT = "percent"
|
PERCENT = "percent"
|
||||||
COMMA = "comma"
|
COMMA = "comma" # ,
|
||||||
SEMICOLON = "semicolon"
|
SEMICOLON = "semicolon" # ;
|
||||||
COLON = "colon"
|
COLON = "colon" # :
|
||||||
DOT = "dot"
|
DOT = "dot" # .
|
||||||
QMARK = "qmark"
|
QMARK = "qmark"
|
||||||
VBAR = "vbar"
|
VBAR = "vbar"
|
||||||
AMPER = "amper"
|
AMPER = "amper"
|
||||||
@@ -95,7 +96,7 @@ class Token:
|
|||||||
if self.type == TokenKind.EOF:
|
if self.type == TokenKind.EOF:
|
||||||
self._repr_value = "<EOF>"
|
self._repr_value = "<EOF>"
|
||||||
elif self.type == TokenKind.WHITESPACE:
|
elif self.type == TokenKind.WHITESPACE:
|
||||||
self._repr_value = "<ws!>" if self.value == "" else "<tab>" if self.value[0] == "\t" else "<ws>"
|
self._repr_value = "<!ws>" if self.value == "" else "<tab>" if self.value[0] == "\t" else "<ws>"
|
||||||
elif self.type == TokenKind.NEWLINE:
|
elif self.type == TokenKind.NEWLINE:
|
||||||
self._repr_value = "<nl>"
|
self._repr_value = "<nl>"
|
||||||
elif self.type == TokenKind.CONCEPT:
|
elif self.type == TokenKind.CONCEPT:
|
||||||
@@ -566,3 +567,18 @@ class IterParser:
|
|||||||
return token_after
|
return token_after
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
return Token(TokenKind.EOF, -1, -1, -1, -1)
|
return Token(TokenKind.EOF, -1, -1, -1, -1)
|
||||||
|
|
||||||
|
|
||||||
|
def simple_token_compare(a, b):
|
||||||
|
return a.type == b.type and a.value == b.value
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def comparable_tokens():
|
||||||
|
eq = Token.__eq__
|
||||||
|
ne = Token.__ne__
|
||||||
|
setattr(Token, "__eq__", simple_token_compare)
|
||||||
|
setattr(Token, "__ne__", lambda a, b: not simple_token_compare(a, b))
|
||||||
|
yield
|
||||||
|
setattr(Token, "__eq__", eq)
|
||||||
|
setattr(Token, "__ne__", ne)
|
||||||
|
|||||||
+15
-23
@@ -8,10 +8,10 @@ import warnings
|
|||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
# from pyparsing import *
|
# from pyparsing import *
|
||||||
from pyparsing import Literal, Word, nums, Combine, Optional, delimitedList, oneOf, alphas, Suppress
|
from pyparsing import Combine, Literal, Optional, Suppress, Word, alphas, delimitedList, nums, oneOf
|
||||||
|
|
||||||
from core.global_symbols import CustomType
|
from core.global_symbols import CustomType
|
||||||
from core.tokenizer import TokenKind, Tokenizer, Token
|
from core.tokenizer import Token, TokenKind, Tokenizer
|
||||||
|
|
||||||
COLORS = {
|
COLORS = {
|
||||||
"black",
|
"black",
|
||||||
@@ -306,42 +306,34 @@ def dict_product(a, b):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def merge_dictionaries(a, b):
|
def merge_dicts(*items):
|
||||||
"""
|
"""
|
||||||
Returns a new dictionary which is the merge
|
Returns a new dictionary which is the merge of all others
|
||||||
:param a:
|
:type items: object
|
||||||
:param b:
|
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if a is None and b is None:
|
if items is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
res = {}
|
res = {}
|
||||||
if a:
|
for item in [item for item in items if item]:
|
||||||
res.update(a)
|
res.update(item)
|
||||||
|
|
||||||
if b:
|
|
||||||
res.update(b)
|
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def merge_sets(a, b):
|
def merge_sets(*items):
|
||||||
"""
|
"""
|
||||||
Merge that can handle None
|
Returns a new dictionary which is the merge of all others
|
||||||
:param a:
|
:type items: object
|
||||||
:param b:
|
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if a is None and b is None:
|
if items is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
res = set()
|
res = set()
|
||||||
if a:
|
for item in [item for item in items if item]:
|
||||||
res.update(a)
|
res.update(item)
|
||||||
|
|
||||||
if b:
|
|
||||||
res.update(b)
|
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@@ -612,7 +604,7 @@ def tokens_index(tokens, sub_tokens, skip=0, start_from_end=False):
|
|||||||
else:
|
else:
|
||||||
skip -= 1
|
skip -= 1
|
||||||
|
|
||||||
raise ValueError(f"sub tokens '{sub_tokens}' not found")
|
raise ValueError(f"sub tokens '{get_text_from_tokens(sub_tokens)}' from {sub_tokens} not found")
|
||||||
|
|
||||||
|
|
||||||
def as_bag(obj, forced_properties=None):
|
def as_bag(obj, forced_properties=None):
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from core.builtin_concepts_ids import BuiltinConcepts
|
||||||
from core.sheerka.Sheerka import ExecutionContext
|
from core.sheerka.Sheerka import ExecutionContext
|
||||||
|
|
||||||
|
|
||||||
@@ -9,10 +10,6 @@ class BaseEvaluator:
|
|||||||
PREFIX = "evaluators."
|
PREFIX = "evaluators."
|
||||||
|
|
||||||
def __init__(self, name, steps, priority: int, enabled=True):
|
def __init__(self, name, steps, priority: int, enabled=True):
|
||||||
# self.log = get_logger(self.PREFIX + self.__class__.__name__)
|
|
||||||
# self.init_log = get_logger("init." + self.PREFIX + self.__class__.__name__)
|
|
||||||
# self.verbose_log = get_logger("verbose." + self.PREFIX + self.__class__.__name__)
|
|
||||||
|
|
||||||
self.name = BaseEvaluator.get_name(name)
|
self.name = BaseEvaluator.get_name(name)
|
||||||
self.short_name = name
|
self.short_name = name
|
||||||
self.steps = steps
|
self.steps = steps
|
||||||
@@ -71,3 +68,8 @@ class AllReturnValuesEvaluator(BaseEvaluator):
|
|||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self.eaten.clear()
|
self.eaten.clear()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def valid_parser_results(context, return_values):
|
||||||
|
return [ret for ret in return_values if
|
||||||
|
ret.status and context.sheerka.isinstance(ret.body, BuiltinConcepts.PARSER_RESULT)]
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
|
|||||||
|
|
||||||
if def_concept_node.variables != NotInit:
|
if def_concept_node.variables != NotInit:
|
||||||
certain_variables = def_concept_node.variables.copy()
|
certain_variables = def_concept_node.variables.copy()
|
||||||
|
variables_found.update(certain_variables)
|
||||||
skip_variables_resolution = True
|
skip_variables_resolution = True
|
||||||
|
|
||||||
# get variables and set the sources
|
# get variables and set the sources
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ class ResolveAmbiguityEvaluator(AllReturnValuesEvaluator):
|
|||||||
# If they share the same source, that means that there are multiple results for one ParserInput
|
# If they share the same source, that means that there are multiple results for one ParserInput
|
||||||
self.sources = {}
|
self.sources = {}
|
||||||
success = False
|
success = False
|
||||||
for ret in [ret for ret in return_values if
|
for ret in self.valid_parser_results(context, return_values):
|
||||||
ret.status and context.sheerka.isinstance(ret.body, BuiltinConcepts.PARSER_RESULT)]:
|
|
||||||
source = self.get_source(context, ret)
|
source = self.get_source(context, ret)
|
||||||
concept_is_instance = self.get_has_concept_instance(ret)
|
concept_is_instance = self.get_has_concept_instance(ret)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
from core.builtin_concepts_ids import BuiltinConcepts
|
||||||
|
from core.sheerka.ExecutionContext import ExecutionContext
|
||||||
|
from evaluators.BaseEvaluator import AllReturnValuesEvaluator
|
||||||
|
from parsers.BaseNodeParser import ConceptNode
|
||||||
|
from parsers.BaseParser import BaseParser
|
||||||
|
from parsers.PythonParser import PythonParser
|
||||||
|
from parsers.SequenceNodeParser import SequenceNodeParser
|
||||||
|
|
||||||
|
|
||||||
|
class ResolveMultiplePluralAmbiguityEvaluator(AllReturnValuesEvaluator):
|
||||||
|
NAME = "ResolveMultiplePluralAmbiguity"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(self.NAME, [BuiltinConcepts.AFTER_PARSING], 55)
|
||||||
|
self.python = None
|
||||||
|
self.sequence = None
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
super().reset()
|
||||||
|
self.python = None
|
||||||
|
self.sequence = None
|
||||||
|
|
||||||
|
def matches(self, context: ExecutionContext, return_values):
|
||||||
|
for ret in self.valid_parser_results(context, return_values):
|
||||||
|
if ret.body.parser.short_name == PythonParser.NAME:
|
||||||
|
self.python = ret
|
||||||
|
elif ret.body.parser.short_name == SequenceNodeParser.NAME:
|
||||||
|
self.sequence = ret
|
||||||
|
|
||||||
|
if self.python and self.sequence:
|
||||||
|
if (len(self.sequence.body.body) == 1 and
|
||||||
|
isinstance(self.sequence.body.body[0], ConceptNode) and
|
||||||
|
context.sheerka.is_plural(self.sequence.body.body[0].concept)):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def eval(self, context: ExecutionContext, return_values):
|
||||||
|
symbol = self.sequence.body.body[0].concept.name
|
||||||
|
if context.sheerka.isinstance(context.sheerka.memory(context, symbol), BuiltinConcepts.NOT_FOUND):
|
||||||
|
return [self.copy(context, self.sequence)]
|
||||||
|
else:
|
||||||
|
return [self.copy(context, self.python)]
|
||||||
|
|
||||||
|
def copy(self, context, to_keep):
|
||||||
|
return context.sheerka.ret(self.name,
|
||||||
|
to_keep.status,
|
||||||
|
to_keep.value,
|
||||||
|
parents=[self.python, self.sequence])
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
from core.builtin_concepts import BuiltinConcepts
|
from core.builtin_concepts import BuiltinConcepts
|
||||||
from core.concept import Concept, ConceptParts
|
from core.concept import Concept, ConceptParts
|
||||||
|
from core.sheerka.services.SheerkaEvaluateConcept import EvaluationHints
|
||||||
from evaluators.BaseEvaluator import OneReturnValueEvaluator
|
from evaluators.BaseEvaluator import OneReturnValueEvaluator
|
||||||
from parsers.BaseNodeParser import ConceptNode
|
from parsers.BaseNodeParser import ConceptNode
|
||||||
from parsers.BaseParser import BaseParser
|
from parsers.BaseParser import BaseParser
|
||||||
@@ -73,8 +74,7 @@ class ValidateConceptEvaluator(OneReturnValueEvaluator):
|
|||||||
|
|
||||||
res = sheerka.evaluate_concept(context,
|
res = sheerka.evaluate_concept(context,
|
||||||
concept,
|
concept,
|
||||||
eval_body=False,
|
hints=EvaluationHints(eval_body=True, expression_only=True),
|
||||||
validation_only=True,
|
|
||||||
metadata=["variables", ConceptParts.PRE, ConceptParts.WHERE])
|
metadata=["variables", ConceptParts.PRE, ConceptParts.WHERE])
|
||||||
|
|
||||||
# either the 'pre' or the 'where' condition is not fulfilled
|
# either the 'pre' or the 'where' condition is not fulfilled
|
||||||
|
|||||||
@@ -323,7 +323,7 @@ class BaseCustomGrammarParser(BaseParserInputParser):
|
|||||||
keywords_found.add(token.value)
|
keywords_found.add(token.value)
|
||||||
colon_mode_activated = self.parser_input.the_token_after().type == TokenKind.COLON
|
colon_mode_activated = self.parser_input.the_token_after().type == TokenKind.COLON
|
||||||
if not self.parser_input.next_token():
|
if not self.parser_input.next_token():
|
||||||
self.add_error(UnexpectedEofParsingError(f"While parsing keyword '{keyword.value}'."))
|
self.add_error(UnexpectedEofParsingError(f"while parsing keyword '{keyword.value}'"))
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
buffer.append(token)
|
buffer.append(token)
|
||||||
|
|||||||
@@ -4,11 +4,26 @@ from typing import List, Union
|
|||||||
|
|
||||||
from core.builtin_concepts_ids import BuiltinConcepts
|
from core.builtin_concepts_ids import BuiltinConcepts
|
||||||
from core.sheerka.services.SheerkaExecute import ParserInput
|
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||||
from core.tokenizer import Token, TokenKind, Tokenizer, LexerError
|
from core.tokenizer import LexerError, Token, TokenKind, Tokenizer
|
||||||
from core.utils import tokens_are_matching, get_text_from_tokens
|
from core.utils import get_text_from_tokens, tokens_are_matching
|
||||||
from parsers.BaseNodeParser import UnrecognizedTokensNode
|
from parsers.BaseNodeParser import UnrecognizedTokensNode
|
||||||
from parsers.BaseParser import Node, ParsingError, BaseParser, ErrorSink, UnexpectedTokenParsingError
|
from parsers.BaseParser import BaseParser, ErrorSink, Node, ParsingError, UnexpectedTokenParsingError
|
||||||
|
|
||||||
|
open_parenthesis_mapping = {
|
||||||
|
TokenKind.LBRACKET: Token(TokenKind.LBRACKET, "[", -1, -1, -1),
|
||||||
|
TokenKind.LPAR: Token(TokenKind.LPAR, "(", -1, -1, -1),
|
||||||
|
TokenKind.LBRACE: Token(TokenKind.LBRACE, "{", -1, -1, -1),
|
||||||
|
}
|
||||||
|
|
||||||
|
end_parenthesis_mapping = {
|
||||||
|
TokenKind.LBRACKET: Token(TokenKind.RBRACKET, "]", -1, -1, -1),
|
||||||
|
TokenKind.LPAR: Token(TokenKind.RPAR, ")", -1, -1, -1),
|
||||||
|
TokenKind.LBRACE: Token(TokenKind.RBRACE, "}", -1, -1, -1),
|
||||||
|
}
|
||||||
|
|
||||||
|
end_parenthesis_types = {TokenKind.RBRACKET, TokenKind.RPAR, TokenKind.RBRACE}
|
||||||
|
|
||||||
|
comma = Token(TokenKind.COMMA, ",", -1, -1, -1)
|
||||||
|
|
||||||
class ComparisonType:
|
class ComparisonType:
|
||||||
EQUALS = "EQ"
|
EQUALS = "EQ"
|
||||||
@@ -26,7 +41,14 @@ class LeftPartNotFoundError(ParsingError):
|
|||||||
"""
|
"""
|
||||||
When the expression starts with 'or' or 'and'
|
When the expression starts with 'or' or 'and'
|
||||||
"""
|
"""
|
||||||
pass
|
keyword: str
|
||||||
|
pos: int
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"LeftPartNotFoundError(keyword={self.keyword}, pos={self.pos})"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"There is not left part to '{self.keyword}' at position {self.pos}"
|
||||||
|
|
||||||
|
|
||||||
@dataclass()
|
@dataclass()
|
||||||
@@ -390,6 +412,67 @@ class FunctionNode(ExprNode):
|
|||||||
return f"{self.first} {self.parameters} {self.last}"
|
return f"{self.first} {self.parameters} {self.last}"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass()
|
||||||
|
class Comprehension:
|
||||||
|
target: ExprNode
|
||||||
|
iterable: ExprNode
|
||||||
|
if_expr: ExprNode
|
||||||
|
|
||||||
|
|
||||||
|
class ListComprehensionNode(ExprNode):
|
||||||
|
def __init__(self, start, end, tokens, element: ExprNode, generators: List[Comprehension]):
|
||||||
|
super().__init__(start, end, tokens)
|
||||||
|
self.element = element
|
||||||
|
self.generators = generators
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if id(self) == id(other):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if not isinstance(other, ListComprehensionNode):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not super().__eq__(other):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return self.element == other.element and self.generators == other.generators
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
msg = f"ListComprehensionNode(start={self.start}, end={self.end}, element='{self.element}', generators="
|
||||||
|
for comp in self.generators:
|
||||||
|
msg += f"['{comp.target}', {comp.iterable}, '{comp.if_expr}'], "
|
||||||
|
return msg + ")"
|
||||||
|
|
||||||
|
|
||||||
|
class ListNode(ExprNode):
|
||||||
|
def __init__(self, start, end, tokens, first, last, items: List[ExprNode], sep=None):
|
||||||
|
super().__init__(start, end, tokens)
|
||||||
|
self.first = first
|
||||||
|
self.last = last
|
||||||
|
self.items = items
|
||||||
|
self.sep = sep or comma
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if id(self) == id(other):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if not isinstance(other, ListNode):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not super().__eq__(other):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return (self.first == other.first and
|
||||||
|
self.last == other.last and
|
||||||
|
self.items == other.items and
|
||||||
|
self.sep == other.sep)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
msg = f"ListNode(start={self.start}, end={self.end}, sep={self.sep}"
|
||||||
|
msg += f"first='{self.first}', last='{self.last}', items={self.items})"
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
class BaseExpressionParser(BaseParser):
|
class BaseExpressionParser(BaseParser):
|
||||||
|
|
||||||
def reset_parser_input(self, parser_input: ParserInput, error_sink):
|
def reset_parser_input(self, parser_input: ParserInput, error_sink):
|
||||||
@@ -432,12 +515,15 @@ class BaseExpressionParser(BaseParser):
|
|||||||
# The node is compiled in ExpressionParser.parse() or FunctionParser.parse(), depending of the requirement
|
# The node is compiled in ExpressionParser.parse() or FunctionParser.parse(), depending of the requirement
|
||||||
node = self.parse_input(context, parser_input, error_sink)
|
node = self.parse_input(context, parser_input, error_sink)
|
||||||
|
|
||||||
|
if not error_sink.has_error:
|
||||||
token = parser_input.token
|
token = parser_input.token
|
||||||
if token and token.type != TokenKind.EOF:
|
if token and token.type != TokenKind.EOF:
|
||||||
if token.type == TokenKind.RPAR:
|
if token.type == TokenKind.RPAR:
|
||||||
error_sink.add_error(ParenthesisMismatchError(token))
|
error_sink.add_error(ParenthesisMismatchError(token))
|
||||||
else:
|
else:
|
||||||
error_sink.add_error(UnexpectedTokenParsingError(f"Unexpected token '{token}'", token, [TokenKind.EOF]))
|
error_sink.add_error(UnexpectedTokenParsingError(f"Unexpected token '{token}'",
|
||||||
|
token,
|
||||||
|
[TokenKind.EOF]))
|
||||||
|
|
||||||
if isinstance(node, ParenthesisNode):
|
if isinstance(node, ParenthesisNode):
|
||||||
node = node.node
|
node = node.node
|
||||||
@@ -454,17 +540,16 @@ class BaseExpressionParser(BaseParser):
|
|||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def parse_input(self, context, parser_input: ParserInput, error_sink: list):
|
def parse_input(self, context, parser_input: ParserInput, error_sink: ErrorSink):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def parse_tokens_stop_condition(self, token, parser_input):
|
def parse_tokens_stop_condition(self, token, parser_input):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def parse_tokens(self, context, parser_input, error_sink):
|
def parse_tokens(self, context, parser_input, error_sink, stop_condition, expr_parser):
|
||||||
def stop():
|
def stop():
|
||||||
return token.type == TokenKind.EOF or \
|
return token.type == TokenKind.EOF or \
|
||||||
paren_count == 0 and (token.type == TokenKind.RPAR or
|
paren_count == 0 and (token.type == TokenKind.RPAR or stop_condition(token, parser_input))
|
||||||
self.parse_tokens_stop_condition(token, parser_input))
|
|
||||||
|
|
||||||
token = parser_input.token
|
token = parser_input.token
|
||||||
if token.type == TokenKind.EOF:
|
if token.type == TokenKind.EOF:
|
||||||
@@ -474,6 +559,10 @@ class BaseExpressionParser(BaseParser):
|
|||||||
last_paren = token
|
last_paren = token
|
||||||
start = parser_input.pos
|
start = parser_input.pos
|
||||||
parser_input.next_token()
|
parser_input.next_token()
|
||||||
|
# KSI 2021-09-01. I am quite sure I need a specific parser to parse inside the parenthesis
|
||||||
|
# if so, add a inside_parenthesis_parser to the list of this function parameter
|
||||||
|
# parser_to_use = inside_parenthesis_parser or self
|
||||||
|
# expr = parser_to_use.parse_input(context, parser_input, error_sink)
|
||||||
expr = self.parse_input(context, parser_input, error_sink)
|
expr = self.parse_input(context, parser_input, error_sink)
|
||||||
token = parser_input.token
|
token = parser_input.token
|
||||||
if token.type != TokenKind.RPAR:
|
if token.type != TokenKind.RPAR:
|
||||||
@@ -491,10 +580,10 @@ class BaseExpressionParser(BaseParser):
|
|||||||
while not stop():
|
while not stop():
|
||||||
last_is_whitespace = token.type == TokenKind.WHITESPACE
|
last_is_whitespace = token.type == TokenKind.WHITESPACE
|
||||||
end += 1
|
end += 1
|
||||||
if token.type == TokenKind.LPAR:
|
if token.type in (TokenKind.LPAR, TokenKind.LBRACKET, TokenKind.LBRACE):
|
||||||
last_paren = token
|
last_paren = token
|
||||||
paren_count += 1
|
paren_count += 1
|
||||||
if token.type == TokenKind.RPAR:
|
if token.type in (TokenKind.RPAR, TokenKind.RBRACKET, TokenKind.RBRACE):
|
||||||
paren_count -= 1
|
paren_count -= 1
|
||||||
parser_input.next_token(False)
|
parser_input.next_token(False)
|
||||||
token = parser_input.token
|
token = parser_input.token
|
||||||
@@ -503,15 +592,15 @@ class BaseExpressionParser(BaseParser):
|
|||||||
end -= 1
|
end -= 1
|
||||||
|
|
||||||
if start == end:
|
if start == end:
|
||||||
if token.type != TokenKind.RPAR:
|
|
||||||
error_sink.add_error(LeftPartNotFoundError())
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if paren_count != 0:
|
if paren_count > 0:
|
||||||
|
# Only if the count is > 0 as the left parenthesis may have occurred before the parse_tokens()
|
||||||
error_sink.add_error(ParenthesisMismatchError(last_paren))
|
error_sink.add_error(ParenthesisMismatchError(last_paren))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if self.expr_parser:
|
if expr_parser:
|
||||||
|
# to sub parse (parse with more fineness)
|
||||||
new_parsing_input = ParserInput(
|
new_parsing_input = ParserInput(
|
||||||
None,
|
None,
|
||||||
tokens=parser_input.tokens,
|
tokens=parser_input.tokens,
|
||||||
@@ -520,14 +609,14 @@ class BaseExpressionParser(BaseParser):
|
|||||||
end=end - 1,
|
end=end - 1,
|
||||||
yield_oef=False).reset()
|
yield_oef=False).reset()
|
||||||
new_parsing_input.next_token()
|
new_parsing_input.next_token()
|
||||||
return self.expr_parser.parse_input(context, new_parsing_input, error_sink)
|
return expr_parser.parse_input(context, new_parsing_input, error_sink)
|
||||||
else:
|
else:
|
||||||
return NameExprNode(start, end - 1, parser_input.tokens[start:end])
|
return NameExprNode(start, end - 1, parser_input.tokens[start:end])
|
||||||
|
|
||||||
|
|
||||||
class ExpressionVisitor:
|
class ExpressionVisitor:
|
||||||
"""
|
"""
|
||||||
Pyhtonic implementation of visitors for ExprNode
|
Pythonic implementation of visitors for ExprNode
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def visit(self, expr_node):
|
def visit(self, expr_node):
|
||||||
@@ -548,6 +637,29 @@ class ExpressionVisitor:
|
|||||||
self.visit(value)
|
self.visit(value)
|
||||||
|
|
||||||
|
|
||||||
|
class ExpressionVisitorWithHint:
|
||||||
|
"""
|
||||||
|
Pythonic implementation of visitors for ExprNode
|
||||||
|
"""
|
||||||
|
|
||||||
|
def visit(self, expr_node, hint):
|
||||||
|
name = expr_node.__class__.__name__
|
||||||
|
|
||||||
|
method = 'visit_' + name
|
||||||
|
visitor = getattr(self, method, self.generic_visit)
|
||||||
|
return visitor(expr_node, hint)
|
||||||
|
|
||||||
|
def generic_visit(self, expr_node, hint):
|
||||||
|
"""Called if no explicit visitor function exists for a node."""
|
||||||
|
for field, value in expr_node.__dict__.items():
|
||||||
|
if isinstance(value, (list, tuple)):
|
||||||
|
for item in value:
|
||||||
|
if isinstance(item, ExprNode):
|
||||||
|
self.visit(item, hint)
|
||||||
|
elif isinstance(value, ExprNode):
|
||||||
|
self.visit(value, hint)
|
||||||
|
|
||||||
|
|
||||||
class TrueifyVisitor(ExpressionVisitor):
|
class TrueifyVisitor(ExpressionVisitor):
|
||||||
"""
|
"""
|
||||||
Visit an ExprNode
|
Visit an ExprNode
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
import core.utils
|
import core.utils
|
||||||
from core.tokenizer import TokenKind, Token
|
from cache.FastCache import FastCache
|
||||||
|
from core import builtin_helpers
|
||||||
|
from core.sheerka.ExecutionContext import ExecutionContext
|
||||||
|
from core.tokenizer import Token, TokenKind
|
||||||
from core.var_ref import VariableRef
|
from core.var_ref import VariableRef
|
||||||
from parsers.BaseParser import Node, ParsingError, BaseParserInputParser
|
from parsers.BaseParser import BaseParserInputParser, Node, ParsingError
|
||||||
|
|
||||||
DEBUG_COMPILED = True
|
DEBUG_COMPILED = True
|
||||||
|
|
||||||
@@ -411,6 +414,13 @@ class SourceCodeWithConceptNode(LexerNode):
|
|||||||
def get_source_to_parse(self):
|
def get_source_to_parse(self):
|
||||||
return self.python_node.source
|
return self.python_node.source
|
||||||
|
|
||||||
|
def get_errors(self):
|
||||||
|
errors = []
|
||||||
|
for n in self.nodes:
|
||||||
|
if hasattr(n, "error") and n.error:
|
||||||
|
errors.append(n.error)
|
||||||
|
return errors
|
||||||
|
|
||||||
|
|
||||||
class VariableNode(LexerNode):
|
class VariableNode(LexerNode):
|
||||||
"""
|
"""
|
||||||
@@ -460,13 +470,52 @@ class NoMatchingTokenError(ParsingError):
|
|||||||
pos: int
|
pos: int
|
||||||
|
|
||||||
|
|
||||||
|
class UnrecognizedTokensCache:
|
||||||
|
def __init__(self, parsers):
|
||||||
|
self.parsers = parsers
|
||||||
|
self.cache = FastCache()
|
||||||
|
self.hits = 0
|
||||||
|
self.calls = 0
|
||||||
|
|
||||||
|
def get_lexer_nodes_from_unrecognized(self,
|
||||||
|
context: ExecutionContext,
|
||||||
|
unrecognized_tokens: UnrecognizedTokensNode):
|
||||||
|
to_request = unrecognized_tokens.source
|
||||||
|
self.calls += 1
|
||||||
|
if to_request in self.cache:
|
||||||
|
self.hits += 1
|
||||||
|
return self.cache.get(to_request)
|
||||||
|
|
||||||
|
res = builtin_helpers.get_lexer_nodes_from_unrecognized(context,
|
||||||
|
unrecognized_tokens,
|
||||||
|
self.parsers)
|
||||||
|
self.cache.put(to_request, res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ratio(self):
|
||||||
|
return self.hits / self.calls if self.calls else 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def calls_details(self):
|
||||||
|
return self.cache.calls
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {
|
||||||
|
"calls": self.calls,
|
||||||
|
"hits": self.hits,
|
||||||
|
"ratio": self.ratio,
|
||||||
|
"calls_details": self.calls_details,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class BaseNodeParser(BaseParserInputParser):
|
class BaseNodeParser(BaseParserInputParser):
|
||||||
"""
|
"""
|
||||||
Parser that return LexerNode
|
Parser that return LexerNode
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, priority, **kwargs):
|
def __init__(self, name, priority, enabled=True, **kwargs):
|
||||||
super().__init__(name, priority, yield_eof=True)
|
super().__init__(name, priority, yield_eof=True, enabled=enabled)
|
||||||
|
|
||||||
def init_from_concepts(self, context, concepts, **kwargs):
|
def init_from_concepts(self, context, concepts, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ class UnexpectedTokenParsingError(ParsingError):
|
|||||||
class UnexpectedEofParsingError(ParsingError):
|
class UnexpectedEofParsingError(ParsingError):
|
||||||
message: str = None
|
message: str = None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"UnexpectedEofParsingError({self.message})"
|
||||||
|
|
||||||
|
|
||||||
class BaseParser:
|
class BaseParser:
|
||||||
PREFIX = "parsers."
|
PREFIX = "parsers."
|
||||||
|
|||||||
@@ -240,31 +240,31 @@ class BnfDefinitionParser(BaseParser):
|
|||||||
self.add_error(concept)
|
self.add_error(concept)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
expr = ConceptExpression(concept, rule_name=concept.name)
|
expr = ConceptExpression(concept, rule_name=concept.key)
|
||||||
return self.eat_rule_name_if_needed(expr)
|
return self.eat_rule_name_if_needed(expr)
|
||||||
|
|
||||||
if token.type in (TokenKind.IDENTIFIER, TokenKind.KEYWORD):
|
if token.type in (TokenKind.IDENTIFIER, TokenKind.KEYWORD):
|
||||||
self.next_token()
|
self.next_token()
|
||||||
|
|
||||||
concept_name = token.str_value
|
concept_key = token.str_value
|
||||||
|
|
||||||
# we are trying to match against a concept which is still under construction !
|
# we are trying to match against a concept which is still under construction !
|
||||||
# (for example of recursive bnf definition)
|
# (for example of recursive bnf definition)
|
||||||
if self.context.obj and hasattr(self.context.obj, "name"):
|
if self.context.obj and hasattr(self.context.obj, "name"):
|
||||||
if concept_name == str(self.context.obj.name):
|
if concept_key == str(self.context.obj.name):
|
||||||
return self.eat_rule_name_if_needed(ConceptExpression(concept_name)) # 2021-02-17 no rule name ?
|
return self.eat_rule_name_if_needed(ConceptExpression(concept_key)) # 2021-02-17 no rule name ?
|
||||||
|
|
||||||
concept = self.context.get_concept(concept_name)
|
concept = self.context.get_concept(concept_key)
|
||||||
if not self.sheerka.is_known(concept):
|
if not self.sheerka.is_known(concept):
|
||||||
expr = VariableExpression(concept_name)
|
expr = VariableExpression(concept_key)
|
||||||
return self.eat_rule_name_if_needed(expr)
|
return self.eat_rule_name_if_needed(expr)
|
||||||
elif hasattr(concept, "__iter__"):
|
elif hasattr(concept, "__iter__"):
|
||||||
self.add_error(
|
self.add_error(
|
||||||
self.sheerka.new(BuiltinConcepts.CANNOT_RESOLVE_CONCEPT,
|
self.sheerka.new(BuiltinConcepts.CANNOT_RESOLVE_CONCEPT,
|
||||||
body=("key", concept_name)))
|
body=("key", concept_key)))
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
expr = ConceptExpression(concept, rule_name=concept.name)
|
expr = ConceptExpression(concept, rule_name=concept.key)
|
||||||
return self.eat_rule_name_if_needed(expr)
|
return self.eat_rule_name_if_needed(expr)
|
||||||
|
|
||||||
if token.type == TokenKind.STRING:
|
if token.type == TokenKind.STRING:
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ import core.builtin_helpers
|
|||||||
import core.utils
|
import core.utils
|
||||||
from cache.Cache import Cache
|
from cache.Cache import Cache
|
||||||
from core.builtin_concepts import BuiltinConcepts
|
from core.builtin_concepts import BuiltinConcepts
|
||||||
from core.concept import DEFINITION_TYPE_BNF, DoNotResolve, ConceptParts, Concept
|
from core.concept import Concept, ConceptParts, DEFINITION_TYPE_BNF, DoNotResolve
|
||||||
from core.global_symbols import NotFound
|
from core.global_symbols import NotFound
|
||||||
from core.sheerka.services.SheerkaExecute import ParserInput
|
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||||
from core.tokenizer import Tokenizer, TokenKind, Token
|
from core.tokenizer import Token, TokenKind, Tokenizer
|
||||||
from core.utils import CONSOLE_COLORS_MAP as CCM
|
from core.utils import CONSOLE_COLORS_MAP as CCM
|
||||||
from parsers.BaseNodeParser import BaseNodeParser, GrammarErrorNode, UnrecognizedTokensNode, ConceptNode, \
|
from parsers.BaseNodeParser import BaseNodeParser, ConceptNode, GrammarErrorNode, NoMatchingTokenError, RuleNode, \
|
||||||
NoMatchingTokenError, RuleNode, SourceCodeNode, SourceCodeWithConceptNode
|
SourceCodeNode, SourceCodeWithConceptNode, UnrecognizedTokensCache, UnrecognizedTokensNode
|
||||||
|
|
||||||
PARSERS = ["Sequence", "Sya", "Python"]
|
PARSERS = ["Sequence", "Sya", "Python"]
|
||||||
VARIABLE_EXPR_PARSER = ["Sequence", "Sya", "Python", "Bnf"]
|
VARIABLE_EXPR_PARSER = ["Sequence", "Sya", "Python", "Bnf"]
|
||||||
@@ -618,9 +618,8 @@ class VariableExpression(ParsingExpression):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
utn = UnrecognizedTokensNode(start, end, tokens)
|
utn = UnrecognizedTokensNode(start, end, tokens)
|
||||||
nodes_sequences = core.builtin_helpers.get_lexer_nodes_from_unrecognized(parser_helper.parser.context,
|
nodes_sequences = parser_helper.parser.cache2.get_lexer_nodes_from_unrecognized(parser_helper.parser.context,
|
||||||
utn,
|
utn)
|
||||||
VARIABLE_EXPR_PARSER)
|
|
||||||
return nodes_sequences
|
return nodes_sequences
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -1264,8 +1263,8 @@ class BnfConceptParserHelper:
|
|||||||
self.pos = -1
|
self.pos = -1
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
concepts = [item.concept if isinstance(item, ConceptNode) else "***" for item in self.sequence]
|
nodes = core.builtin_helpers.debug_nodes(self.sequence)
|
||||||
return f"BnfConceptParserHelper({concepts})"
|
return f"BnfConceptParserHelper({nodes})"
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if id(self) == id(other):
|
if id(self) == id(other):
|
||||||
@@ -1443,9 +1442,8 @@ class BnfConceptParserHelper:
|
|||||||
self.unrecognized_tokens.fix_source()
|
self.unrecognized_tokens.fix_source()
|
||||||
|
|
||||||
# try to recognize concepts
|
# try to recognize concepts
|
||||||
nodes_sequences = core.builtin_helpers.get_lexer_nodes_from_unrecognized(self.parser.context,
|
nodes_sequences = self.parser.cache.get_lexer_nodes_from_unrecognized(self.parser.context,
|
||||||
self.unrecognized_tokens,
|
self.unrecognized_tokens)
|
||||||
PARSERS)
|
|
||||||
|
|
||||||
if nodes_sequences:
|
if nodes_sequences:
|
||||||
instances = [self]
|
instances = [self]
|
||||||
@@ -1638,6 +1636,8 @@ class BnfNodeParser(BaseNodeParser):
|
|||||||
else:
|
else:
|
||||||
self.concepts_grammars = Cache()
|
self.concepts_grammars = Cache()
|
||||||
|
|
||||||
|
self.cache = UnrecognizedTokensCache(PARSERS)
|
||||||
|
self.cache2 = UnrecognizedTokensCache(VARIABLE_EXPR_PARSER)
|
||||||
self.ignore_case = True
|
self.ignore_case = True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -1963,7 +1963,7 @@ class BnfNodeParser(BaseNodeParser):
|
|||||||
|
|
||||||
nodes = []
|
nodes = []
|
||||||
for c in valid_concepts:
|
for c in valid_concepts:
|
||||||
nodes.append(ConceptExpression(c, rule_name=c.name))
|
nodes.append(ConceptExpression(c, rule_name=c.key))
|
||||||
|
|
||||||
resolved = self.resolve_parsing_expression(ssc,
|
resolved = self.resolve_parsing_expression(ssc,
|
||||||
UnOrderedChoice(*nodes),
|
UnOrderedChoice(*nodes),
|
||||||
@@ -2116,6 +2116,12 @@ class BnfNodeParser(BaseNodeParser):
|
|||||||
|
|
||||||
sequences = self.get_concepts_sequences(context)
|
sequences = self.get_concepts_sequences(context)
|
||||||
valid_parser_helpers = self.get_valid(sequences)
|
valid_parser_helpers = self.get_valid(sequences)
|
||||||
|
|
||||||
|
debugger = context.get_debugger(self.NAME, "parse")
|
||||||
|
if debugger.is_enabled:
|
||||||
|
debugger.debug_var("stats", self.cache.to_dict())
|
||||||
|
#debugger.debug_var("stats", self.cache2.to_dict())
|
||||||
|
|
||||||
if valid_parser_helpers is None:
|
if valid_parser_helpers is None:
|
||||||
return self.sheerka.ret(
|
return self.sheerka.ret(
|
||||||
self.name,
|
self.name,
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ class DefRuleParser(BaseCustomGrammarParser):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
if not self.parser_input.next_token(): # eat as
|
if not self.parser_input.next_token(): # eat as
|
||||||
self.add_error(UnexpectedEofParsingError("While parsing 'when'."))
|
self.add_error(UnexpectedEofParsingError("while parsing 'when'"))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
rule = self.parse_rule()
|
rule = self.parse_rule()
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ class FunctionParser(BaseExpressionParser):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
if not parser_input.next_token():
|
if not parser_input.next_token():
|
||||||
error_sink.add_error(UnexpectedEofParsingError(f"Unexpected EOF while parsing left parenthesis"))
|
error_sink.add_error(UnexpectedEofParsingError(f"while parsing left parenthesis"))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
token = parser_input.token
|
token = parser_input.token
|
||||||
@@ -110,7 +110,7 @@ class FunctionParser(BaseExpressionParser):
|
|||||||
|
|
||||||
start_node = NameExprNode(start, start + 1, parser_input.tokens[start:start + 2])
|
start_node = NameExprNode(start, start + 1, parser_input.tokens[start:start + 2])
|
||||||
if not parser_input.next_token():
|
if not parser_input.next_token():
|
||||||
error_sink.add_error(UnexpectedEofParsingError(f"Unexpected EOF after left parenthesis"))
|
error_sink.add_error(UnexpectedEofParsingError(f"after left parenthesis"))
|
||||||
return FunctionNode(start, start + 1, [], start_node, None, None)
|
return FunctionNode(start, start + 1, [], start_node, None, None)
|
||||||
|
|
||||||
params = self.parse_parameters(context, parser_input, error_sink)
|
params = self.parse_parameters(context, parser_input, error_sink)
|
||||||
@@ -146,7 +146,7 @@ class FunctionParser(BaseExpressionParser):
|
|||||||
|
|
||||||
token = parser_input.token
|
token = parser_input.token
|
||||||
if token.type == TokenKind.EOF:
|
if token.type == TokenKind.EOF:
|
||||||
error_sink.add_error(UnexpectedEofParsingError(f"Unexpected EOF while parsing parameters"))
|
error_sink.add_error(UnexpectedEofParsingError(f"while parsing parameters"))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if token.type == TokenKind.RPAR:
|
if token.type == TokenKind.RPAR:
|
||||||
@@ -173,7 +173,7 @@ class FunctionParser(BaseExpressionParser):
|
|||||||
|
|
||||||
# otherwise, eat until LPAR or separator
|
# otherwise, eat until LPAR or separator
|
||||||
parser_input.seek(start_pos)
|
parser_input.seek(start_pos)
|
||||||
return self.parse_tokens(context, parser_input, error_sink)
|
return self.parse_tokens(context, parser_input, error_sink, self.parse_tokens_stop_condition, self.expr_parser)
|
||||||
|
|
||||||
def parse_tokens_stop_condition(self, token, parser_input):
|
def parse_tokens_stop_condition(self, token, parser_input):
|
||||||
return token.value == self.sep
|
return token.value == self.sep
|
||||||
|
|||||||
@@ -0,0 +1,219 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||||
|
from core.tokenizer import TokenKind
|
||||||
|
from parsers.BaseExpressionParser import BaseExpressionParser, Comprehension, ListComprehensionNode, \
|
||||||
|
ParenthesisMismatchError, end_parenthesis_mapping, end_parenthesis_types
|
||||||
|
from parsers.BaseParser import ErrorSink, ParsingError, UnexpectedEofParsingError
|
||||||
|
from parsers.ListParser import ListParser
|
||||||
|
from sheerkapython.ExprToPython import PythonExprVisitor
|
||||||
|
|
||||||
|
|
||||||
|
class LeadingParenthesisNotFound(ParsingError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FailedToParse(ParsingError):
|
||||||
|
part: str # part that fails ('element', 'targets', 'generator' or 'if'
|
||||||
|
pos: int # position after which the element was not found
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Failed to find <{self.part}> after position {self.pos}"
|
||||||
|
|
||||||
|
|
||||||
|
class ForNotFound(ParsingError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ElementNotFound(ParsingError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ListComprehensionParser(BaseExpressionParser):
|
||||||
|
NAME = "ListComprehension"
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(self.NAME, 55, yield_eof=True)
|
||||||
|
self.expr_parser = kwargs.get("expr_parser", None)
|
||||||
|
self.auto_compile = kwargs.get("auto_compile", True)
|
||||||
|
self.element_parser = ListParser()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def stop_condition(keywords, end_parenthesis=None):
|
||||||
|
if end_parenthesis is None:
|
||||||
|
return lambda t, pi: t.type == TokenKind.IDENTIFIER and t.value in keywords
|
||||||
|
else:
|
||||||
|
return lambda t, pi: t.type == TokenKind.IDENTIFIER and t.value in keywords or t.type in end_parenthesis
|
||||||
|
|
||||||
|
def parse_input(self, context, parser_input, error_sink):
|
||||||
|
return self.parse_list_comprehension(context, parser_input, error_sink)
|
||||||
|
|
||||||
|
def parse_list_comprehension(self, context, parser_input, error_sink):
|
||||||
|
start_pos = parser_input.pos
|
||||||
|
|
||||||
|
# first define the leading parenthesis / bracket / brace
|
||||||
|
start_parenthesis = parser_input.token
|
||||||
|
if start_parenthesis.type not in end_parenthesis_mapping:
|
||||||
|
error_sink.add_error(LeadingParenthesisNotFound())
|
||||||
|
return None
|
||||||
|
|
||||||
|
end_parenthesis = end_parenthesis_mapping[start_parenthesis.type]
|
||||||
|
|
||||||
|
if not parser_input.next_token():
|
||||||
|
error_sink.add_error(UnexpectedEofParsingError("when start parsing"))
|
||||||
|
return None
|
||||||
|
|
||||||
|
element_start_pos = parser_input.pos
|
||||||
|
|
||||||
|
# search the 'for' tokens. They will be the pivot for our parsing
|
||||||
|
for_tokens_positions = self.get_for_positions(parser_input)
|
||||||
|
if not for_tokens_positions:
|
||||||
|
error_sink.add_error(ForNotFound())
|
||||||
|
return None
|
||||||
|
|
||||||
|
comprehensions = []
|
||||||
|
sub_error_sink = ErrorSink()
|
||||||
|
for start, end in reversed(for_tokens_positions):
|
||||||
|
sub_input = parser_input.sub_part(start, end, yield_oef=True)
|
||||||
|
sub_input.reset()
|
||||||
|
sub_input.next_token()
|
||||||
|
comprehension = self.parse_comprehension(context, sub_input, sub_error_sink, end_parenthesis.type)
|
||||||
|
|
||||||
|
if not comprehension:
|
||||||
|
element_end_pos = end
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
comprehensions.insert(0, comprehension)
|
||||||
|
else:
|
||||||
|
element_end_pos = start - 1
|
||||||
|
|
||||||
|
if not comprehensions:
|
||||||
|
error_sink.sink.extend(sub_error_sink.sink)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if element_end_pos < element_start_pos:
|
||||||
|
error_sink.add_error(ElementNotFound())
|
||||||
|
return None
|
||||||
|
|
||||||
|
sub_input = parser_input.sub_part(element_start_pos, element_end_pos, yield_oef=True)
|
||||||
|
sub_input.reset()
|
||||||
|
sub_input.next_token()
|
||||||
|
element_expr = self.element_parser.parse_input(context, sub_input, error_sink)
|
||||||
|
|
||||||
|
if not element_expr:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not parser_input.token.type == end_parenthesis.type:
|
||||||
|
error_sink.add_error(ParenthesisMismatchError(end_parenthesis))
|
||||||
|
return None
|
||||||
|
|
||||||
|
end_pos = parser_input.pos
|
||||||
|
parser_input.next_token()
|
||||||
|
|
||||||
|
return ListComprehensionNode(start_pos,
|
||||||
|
end_pos,
|
||||||
|
parser_input.tokens[start_pos: end_pos + 1],
|
||||||
|
element_expr,
|
||||||
|
comprehensions)
|
||||||
|
|
||||||
|
def parse_comprehension(self, context, parser_input, error_sink, end_parenthesis_type):
|
||||||
|
parser_input.next_token() # eat the leading 'for'
|
||||||
|
|
||||||
|
pos = parser_input.pos
|
||||||
|
target_expr = self.parse_tokens(context,
|
||||||
|
parser_input,
|
||||||
|
error_sink,
|
||||||
|
self.stop_condition(["in"], [end_parenthesis_type]),
|
||||||
|
None)
|
||||||
|
if not target_expr:
|
||||||
|
error_sink.add_error(FailedToParse('target', pos))
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not parser_input.next_token():
|
||||||
|
error_sink.add_error(UnexpectedEofParsingError("while parsing comprehension"))
|
||||||
|
return None
|
||||||
|
|
||||||
|
pos = parser_input.pos
|
||||||
|
generator_expr = self.parse_tokens(context,
|
||||||
|
parser_input,
|
||||||
|
error_sink,
|
||||||
|
self.stop_condition(["for", "if"], [end_parenthesis_type]),
|
||||||
|
None)
|
||||||
|
if not generator_expr:
|
||||||
|
error_sink.add_error(FailedToParse('generator', pos))
|
||||||
|
return None
|
||||||
|
|
||||||
|
token = parser_input.token
|
||||||
|
if token.value == "if":
|
||||||
|
if not parser_input.next_token():
|
||||||
|
error_sink.add_error(UnexpectedEofParsingError("while parsing comprehension"))
|
||||||
|
return None
|
||||||
|
|
||||||
|
pos = parser_input.pos
|
||||||
|
if_expr = self.parse_tokens(context,
|
||||||
|
parser_input,
|
||||||
|
error_sink,
|
||||||
|
self.stop_condition(["for"], [end_parenthesis_type]),
|
||||||
|
None)
|
||||||
|
|
||||||
|
if not if_expr:
|
||||||
|
error_sink.add_error(FailedToParse('if', pos))
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
if_expr = None
|
||||||
|
|
||||||
|
return Comprehension(target_expr, generator_expr, if_expr)
|
||||||
|
|
||||||
|
def parse_tokens_stop_condition(self, token, parser_input):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def parse(self, context, parser_input: ParserInput):
|
||||||
|
ret = super().parse(context, parser_input)
|
||||||
|
|
||||||
|
if not self.auto_compile:
|
||||||
|
return ret
|
||||||
|
|
||||||
|
if ret is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not ret.status:
|
||||||
|
return ret
|
||||||
|
|
||||||
|
node = ret.body.body
|
||||||
|
visitor = PythonExprVisitor(context)
|
||||||
|
|
||||||
|
ret = visitor.compile(node)
|
||||||
|
return ret[0] if len(ret) == 1 else ret
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_for_positions(parser_input):
|
||||||
|
"""
|
||||||
|
browse the tokens to get the starting and ending position of 'for' tokens
|
||||||
|
ex:
|
||||||
|
[ x, t for x in z if t for a in b ]
|
||||||
|
^ ^
|
||||||
|
will return
|
||||||
|
:param parser_input:
|
||||||
|
:return: list of tuple representing where the 'for' token starts and where it ends
|
||||||
|
"""
|
||||||
|
res = []
|
||||||
|
current_pos = None
|
||||||
|
nb_parenthesis = 1
|
||||||
|
while True:
|
||||||
|
if parser_input.token.value == "for":
|
||||||
|
if current_pos:
|
||||||
|
res.append((current_pos, parser_input.pos - 1))
|
||||||
|
current_pos = parser_input.pos
|
||||||
|
elif parser_input.token.type in end_parenthesis_mapping:
|
||||||
|
nb_parenthesis += 1
|
||||||
|
elif parser_input.token.type in end_parenthesis_types:
|
||||||
|
nb_parenthesis -= 1
|
||||||
|
|
||||||
|
if nb_parenthesis == 0 or not parser_input.next_token():
|
||||||
|
break
|
||||||
|
|
||||||
|
if current_pos:
|
||||||
|
res.append((current_pos, parser_input.pos - 1))
|
||||||
|
|
||||||
|
return res
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||||
|
from core.tokenizer import TokenKind
|
||||||
|
from parsers.BaseExpressionParser import BaseExpressionParser, ListNode, NameExprNode, ParenthesisMismatchError, \
|
||||||
|
comma, end_parenthesis_mapping
|
||||||
|
from parsers.BaseParser import ErrorSink
|
||||||
|
|
||||||
|
|
||||||
|
class ListParser(BaseExpressionParser):
|
||||||
|
NAME = "List"
|
||||||
|
|
||||||
|
def __init__(self, sep=None, **kwargs):
|
||||||
|
super().__init__(self.NAME, 50, False, yield_eof=True)
|
||||||
|
# KSI 2021-09-02 : The priority (50) is not set
|
||||||
|
self.sep = sep or comma
|
||||||
|
self.end_tokens = [self.sep] if sep else [self.sep]
|
||||||
|
self.expr_parser = kwargs.get("expr_parser", None)
|
||||||
|
|
||||||
|
def parse_input(self, context, parser_input: ParserInput, error_sink: ErrorSink):
|
||||||
|
start = parser_input.pos
|
||||||
|
token = parser_input.token
|
||||||
|
first = None
|
||||||
|
trailing_token = None
|
||||||
|
|
||||||
|
# define the opening parenthesis and set the expected closing parenthesis
|
||||||
|
if token.type in end_parenthesis_mapping:
|
||||||
|
trailing_token = end_parenthesis_mapping[token.type]
|
||||||
|
self.end_tokens.append(trailing_token)
|
||||||
|
first = NameExprNode(start, start, parser_input.tokens[start:start + 1])
|
||||||
|
|
||||||
|
parser_input.next_token()
|
||||||
|
|
||||||
|
token = parser_input.token
|
||||||
|
items = []
|
||||||
|
while token.type != TokenKind.EOF:
|
||||||
|
parsed = self.parse_tokens(context,
|
||||||
|
parser_input,
|
||||||
|
error_sink,
|
||||||
|
self.parse_tokens_stop_condition,
|
||||||
|
self.expr_parser)
|
||||||
|
|
||||||
|
if parsed:
|
||||||
|
items.append(parsed)
|
||||||
|
|
||||||
|
token = parser_input.token
|
||||||
|
if not parsed or error_sink.has_error or (token.type != self.sep.type or token.value != self.sep.value):
|
||||||
|
break
|
||||||
|
|
||||||
|
parser_input.next_token()
|
||||||
|
|
||||||
|
if error_sink.has_error:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# make sure the trailing parenthesis is present if required
|
||||||
|
if trailing_token:
|
||||||
|
if token.type == trailing_token.type:
|
||||||
|
pos = parser_input.pos
|
||||||
|
last = NameExprNode(pos, pos, parser_input.tokens[pos:pos + 1])
|
||||||
|
else:
|
||||||
|
last = None
|
||||||
|
error_sink.add_error(ParenthesisMismatchError(trailing_token))
|
||||||
|
else:
|
||||||
|
last = None
|
||||||
|
|
||||||
|
end = parser_input.pos if token.type != TokenKind.EOF else parser_input.pos - 1
|
||||||
|
if self.parse_tokens_stop_condition(token, None):
|
||||||
|
parser_input.next_token()
|
||||||
|
|
||||||
|
if first is None and last is None and len(items) == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return ListNode(start, end, parser_input.tokens[start:end + 1], first, last, items, self.sep)
|
||||||
|
|
||||||
|
def parse_tokens_stop_condition(self, token, parser_input):
|
||||||
|
for t in self.end_tokens:
|
||||||
|
if t.type == token.type and t.value == token.value:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
@@ -1,60 +1,7 @@
|
|||||||
from core.sheerka.services.SheerkaExecute import ParserInput
|
from core.tokenizer import TokenKind
|
||||||
from core.sheerka.services.sheerka_service import FailedToCompileError
|
from parsers.BaseExpressionParser import AndNode, BaseExpressionParser, LeftPartNotFoundError, NotNode, \
|
||||||
from core.tokenizer import TokenKind, Tokenizer
|
OrNode, ParenthesisNode
|
||||||
from core.utils import get_text_from_tokens
|
from parsers.BaseParser import UnexpectedEofParsingError
|
||||||
from parsers.BaseExpressionParser import ParenthesisNode, OrNode, AndNode, NotNode, VariableNode, \
|
|
||||||
ComparisonNode, BaseExpressionParser
|
|
||||||
from parsers.BaseParser import UnexpectedEofParsingError, ErrorSink
|
|
||||||
from sheerkarete.common import V
|
|
||||||
from sheerkarete.conditions import Condition, AndConditions
|
|
||||||
|
|
||||||
|
|
||||||
class ReteConditionsEmitter:
|
|
||||||
|
|
||||||
def __init__(self, context):
|
|
||||||
from parsers.RelationalOperatorParser import RelationalOperatorParser
|
|
||||||
self.context = context
|
|
||||||
self.comparison_parser = RelationalOperatorParser()
|
|
||||||
self.var_counter = 0
|
|
||||||
self.variables = {}
|
|
||||||
|
|
||||||
def add_variable(self, target):
|
|
||||||
var_name = f"__x_{self.var_counter:02}__"
|
|
||||||
self.var_counter += 1
|
|
||||||
self.variables[target] = var_name
|
|
||||||
return var_name
|
|
||||||
|
|
||||||
def init_variable_if_needed(self, node, res):
|
|
||||||
if node.name not in self.variables:
|
|
||||||
var_name = self.add_variable(node.name)
|
|
||||||
res.append(Condition(V(var_name), "__name__", node.name))
|
|
||||||
|
|
||||||
return V(self.variables[node.name])
|
|
||||||
|
|
||||||
def get_conditions(self, expr_nodes):
|
|
||||||
conditions = []
|
|
||||||
for expr_node in expr_nodes:
|
|
||||||
error_sink = ErrorSink()
|
|
||||||
parser_input = ParserInput(None, tokens=expr_node.tokens).reset()
|
|
||||||
parser_input.next_token()
|
|
||||||
|
|
||||||
parsed = self.comparison_parser.parse_input(self.context, parser_input, error_sink)
|
|
||||||
if error_sink.has_error:
|
|
||||||
raise FailedToCompileError(error_sink.sink)
|
|
||||||
|
|
||||||
if isinstance(parsed, VariableNode):
|
|
||||||
var_name = self.init_variable_if_needed(parsed, conditions)
|
|
||||||
if parsed.attributes_str is not None:
|
|
||||||
conditions.append(Condition(var_name, parsed.attributes_str, True))
|
|
||||||
|
|
||||||
elif isinstance(parsed, ComparisonNode):
|
|
||||||
if isinstance(parsed.left, VariableNode):
|
|
||||||
left = self.init_variable_if_needed(parsed.left, conditions)
|
|
||||||
attr = parsed.left.attributes_str or "__self__"
|
|
||||||
right = eval(get_text_from_tokens(parsed.right.tokens))
|
|
||||||
conditions.append(Condition(left, attr, right))
|
|
||||||
|
|
||||||
return [AndConditions(conditions)]
|
|
||||||
|
|
||||||
|
|
||||||
class LogicalOperatorParser(BaseExpressionParser):
|
class LogicalOperatorParser(BaseExpressionParser):
|
||||||
@@ -70,9 +17,6 @@ class LogicalOperatorParser(BaseExpressionParser):
|
|||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(self.NAME, 50, False, yield_eof=True)
|
super().__init__(self.NAME, 50, False, yield_eof=True)
|
||||||
self.and_tokens = list(Tokenizer(" and ", yield_eof=False))
|
|
||||||
self.and_not_tokens = list(Tokenizer(" and not ", yield_eof=False))
|
|
||||||
self.not_tokens = list(Tokenizer("not ", yield_eof=False))
|
|
||||||
self.expr_parser = kwargs.get("expr_parser", None)
|
self.expr_parser = kwargs.get("expr_parser", None)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -87,16 +31,20 @@ class LogicalOperatorParser(BaseExpressionParser):
|
|||||||
def parse_or(self, context, parser_input, error_sink):
|
def parse_or(self, context, parser_input, error_sink):
|
||||||
start = parser_input.pos
|
start = parser_input.pos
|
||||||
expr = self.parse_and(context, parser_input, error_sink)
|
expr = self.parse_and(context, parser_input, error_sink)
|
||||||
|
|
||||||
token = parser_input.token
|
token = parser_input.token
|
||||||
if token.type != TokenKind.IDENTIFIER or token.value != "or":
|
if token.type != TokenKind.IDENTIFIER or token.value != "or":
|
||||||
return expr
|
return expr
|
||||||
|
|
||||||
|
if expr is None:
|
||||||
|
error_sink.add_error(LeftPartNotFoundError("or", start))
|
||||||
|
|
||||||
parts = [expr]
|
parts = [expr]
|
||||||
while token.type == TokenKind.IDENTIFIER and token.value == "or":
|
while token.type == TokenKind.IDENTIFIER and token.value == "or":
|
||||||
parser_input.next_token()
|
parser_input.next_token()
|
||||||
expr = self.parse_and(context, parser_input, error_sink)
|
expr = self.parse_and(context, parser_input, error_sink)
|
||||||
if expr is None:
|
if expr is None:
|
||||||
error_sink.add_error(UnexpectedEofParsingError("When parsing 'or'"))
|
error_sink.add_error(UnexpectedEofParsingError("while parsing 'or'"))
|
||||||
end = parser_input.pos
|
end = parser_input.pos
|
||||||
self.clean_parenthesis_nodes(parts)
|
self.clean_parenthesis_nodes(parts)
|
||||||
return OrNode(start, end, parser_input.tokens[start: end + 1], *parts)
|
return OrNode(start, end, parser_input.tokens[start: end + 1], *parts)
|
||||||
@@ -110,16 +58,20 @@ class LogicalOperatorParser(BaseExpressionParser):
|
|||||||
def parse_and(self, context, parser_input, error_sink):
|
def parse_and(self, context, parser_input, error_sink):
|
||||||
start = parser_input.pos
|
start = parser_input.pos
|
||||||
expr = self.parse_not(context, parser_input, error_sink)
|
expr = self.parse_not(context, parser_input, error_sink)
|
||||||
|
|
||||||
token = parser_input.token
|
token = parser_input.token
|
||||||
if token.type != TokenKind.IDENTIFIER or token.value != "and":
|
if token.type != TokenKind.IDENTIFIER or token.value != "and":
|
||||||
return expr
|
return expr
|
||||||
|
|
||||||
|
if expr is None:
|
||||||
|
error_sink.add_error(LeftPartNotFoundError("and", start))
|
||||||
|
|
||||||
parts = [expr]
|
parts = [expr]
|
||||||
while token.type == TokenKind.IDENTIFIER and token.value == "and":
|
while token.type == TokenKind.IDENTIFIER and token.value == "and":
|
||||||
parser_input.next_token()
|
parser_input.next_token()
|
||||||
expr = self.parse_not(context, parser_input, error_sink)
|
expr = self.parse_not(context, parser_input, error_sink)
|
||||||
if expr is None:
|
if expr is None:
|
||||||
error_sink.add_error(UnexpectedEofParsingError("When parsing 'and'"))
|
error_sink.add_error(UnexpectedEofParsingError("while parsing 'and'"))
|
||||||
end = parser_input.pos
|
end = parser_input.pos
|
||||||
self.clean_parenthesis_nodes(parts)
|
self.clean_parenthesis_nodes(parts)
|
||||||
return AndNode(start, end, parser_input.tokens[start: end + 1], *parts)
|
return AndNode(start, end, parser_input.tokens[start: end + 1], *parts)
|
||||||
@@ -143,7 +95,11 @@ class LogicalOperatorParser(BaseExpressionParser):
|
|||||||
parser_input.tokens[start: parsed.end + 1],
|
parser_input.tokens[start: parsed.end + 1],
|
||||||
node)
|
node)
|
||||||
else:
|
else:
|
||||||
return self.parse_tokens(context, parser_input, error_sink)
|
return self.parse_tokens(context,
|
||||||
|
parser_input,
|
||||||
|
error_sink,
|
||||||
|
self.parse_tokens_stop_condition,
|
||||||
|
self.expr_parser)
|
||||||
|
|
||||||
def parse_tokens_stop_condition(self, token, parser_input):
|
def parse_tokens_stop_condition(self, token, parser_input):
|
||||||
return token.type == TokenKind.IDENTIFIER and token.value in ("and", "or") or \
|
return token.type == TokenKind.IDENTIFIER and token.value in ("and", "or") or \
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ class RelationalOperatorParser(BaseExpressionParser):
|
|||||||
|
|
||||||
def parse_compare(self, context, parser_input, error_sink):
|
def parse_compare(self, context, parser_input, error_sink):
|
||||||
start = parser_input.pos
|
start = parser_input.pos
|
||||||
left = self.parse_tokens(context, parser_input, error_sink)
|
left = self.parse_tokens(context, parser_input, error_sink, self.parse_tokens_stop_condition, self.expr_parser)
|
||||||
if left is None:
|
if left is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if (comp := self.eat_comparison(parser_input)) is None:
|
if (comp := self.eat_comparison(parser_input)) is None:
|
||||||
return left
|
return left
|
||||||
|
|
||||||
right = self.parse_tokens(context, parser_input, error_sink)
|
right = self.parse_tokens(context, parser_input, error_sink, self.parse_tokens_stop_condition, self.expr_parser)
|
||||||
|
|
||||||
if comp == ComparisonType.IN and not isinstance(right, ParenthesisNode):
|
if comp == ComparisonType.IN and not isinstance(right, ParenthesisNode):
|
||||||
t = right.tokens[0]
|
t = right.tokens[0]
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from core import builtin_helpers
|
|
||||||
from core.builtin_concepts import BuiltinConcepts
|
from core.builtin_concepts import BuiltinConcepts
|
||||||
from core.builtin_helpers import update_concepts_hints
|
from core.builtin_helpers import debug_nodes, update_concepts_hints
|
||||||
from core.concept import DEFINITION_TYPE_BNF, Concept
|
from core.concept import Concept, DEFINITION_TYPE_BNF
|
||||||
from core.global_symbols import NotFound
|
from core.global_symbols import NotFound
|
||||||
from core.sheerka.services.SheerkaExecute import ParserInput
|
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||||
from core.tokenizer import Tokenizer, TokenKind
|
from core.tokenizer import TokenKind, Tokenizer
|
||||||
from core.utils import strip_tokens, make_unique
|
from core.utils import make_unique, strip_tokens
|
||||||
from parsers.BaseNodeParser import BaseNodeParser, ConceptNode, UnrecognizedTokensNode, SourceCodeNode
|
from parsers.BaseNodeParser import BaseNodeParser, ConceptNode, SourceCodeNode, UnrecognizedTokensCache, \
|
||||||
from parsers.BaseParser import UnexpectedTokenParsingError, ParsingError
|
UnrecognizedTokensNode
|
||||||
|
from parsers.BaseParser import ParsingError, UnexpectedTokenParsingError
|
||||||
from parsers.BnfNodeParser import BnfNodeParser
|
from parsers.BnfNodeParser import BnfNodeParser
|
||||||
from parsers.SyaNodeParser import SyaNodeParser
|
from parsers.SyaNodeParser import SyaNodeParser
|
||||||
|
|
||||||
@@ -47,9 +47,10 @@ class TokensNodeFoundError(ParsingError):
|
|||||||
|
|
||||||
|
|
||||||
class AtomConceptParserHelper:
|
class AtomConceptParserHelper:
|
||||||
def __init__(self, context):
|
def __init__(self, parser):
|
||||||
|
|
||||||
self.context = context
|
self.parser = parser
|
||||||
|
self.context = parser.context
|
||||||
self.debug = []
|
self.debug = []
|
||||||
self.sequence = [] # sequence of concepts already found found
|
self.sequence = [] # sequence of concepts already found found
|
||||||
self.current_concept: ConceptNode = None # concept being parsed
|
self.current_concept: ConceptNode = None # concept being parsed
|
||||||
@@ -80,7 +81,7 @@ class AtomConceptParserHelper:
|
|||||||
return hash(len(self.sequence))
|
return hash(len(self.sequence))
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"{self.sequence}"
|
return f"{debug_nodes(self.sequence)}"
|
||||||
|
|
||||||
def lock(self):
|
def lock(self):
|
||||||
self.is_locked = True
|
self.is_locked = True
|
||||||
@@ -146,10 +147,7 @@ class AtomConceptParserHelper:
|
|||||||
self.unrecognized_tokens.fix_source()
|
self.unrecognized_tokens.fix_source()
|
||||||
|
|
||||||
# try to recognize concepts
|
# try to recognize concepts
|
||||||
nodes_sequences = builtin_helpers.get_lexer_nodes_from_unrecognized(
|
nodes_sequences = self.parser.cache.get_lexer_nodes_from_unrecognized(self.context, self.unrecognized_tokens)
|
||||||
self.context,
|
|
||||||
self.unrecognized_tokens,
|
|
||||||
PARSERS)
|
|
||||||
|
|
||||||
if nodes_sequences:
|
if nodes_sequences:
|
||||||
instances = [self]
|
instances = [self]
|
||||||
@@ -191,7 +189,7 @@ class AtomConceptParserHelper:
|
|||||||
self.errors.append(TokensNodeFoundError(self.expected_tokens))
|
self.errors.append(TokensNodeFoundError(self.expected_tokens))
|
||||||
|
|
||||||
def clone(self):
|
def clone(self):
|
||||||
clone = AtomConceptParserHelper(self.context)
|
clone = AtomConceptParserHelper(self.parser)
|
||||||
clone.debug = self.debug[:]
|
clone.debug = self.debug[:]
|
||||||
clone.sequence = self.sequence[:]
|
clone.sequence = self.sequence[:]
|
||||||
clone.current_concept = self.current_concept.clone() if self.current_concept else None
|
clone.current_concept = self.current_concept.clone() if self.current_concept else None
|
||||||
@@ -224,6 +222,7 @@ class SequenceNodeParser(BaseNodeParser):
|
|||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(SequenceNodeParser.NAME, 50, **kwargs)
|
super().__init__(SequenceNodeParser.NAME, 50, **kwargs)
|
||||||
|
self.cache = UnrecognizedTokensCache(PARSERS)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _is_eligible(concept):
|
def _is_eligible(concept):
|
||||||
@@ -278,7 +277,7 @@ class SequenceNodeParser(BaseNodeParser):
|
|||||||
concept_parser_helpers.extend(forked)
|
concept_parser_helpers.extend(forked)
|
||||||
forked.clear()
|
forked.clear()
|
||||||
|
|
||||||
concept_parser_helpers = [AtomConceptParserHelper(self.context)]
|
concept_parser_helpers = [AtomConceptParserHelper(self)]
|
||||||
|
|
||||||
while self.parser_input.next_token(False):
|
while self.parser_input.next_token(False):
|
||||||
for concept_parser in concept_parser_helpers:
|
for concept_parser in concept_parser_helpers:
|
||||||
@@ -355,7 +354,7 @@ class SequenceNodeParser(BaseNodeParser):
|
|||||||
res = []
|
res = []
|
||||||
start, end = self.get_tokens_boundaries(self.parser_input.as_tokens())
|
start, end = self.get_tokens_boundaries(self.parser_input.as_tokens())
|
||||||
for concept in concepts:
|
for concept in concepts:
|
||||||
parser_helper = AtomConceptParserHelper(None)
|
parser_helper = AtomConceptParserHelper(self)
|
||||||
parser_helper.sequence.append(ConceptNode(concept,
|
parser_helper.sequence.append(ConceptNode(concept,
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
@@ -419,6 +418,9 @@ class SequenceNodeParser(BaseNodeParser):
|
|||||||
False,
|
False,
|
||||||
context.sheerka.new(BuiltinConcepts.ERROR, body=self.error_sink))
|
context.sheerka.new(BuiltinConcepts.ERROR, body=self.error_sink))
|
||||||
|
|
||||||
|
debugger = context.get_debugger(self.NAME, "parse")
|
||||||
|
debugger.debug_entering(source=self.parser_input.as_text())
|
||||||
|
|
||||||
sequences = self.get_concepts_sequences()
|
sequences = self.get_concepts_sequences()
|
||||||
if by_name := self.get_by_name():
|
if by_name := self.get_by_name():
|
||||||
# note that concepts by names must be appended, not prepended
|
# note that concepts by names must be appended, not prepended
|
||||||
@@ -427,6 +429,10 @@ class SequenceNodeParser(BaseNodeParser):
|
|||||||
|
|
||||||
parser_helpers = self.get_valid(sequences)
|
parser_helpers = self.get_valid(sequences)
|
||||||
|
|
||||||
|
if debugger.is_enabled():
|
||||||
|
debugger.debug_var("stats", self.cache.to_dict())
|
||||||
|
debugger.debug_leaving(result=parser_helpers)
|
||||||
|
|
||||||
if len(parser_helpers):
|
if len(parser_helpers):
|
||||||
ret = []
|
ret = []
|
||||||
for parser_helper in parser_helpers:
|
for parser_helper in parser_helpers:
|
||||||
@@ -471,9 +477,19 @@ class SequenceNodeParser(BaseNodeParser):
|
|||||||
if not eligible:
|
if not eligible:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return [self.sheerka.new_dynamic(c, BuiltinConcepts.PLURAL, name=token.value, props={BuiltinConcepts.PLURAL: c})
|
plural_concepts = [self.sheerka.new_dynamic(c,
|
||||||
|
BuiltinConcepts.PLURAL,
|
||||||
|
name=token.value,
|
||||||
|
props={BuiltinConcepts.PLURAL: c})
|
||||||
for c in concepts]
|
for c in concepts]
|
||||||
|
|
||||||
|
for concept in plural_concepts:
|
||||||
|
underlying_concept = concept.get_prop(BuiltinConcepts.PLURAL)
|
||||||
|
if self.sheerka.isaset(self.context, underlying_concept):
|
||||||
|
concept.get_metadata().body = f"get_set_elements(c:|{underlying_concept.id}:)"
|
||||||
|
|
||||||
|
return plural_concepts
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def as_list(obj):
|
def as_list(obj):
|
||||||
if obj is None:
|
if obj is None:
|
||||||
|
|||||||
+939
-1392
File diff suppressed because it is too large
Load Diff
@@ -2,9 +2,9 @@ from dataclasses import dataclass
|
|||||||
|
|
||||||
import core.utils
|
import core.utils
|
||||||
from core.builtin_concepts import BuiltinConcepts
|
from core.builtin_concepts import BuiltinConcepts
|
||||||
from core.builtin_helpers import only_successful, get_lexer_nodes, update_compiled
|
from core.builtin_helpers import get_lexer_nodes, only_successful, update_compiled
|
||||||
from parsers.BaseNodeParser import ConceptNode, UnrecognizedTokensNode, SourceCodeNode, SourceCodeWithConceptNode
|
from parsers.BaseNodeParser import ConceptNode, SourceCodeNode, SourceCodeWithConceptNode, UnrecognizedTokensNode
|
||||||
from parsers.BaseParser import BaseParser, ParsingError, BaseParserInputParser
|
from parsers.BaseParser import BaseParserInputParser, ParsingError
|
||||||
from parsers.BnfNodeParser import BnfNodeParser
|
from parsers.BnfNodeParser import BnfNodeParser
|
||||||
from parsers.PythonParser import PythonParser
|
from parsers.PythonParser import PythonParser
|
||||||
from parsers.SequenceNodeParser import SequenceNodeParser
|
from parsers.SequenceNodeParser import SequenceNodeParser
|
||||||
|
|||||||
@@ -0,0 +1,389 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from itertools import product
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
from core.builtin_helpers import is_only_successful, only_successful
|
||||||
|
from core.global_symbols import INIT_AST_PARSERS, NotInit
|
||||||
|
from core.sheerka.services.SheerkaEvaluateConcept import EvaluationHints
|
||||||
|
from core.tokenizer import TokenKind
|
||||||
|
from core.utils import merge_dicts, merge_sets
|
||||||
|
from parsers.BaseExpressionParser import AndNode, ComparisonNode, ComparisonType, ExprNode, ExpressionVisitorWithHint, \
|
||||||
|
FunctionNode, ListComprehensionNode, \
|
||||||
|
ListNode, NameExprNode, NotNode, VariableNode, end_parenthesis_mapping, open_parenthesis_mapping
|
||||||
|
from parsers.PythonParser import PythonNode
|
||||||
|
from sheerkapython.python_wrapper import sheerka_globals
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass()
|
||||||
|
class PythonExprVisitorObj:
|
||||||
|
text: Union[str, None] # human readable
|
||||||
|
source: Union[str, None] # python expression to compile
|
||||||
|
objects: dict # dictionaries of object created during the visit
|
||||||
|
variables: set # I intended to detect unbound symbols, but it's actually not used
|
||||||
|
|
||||||
|
|
||||||
|
class PythonExprVisitor(ExpressionVisitorWithHint):
|
||||||
|
def __init__(self, context, obj_counter=0):
|
||||||
|
self.context = context
|
||||||
|
self.obj_counter = obj_counter
|
||||||
|
self.objects_by_id = {}
|
||||||
|
self.objects_by_name = {}
|
||||||
|
self.errors = {}
|
||||||
|
self.results = []
|
||||||
|
|
||||||
|
def compile(self, expr_node, hint=None):
|
||||||
|
hint = hint or EvaluationHints(eval_body=True)
|
||||||
|
visitor_objects = self.visit(expr_node, hint)
|
||||||
|
|
||||||
|
for obj in visitor_objects:
|
||||||
|
|
||||||
|
ret = self.context.sheerka.parse_python(self.context, obj.source)
|
||||||
|
if ret.status:
|
||||||
|
ret.body.body.original_source = obj.text
|
||||||
|
ret.body.body.objects = obj.objects
|
||||||
|
self.results.append(ret)
|
||||||
|
else:
|
||||||
|
self.errors[obj.text] = self.context.sheerka.get_error_cause(ret.body)
|
||||||
|
|
||||||
|
return self.results
|
||||||
|
|
||||||
|
def visit_ListComprehensionNode(self, expr_node: ListComprehensionNode, hint: EvaluationHints):
|
||||||
|
"""
|
||||||
|
|
||||||
|
:param expr_node:
|
||||||
|
:param hint:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
visitor_objects = []
|
||||||
|
source = expr_node.get_source()
|
||||||
|
|
||||||
|
not_a_question_hint = EvaluationHints(eval_body=True, eval_question=False)
|
||||||
|
is_a_question_hint = EvaluationHints(eval_body=True, eval_question=True)
|
||||||
|
product_inputs = []
|
||||||
|
|
||||||
|
# add parenthesis around the element if needed
|
||||||
|
# test case test_ExprToPython.test_i_can_compile_when_element_is_missing_its_parenthesis()
|
||||||
|
if expr_node.element.first is None and len(expr_node.element.items) > 1:
|
||||||
|
expr_node.element.first = NameExprNode(-1, -1, [open_parenthesis_mapping[TokenKind.LPAR]])
|
||||||
|
expr_node.element.last = NameExprNode(-1, -1, [end_parenthesis_mapping[TokenKind.LPAR]])
|
||||||
|
|
||||||
|
element_objs = self.visit(expr_node.element, not_a_question_hint)
|
||||||
|
product_inputs.append(element_objs)
|
||||||
|
for comp in expr_node.generators:
|
||||||
|
target_objs = self.visit(comp.target, not_a_question_hint)
|
||||||
|
iter_objs = self.visit(comp.iterable, not_a_question_hint)
|
||||||
|
if comp.if_expr:
|
||||||
|
# parse it using PythonConditionExprVisitor
|
||||||
|
res = self.context.sheerka.parse_expression(self.context, comp.if_expr.get_source())
|
||||||
|
if not res.status:
|
||||||
|
self.errors[comp.if_expr.get_source()] = res.body
|
||||||
|
return None
|
||||||
|
if_expr_objs = self.visit(res.body.body, is_a_question_hint)
|
||||||
|
else:
|
||||||
|
if_expr_objs = [None]
|
||||||
|
|
||||||
|
product_inputs.extend([target_objs, iter_objs, if_expr_objs])
|
||||||
|
|
||||||
|
for items in product(*product_inputs):
|
||||||
|
visitor_objects.append(self.create_list_comprehension(source, *items))
|
||||||
|
|
||||||
|
return visitor_objects
|
||||||
|
|
||||||
|
def visit_VariableNode(self, expr_node: VariableNode, hint: EvaluationHints):
|
||||||
|
source = expr_node.get_source()
|
||||||
|
return self.parse_source_code(source, hint)
|
||||||
|
|
||||||
|
def visit_NameExprNode(self, expr_node: NameExprNode, hint: EvaluationHints):
|
||||||
|
"""
|
||||||
|
create visitor objects from NameExprNode
|
||||||
|
:param expr_node:
|
||||||
|
:param hint:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
source = expr_node.get_source()
|
||||||
|
return self.parse_source_code(source, hint)
|
||||||
|
|
||||||
|
def visit_ListNode(self, expr_node: ListNode, hint: EvaluationHints):
|
||||||
|
visitor_objects = []
|
||||||
|
source = expr_node.get_source()
|
||||||
|
|
||||||
|
items_objs = []
|
||||||
|
for item in expr_node.items:
|
||||||
|
items_objs.append(self.visit(item, hint))
|
||||||
|
|
||||||
|
for items in product(*items_objs):
|
||||||
|
visitor_objects.append(self.create_list(source,
|
||||||
|
expr_node.first.get_source() if expr_node.first else None,
|
||||||
|
expr_node.last.get_source() if expr_node.last else None,
|
||||||
|
items,
|
||||||
|
expr_node.sep))
|
||||||
|
return visitor_objects
|
||||||
|
|
||||||
|
def visit_AndNode(self, expr_node: AndNode, hint: EvaluationHints):
|
||||||
|
"""
|
||||||
|
|
||||||
|
:param expr_node:
|
||||||
|
:param hint:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return self.visit_or_or_and_node("and", expr_node, hint)
|
||||||
|
|
||||||
|
def visit_OrNode(self, expr_node: AndNode, hint: EvaluationHints):
|
||||||
|
"""
|
||||||
|
|
||||||
|
:param expr_node:
|
||||||
|
:param hint:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return self.visit_or_or_and_node("or", expr_node, hint)
|
||||||
|
|
||||||
|
def visit_NotNode(self, expr_node: NotNode, hint: EvaluationHints):
|
||||||
|
"""
|
||||||
|
|
||||||
|
:param expr_node:
|
||||||
|
:param hint:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
visitor_objects = []
|
||||||
|
|
||||||
|
source = expr_node.get_source()
|
||||||
|
objs = self.visit(expr_node.node, hint)
|
||||||
|
|
||||||
|
for obj in objs:
|
||||||
|
visitor_objects.append(self.create_not(source, obj))
|
||||||
|
|
||||||
|
return visitor_objects
|
||||||
|
|
||||||
|
def visit_ComparisonNode(self, expr_node: ComparisonNode, hint: EvaluationHints):
|
||||||
|
"""
|
||||||
|
|
||||||
|
:param expr_node:
|
||||||
|
:param hint:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
visitor_objects = []
|
||||||
|
source = expr_node.get_source()
|
||||||
|
|
||||||
|
left = self.visit(expr_node.left, hint)
|
||||||
|
right = self.visit(expr_node.right, hint)
|
||||||
|
|
||||||
|
for left_obj, right_obj in product(left, right):
|
||||||
|
visitor_objects.append(self.create_comparison(source, expr_node.comp, left_obj, right_obj))
|
||||||
|
|
||||||
|
return visitor_objects
|
||||||
|
|
||||||
|
def visit_FunctionNode(self, expr_node: FunctionNode, hint: EvaluationHints):
|
||||||
|
visitor_objects = []
|
||||||
|
source = expr_node.get_source()
|
||||||
|
|
||||||
|
parameters_objects = []
|
||||||
|
for parameter in expr_node.parameters:
|
||||||
|
parameters_objects.append(self.visit(parameter.value, hint))
|
||||||
|
|
||||||
|
for parameters in product(*parameters_objects):
|
||||||
|
visitor_objects.append(self.create_function(source,
|
||||||
|
expr_node.first.get_source(),
|
||||||
|
expr_node.last.get_source(),
|
||||||
|
parameters))
|
||||||
|
|
||||||
|
return visitor_objects
|
||||||
|
|
||||||
|
def visit_or_or_and_node(self, node_type, expr_node: ExprNode, hint: EvaluationHints):
|
||||||
|
"""
|
||||||
|
|
||||||
|
:param node_type:
|
||||||
|
:param expr_node:
|
||||||
|
:param hint:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
visitor_objects = []
|
||||||
|
|
||||||
|
source = expr_node.get_source()
|
||||||
|
objs = []
|
||||||
|
for node in expr_node.parts:
|
||||||
|
objs.append(self.visit(node, hint))
|
||||||
|
|
||||||
|
for objs_parts in product(*objs):
|
||||||
|
visitor_objects.append(self.create_and_or(node_type, source, objs_parts))
|
||||||
|
|
||||||
|
return visitor_objects
|
||||||
|
|
||||||
|
def parse_source_code(self, source, hint):
|
||||||
|
res = self.context.sheerka.parse_unrecognized(self.context,
|
||||||
|
source,
|
||||||
|
INIT_AST_PARSERS,
|
||||||
|
filter_func=only_successful,
|
||||||
|
is_question=hint.eval_question)
|
||||||
|
|
||||||
|
return_values = res.body.body if is_only_successful(self.context.sheerka, res) else [res]
|
||||||
|
|
||||||
|
visitor_objects = []
|
||||||
|
for ret_val in return_values:
|
||||||
|
if not ret_val.status:
|
||||||
|
self.errors[source] = ret_val.body
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(ret_val.body.body, list):
|
||||||
|
if len(ret_val.body.body) > 1:
|
||||||
|
raise NotImplementedError("Too many concept found. Not handled yet !")
|
||||||
|
body = ret_val.body.body[0]
|
||||||
|
else:
|
||||||
|
body = ret_val.body.body
|
||||||
|
|
||||||
|
if hasattr(body, "get_concept"):
|
||||||
|
visitor_objects.append(self.create_call_concept(source, body.get_concept(), hint.eval_question))
|
||||||
|
elif hasattr(body, "get_python_node"):
|
||||||
|
visitor_objects.append(self.create_source_code_from_python_node(body.get_python_node()))
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(f"{body=}. Not yet implemented")
|
||||||
|
|
||||||
|
return visitor_objects
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_source_code_from_python_node(node: PythonNode):
|
||||||
|
return PythonExprVisitorObj(text=node.original_source,
|
||||||
|
source=node.source,
|
||||||
|
objects=node.objects,
|
||||||
|
variables=set())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_list_comprehension(text, *items):
|
||||||
|
objects = {}
|
||||||
|
variables = set()
|
||||||
|
|
||||||
|
def update_objects_and_variables(*objs):
|
||||||
|
for obj in [obj for obj in objs if obj]:
|
||||||
|
objects.update(obj.objects)
|
||||||
|
variables.update(obj.variables)
|
||||||
|
|
||||||
|
items = list(items)
|
||||||
|
|
||||||
|
element = items.pop(0)
|
||||||
|
update_objects_and_variables(element)
|
||||||
|
source = f"[ {element.source}"
|
||||||
|
|
||||||
|
while len(items):
|
||||||
|
target = items.pop(0)
|
||||||
|
iterable = items.pop(0)
|
||||||
|
if_expr = items.pop(0)
|
||||||
|
update_objects_and_variables(target, iterable, if_expr)
|
||||||
|
source += f" for {target.source} in {iterable.source}"
|
||||||
|
if if_expr:
|
||||||
|
source += f" if {if_expr.source}"
|
||||||
|
source += " ]"
|
||||||
|
|
||||||
|
return PythonExprVisitorObj(text=text,
|
||||||
|
source=source,
|
||||||
|
objects=objects,
|
||||||
|
variables=variables)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_and_or(node_type, text, parts):
|
||||||
|
return PythonExprVisitorObj(text=text,
|
||||||
|
source=f" {node_type} ".join([p.source for p in parts]),
|
||||||
|
objects=merge_dicts(*[p.objects for p in parts]),
|
||||||
|
variables=merge_sets(*[p.variables for p in parts]))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_comparison(text, op, left_obj, right_obj):
|
||||||
|
def get_source(_op, a, b):
|
||||||
|
if _op == ComparisonType.EQUALS and b == "sheerka":
|
||||||
|
return f"is_sheerka({a})"
|
||||||
|
else:
|
||||||
|
return ComparisonNode.rebuild_source(a, op, b)
|
||||||
|
|
||||||
|
return PythonExprVisitorObj(text=text,
|
||||||
|
source=get_source(op, left_obj.source, right_obj.source),
|
||||||
|
objects=merge_dicts(left_obj.objects, right_obj.objects),
|
||||||
|
variables=merge_sets(left_obj.variables, right_obj.variables))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_not(text, node):
|
||||||
|
return PythonExprVisitorObj(text=text,
|
||||||
|
source=f"not {node.source}",
|
||||||
|
objects=node.objects,
|
||||||
|
variables=node.variables)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_function(text, first, last, parameters):
|
||||||
|
def get_source(_first, _last, _parameters):
|
||||||
|
return f"{_first}{', '.join(p for p in _parameters)}{_last}"
|
||||||
|
|
||||||
|
return PythonExprVisitorObj(text=text,
|
||||||
|
source=get_source(first, last, [p.source for p in parameters]),
|
||||||
|
objects=merge_dicts(*[p.objects for p in parameters]),
|
||||||
|
variables=merge_sets(*[p.variables for p in parameters]))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_list(text, first, last, items, sep):
|
||||||
|
def get_source(_first, _last, _items, _sep):
|
||||||
|
res = _first or ""
|
||||||
|
res += f"{_sep.value} ".join(item for item in _items)
|
||||||
|
if _last:
|
||||||
|
res += _last
|
||||||
|
return res
|
||||||
|
|
||||||
|
return PythonExprVisitorObj(text=text,
|
||||||
|
source=get_source(first, last, [p.source for p in items], sep),
|
||||||
|
objects=merge_dicts(*[p.objects for p in items]),
|
||||||
|
variables=merge_sets(*[p.variables for p in items]))
|
||||||
|
|
||||||
|
def get_object_name(self, obj):
|
||||||
|
"""
|
||||||
|
object found during the parsing are not serialized
|
||||||
|
They are kept in a dictionary.
|
||||||
|
This function returns a new name for every new object
|
||||||
|
:param obj: object for which a name is to be created
|
||||||
|
:param objects: already created names (it's a dictionary)
|
||||||
|
:return: tuple(name created, dictionary of already created names)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.context.sheerka.is_sheerka(obj):
|
||||||
|
return "sheerka"
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.objects_by_id[id(obj)]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
object_name = f"__o_{self.obj_counter:02}__"
|
||||||
|
self.obj_counter += 1
|
||||||
|
|
||||||
|
self.objects_by_id[id(obj)] = object_name
|
||||||
|
self.objects_by_name[object_name] = obj
|
||||||
|
return object_name
|
||||||
|
|
||||||
|
def create_call_concept(self, source, concept, is_question):
|
||||||
|
name = self.get_object_name(concept)
|
||||||
|
parameters = {}
|
||||||
|
|
||||||
|
for var_name, default_value in concept.get_metadata().variables:
|
||||||
|
if var_name not in concept.get_metadata().parameters:
|
||||||
|
continue
|
||||||
|
parameters[var_name] = default_value if default_value is not NotInit else var_name
|
||||||
|
|
||||||
|
function_to_call = "evaluate_question" if is_question else "call_concept"
|
||||||
|
to_compile = f"{function_to_call}({name}"
|
||||||
|
for p_name, p_value in parameters.items():
|
||||||
|
to_compile += f", {p_name}={p_value}"
|
||||||
|
to_compile += ")"
|
||||||
|
|
||||||
|
concept.get_hints().use_copy = True
|
||||||
|
concept.get_hints().is_evaluated = True
|
||||||
|
return PythonExprVisitorObj(source, to_compile, {name: concept}, set())
|
||||||
|
|
||||||
|
def is_a_possible_variable(self, name):
|
||||||
|
"""
|
||||||
|
tells whether or not the name can be a variable
|
||||||
|
:param name:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if self.context.sheerka.is_a_concept_name(name):
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
eval(name, sheerka_globals)
|
||||||
|
except:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
+10
-2
@@ -1,12 +1,13 @@
|
|||||||
import ast
|
import ast
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
|
from core.builtin_concepts import BuiltinConcepts, ParserResultConcept, ReturnValueConcept
|
||||||
from core.concept import Concept, DEFINITION_TYPE_BNF, DEFINITION_TYPE_DEF, freeze_concept_attrs
|
from core.concept import Concept, DEFINITION_TYPE_BNF, DEFINITION_TYPE_DEF, freeze_concept_attrs
|
||||||
from core.rule import Rule, ACTION_TYPE_PRINT, ACTION_TYPE_EXEC
|
from core.rule import ACTION_TYPE_EXEC, ACTION_TYPE_PRINT, Rule
|
||||||
from core.sheerka.ExecutionContext import ExecutionContext
|
from core.sheerka.ExecutionContext import ExecutionContext
|
||||||
from core.sheerka.Sheerka import Sheerka
|
from core.sheerka.Sheerka import Sheerka
|
||||||
from core.sheerka.services.SheerkaConceptManager import SheerkaConceptManager
|
from core.sheerka.services.SheerkaConceptManager import SheerkaConceptManager
|
||||||
|
from core.sheerka.services.SheerkaDebugManager import ListDebugLogger
|
||||||
from core.sheerka.services.SheerkaRuleManager import SheerkaRuleManager
|
from core.sheerka.services.SheerkaRuleManager import SheerkaRuleManager
|
||||||
from parsers.BnfDefinitionParser import BnfDefinitionParser
|
from parsers.BnfDefinitionParser import BnfDefinitionParser
|
||||||
from parsers.BnfNodeParser import StrMatch
|
from parsers.BnfNodeParser import StrMatch
|
||||||
@@ -290,3 +291,10 @@ class BaseTest:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def successful_return_values(return_values):
|
def successful_return_values(return_values):
|
||||||
return [ret_val for ret_val in return_values if ret_val.status]
|
return [ret_val for ret_val in return_values if ret_val.status]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def activate_debug(context, pattern="Sya.*.*"):
|
||||||
|
sheerka = context.sheerka
|
||||||
|
sheerka.set_debug(context, True)
|
||||||
|
sheerka.set_debug_var(context, pattern)
|
||||||
|
sheerka.set_debug_logger_definition(ListDebugLogger)
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
|
|||||||
|
|
||||||
item_container = f"debug_{item_type}_settings"
|
item_container = f"debug_{item_type}_settings"
|
||||||
assert getattr(service, item_container) == [DebugItem(
|
assert getattr(service, item_container) == [DebugItem(
|
||||||
|
item_type,
|
||||||
item,
|
item,
|
||||||
service_name,
|
service_name,
|
||||||
method_name,
|
method_name,
|
||||||
@@ -89,7 +90,7 @@ class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
|
|||||||
|
|
||||||
service.add_or_update_debug_item(context, "vars", item="item")
|
service.add_or_update_debug_item(context, "vars", item="item")
|
||||||
assert service.debug_vars_settings == [
|
assert service.debug_vars_settings == [
|
||||||
DebugItem("item", None, None, None, False, None, False, True)
|
DebugItem("vars", "item", None, None, None, False, None, False, True)
|
||||||
]
|
]
|
||||||
|
|
||||||
def test_i_can_update_debug_item(self):
|
def test_i_can_update_debug_item(self):
|
||||||
@@ -101,8 +102,8 @@ class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
|
|||||||
service.add_or_update_debug_item(context, "vars", "item", "service_name", "method_name", enabled=False)
|
service.add_or_update_debug_item(context, "vars", "item", "service_name", "method_name", enabled=False)
|
||||||
|
|
||||||
assert service.debug_vars_settings == [
|
assert service.debug_vars_settings == [
|
||||||
DebugItem("item", "service_name", "method_name", None, False, None, False, False),
|
DebugItem("vars", "item", "service_name", "method_name", None, False, None, False, False),
|
||||||
DebugItem("item2", "service_name", "method_name", None, False, None, False, True),
|
DebugItem("vars", "item2", "service_name", "method_name", None, False, None, False, True),
|
||||||
]
|
]
|
||||||
|
|
||||||
@pytest.mark.parametrize("settings, expected", [
|
@pytest.mark.parametrize("settings, expected", [
|
||||||
@@ -407,7 +408,7 @@ class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
|
|||||||
|
|
||||||
sheerka.set_debug_var(context, "s.m.v", "1+", 10, variable="my_var")
|
sheerka.set_debug_var(context, "s.m.v", "1+", 10, variable="my_var")
|
||||||
assert service.debug_vars_settings == [
|
assert service.debug_vars_settings == [
|
||||||
DebugItem("my_var", "s", "m", 1, True, 10, False, True)
|
DebugItem("vars", "my_var", "s", "m", 1, True, 10, False, True)
|
||||||
]
|
]
|
||||||
assert service.debug_concepts_settings == []
|
assert service.debug_concepts_settings == []
|
||||||
assert service.debug_rules_settings == []
|
assert service.debug_rules_settings == []
|
||||||
@@ -418,7 +419,7 @@ class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
|
|||||||
|
|
||||||
sheerka.set_debug_rule(context, "s.m.v", "1+", 10, rule="my_rule")
|
sheerka.set_debug_rule(context, "s.m.v", "1+", 10, rule="my_rule")
|
||||||
assert service.debug_rules_settings == [
|
assert service.debug_rules_settings == [
|
||||||
DebugItem("my_rule", "s", "m", 1, True, 10, False, True)
|
DebugItem("rules", "my_rule", "s", "m", 1, True, 10, False, True)
|
||||||
]
|
]
|
||||||
assert service.debug_concepts_settings == []
|
assert service.debug_concepts_settings == []
|
||||||
assert service.debug_vars_settings == []
|
assert service.debug_vars_settings == []
|
||||||
@@ -429,7 +430,7 @@ class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
|
|||||||
|
|
||||||
sheerka.set_debug_concept(context, "s.m.v", "1+", 10, concept="my_concept")
|
sheerka.set_debug_concept(context, "s.m.v", "1+", 10, concept="my_concept")
|
||||||
assert service.debug_concepts_settings == [
|
assert service.debug_concepts_settings == [
|
||||||
DebugItem("my_concept", "s", "m", 1, True, 10, False, True)
|
DebugItem("concepts", "my_concept", "s", "m", 1, True, 10, False, True)
|
||||||
]
|
]
|
||||||
assert service.debug_rules_settings == []
|
assert service.debug_rules_settings == []
|
||||||
assert service.debug_vars_settings == []
|
assert service.debug_vars_settings == []
|
||||||
@@ -449,11 +450,11 @@ class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
|
|||||||
|
|
||||||
assert another_service.activated
|
assert another_service.activated
|
||||||
assert another_service.debug_vars_settings == [
|
assert another_service.debug_vars_settings == [
|
||||||
DebugItem('var', 'service_name', None, None, False, None, False, True)]
|
DebugItem("vars", 'var', 'service_name', None, None, False, None, False, True)]
|
||||||
assert another_service.debug_rules_settings == [
|
assert another_service.debug_rules_settings == [
|
||||||
DebugItem('1', None, None, None, False, None, False, True)]
|
DebugItem("rules", '1', None, None, None, False, None, False, True)]
|
||||||
assert another_service.debug_concepts_settings == [
|
assert another_service.debug_concepts_settings == [
|
||||||
DebugItem('1001', None, None, None, False, None, False, True)]
|
DebugItem("concepts", '1001', None, None, None, False, None, False, True)]
|
||||||
|
|
||||||
def test_i_can_inspect_concept_all_attributes(self):
|
def test_i_can_inspect_concept_all_attributes(self):
|
||||||
sheerka, context, foo = self.init_concepts("foo")
|
sheerka, context, foo = self.init_concepts("foo")
|
||||||
@@ -766,9 +767,12 @@ class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
|
|||||||
|
|
||||||
sheerka.pop_ontology(context)
|
sheerka.pop_ontology(context)
|
||||||
assert service.activated
|
assert service.activated
|
||||||
assert service.debug_vars_settings == [DebugItem("v_name", "v_service", "v_method", 1, True, 1, False, True)]
|
assert service.debug_vars_settings == [
|
||||||
assert service.debug_rules_settings == [DebugItem("r_name", "r_service", "r_method", 2, True, 2, False, True)]
|
DebugItem("vars", "v_name", "v_service", "v_method", 1, True, 1, False, True)]
|
||||||
assert service.debug_concepts_settings == [DebugItem("c_name", "c_serv", "c_method", 3, True, 3, False, True)]
|
assert service.debug_rules_settings == [
|
||||||
|
DebugItem("rules", "r_name", "r_service", "r_method", 2, True, 2, False, True)]
|
||||||
|
assert service.debug_concepts_settings == [
|
||||||
|
DebugItem("concepts", "c_name", "c_serv", "c_method", 3, True, 3, False, True)]
|
||||||
|
|
||||||
def test_i_can_register_debug_item(self):
|
def test_i_can_register_debug_item(self):
|
||||||
sheerka, context = self.init_concepts()
|
sheerka, context = self.init_concepts()
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept, ParserRes
|
|||||||
from core.concept import Concept, DoNotResolve, ConceptParts, InfiniteRecursionResolved, \
|
from core.concept import Concept, DoNotResolve, ConceptParts, InfiniteRecursionResolved, \
|
||||||
DEFINITION_TYPE_DEF
|
DEFINITION_TYPE_DEF
|
||||||
from core.global_symbols import NotInit, NotFound
|
from core.global_symbols import NotInit, NotFound
|
||||||
from core.sheerka.services.SheerkaEvaluateConcept import SheerkaEvaluateConcept
|
from core.sheerka.services.SheerkaEvaluateConcept import EvaluationHints, SheerkaEvaluateConcept
|
||||||
from core.sheerka.services.SheerkaExecute import ParserInput
|
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||||
from core.sheerka.services.SheerkaMemory import SheerkaMemory
|
from core.sheerka.services.SheerkaMemory import SheerkaMemory
|
||||||
from parsers.BaseParser import BaseParser
|
from parsers.BaseParser import BaseParser
|
||||||
@@ -13,7 +13,7 @@ from parsers.ExpressionParser import ExpressionParser
|
|||||||
from parsers.PythonParser import PythonNode, PythonParser
|
from parsers.PythonParser import PythonNode, PythonParser
|
||||||
from parsers.SyaNodeParser import SyaNodeParser
|
from parsers.SyaNodeParser import SyaNodeParser
|
||||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||||
from tests.evaluators.EvaluatorTestsUtils import pr_ret_val, python_ret_val
|
from tests.evaluators.EvaluatorTestsUtils import exact, pr_ret_val, python_ret_val
|
||||||
from tests.parsers.parsers_utils import CB, compare_with_test_object
|
from tests.parsers.parsers_utils import CB, compare_with_test_object
|
||||||
|
|
||||||
|
|
||||||
@@ -487,7 +487,9 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
|||||||
eval_where=True,
|
eval_where=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka, False, True), concept, eval_body=False)
|
evaluated = sheerka.evaluate_concept(self.get_context(sheerka, False, True),
|
||||||
|
concept,
|
||||||
|
hints=EvaluationHints(eval_body=False))
|
||||||
|
|
||||||
if expected:
|
if expected:
|
||||||
assert evaluated.key == concept.key
|
assert evaluated.key == concept.key
|
||||||
@@ -532,13 +534,13 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
|||||||
)
|
)
|
||||||
|
|
||||||
foo_instance = sheerka.new("foo")
|
foo_instance = sheerka.new("foo")
|
||||||
evaluated = sheerka.evaluate_concept(context, foo_instance, eval_body=False)
|
evaluated = sheerka.evaluate_concept(context, foo_instance, hints=EvaluationHints(eval_body=False))
|
||||||
|
|
||||||
assert ConceptParts.BODY in evaluated.get_compiled()
|
assert ConceptParts.BODY in evaluated.get_compiled()
|
||||||
assert evaluated.body == NotInit
|
assert evaluated.body == NotInit
|
||||||
assert not evaluated.get_hints().is_evaluated
|
assert not evaluated.get_hints().is_evaluated
|
||||||
|
|
||||||
evaluated = sheerka.evaluate_concept(context, foo_instance, eval_body=True) # evaluate the body this time
|
evaluated = sheerka.evaluate_concept(context, foo_instance, hints=EvaluationHints(eval_body=True)) # evaluate the body this time
|
||||||
assert isinstance(evaluated.body, bool) and evaluated.body
|
assert isinstance(evaluated.body, bool) and evaluated.body
|
||||||
|
|
||||||
def test_i_can_apply_intermediate_where_condition_using_python(self):
|
def test_i_can_apply_intermediate_where_condition_using_python(self):
|
||||||
@@ -811,7 +813,7 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
|||||||
Concept("bar", pre="print('10')"), # print won't be executed
|
Concept("bar", pre="print('10')"), # print won't be executed
|
||||||
)
|
)
|
||||||
|
|
||||||
evaluated = sheerka.evaluate_concept(context, foo, eval_body=True)
|
evaluated = sheerka.evaluate_concept(context, foo, hints=EvaluationHints(eval_body=True))
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert evaluated.key == foo.key
|
assert evaluated.key == foo.key
|
||||||
assert captured.out == "10\n"
|
assert captured.out == "10\n"
|
||||||
@@ -828,7 +830,7 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
|||||||
Concept("foo", pre="in_context('foo')", body="print('10')"),
|
Concept("foo", pre="in_context('foo')", body="print('10')"),
|
||||||
)
|
)
|
||||||
|
|
||||||
evaluated = sheerka.evaluate_concept(context, concept, eval_body=True)
|
evaluated = sheerka.evaluate_concept(context, concept, hints=EvaluationHints(eval_body=True))
|
||||||
assert sheerka.isinstance(evaluated, BuiltinConcepts.CONDITION_FAILED)
|
assert sheerka.isinstance(evaluated, BuiltinConcepts.CONDITION_FAILED)
|
||||||
assert evaluated.body == "in_context('foo')"
|
assert evaluated.body == "in_context('foo')"
|
||||||
assert evaluated.concept == concept
|
assert evaluated.concept == concept
|
||||||
@@ -898,7 +900,7 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
|||||||
def test_is_evaluated_is_correctly_set(self, concept, expected):
|
def test_is_evaluated_is_correctly_set(self, concept, expected):
|
||||||
sheerka, context, concept = self.init_concepts(concept)
|
sheerka, context, concept = self.init_concepts(concept)
|
||||||
|
|
||||||
evaluated = sheerka.evaluate_concept(context, concept, eval_body=True)
|
evaluated = sheerka.evaluate_concept(context, concept, hints=EvaluationHints(eval_body=True))
|
||||||
|
|
||||||
assert evaluated.key == concept.key
|
assert evaluated.key == concept.key
|
||||||
assert concept.get_hints().is_evaluated == expected
|
assert concept.get_hints().is_evaluated == expected
|
||||||
@@ -919,24 +921,24 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
|||||||
res = sheerka.evaluate_concept(context, bar)
|
res = sheerka.evaluate_concept(context, bar)
|
||||||
assert sheerka.isinstance(res, "bar")
|
assert sheerka.isinstance(res, "bar")
|
||||||
|
|
||||||
res = sheerka.evaluate_concept(context, bar, eval_body=True)
|
res = sheerka.evaluate_concept(context, bar, hints=EvaluationHints(eval_body=True))
|
||||||
assert sheerka.isinstance(res, "foo")
|
assert sheerka.isinstance(res, "foo")
|
||||||
|
|
||||||
# And the result is still the same after a second call
|
# And the result is still the same after a second call
|
||||||
assert bar.get_hints().is_evaluated
|
assert bar.get_hints().is_evaluated
|
||||||
res = sheerka.evaluate_concept(context, bar, eval_body=True)
|
res = sheerka.evaluate_concept(context, bar, hints=EvaluationHints(eval_body=True))
|
||||||
assert sheerka.isinstance(res, "foo")
|
assert sheerka.isinstance(res, "foo")
|
||||||
|
|
||||||
def test_ret_is_evaluated_only_is_body_is_requested(self):
|
def test_ret_is_evaluated_only_is_body_is_requested(self):
|
||||||
sheerka, context, foo, bar = self.init_concepts("foo", Concept("bar", ret="__NOT_FOUND"))
|
sheerka, context, foo, bar = self.init_concepts("foo", Concept("bar", ret="__NOT_FOUND"))
|
||||||
|
|
||||||
res = sheerka.evaluate_concept(context, bar, eval_body=False)
|
res = sheerka.evaluate_concept(context, bar, hints=EvaluationHints(eval_body=False))
|
||||||
assert res.id == bar.id
|
assert res.id == bar.id
|
||||||
|
|
||||||
def test_i_can_eval_concept_with_rules(self):
|
def test_i_can_eval_concept_with_rules(self):
|
||||||
sheerka, context, foo = self.init_concepts(Concept("foo a", body="a.name").def_var("a", "r:|1:"))
|
sheerka, context, foo = self.init_concepts(Concept("foo a", body="a.name").def_var("a", "r:|1:"))
|
||||||
|
|
||||||
res = sheerka.evaluate_concept(context, foo, eval_body=True)
|
res = sheerka.evaluate_concept(context, foo, hints=EvaluationHints(eval_body=True))
|
||||||
assert res.body == "Print return values"
|
assert res.body == "Print return values"
|
||||||
|
|
||||||
def test_i_can_manage_python_concept_infinite_recursion_when_initializing_ast(self):
|
def test_i_can_manage_python_concept_infinite_recursion_when_initializing_ast(self):
|
||||||
@@ -957,18 +959,18 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
|||||||
evaluator = SheerkaEvaluateConcept(sheerka)
|
evaluator = SheerkaEvaluateConcept(sheerka)
|
||||||
|
|
||||||
# 'def concept foo as foo'
|
# 'def concept foo as foo'
|
||||||
return_values = [pr_ret_val(foo, parser="ExactConcept"), python_ret_val("foo")]
|
return_values = [pr_ret_val(foo, parser=exact), python_ret_val("foo")]
|
||||||
|
|
||||||
res = evaluator.get_recursive_definitions(context, foo, return_values)
|
res = evaluator.get_recursive_definitions(context, foo, return_values)
|
||||||
|
|
||||||
assert list(res) == [BaseParser.get_name("ExactConcept")]
|
assert list(r.name for r in res) == [BaseParser.get_name("ExactConcept")]
|
||||||
|
|
||||||
def test_i_can_detect_when_no_recursive_definition(self):
|
def test_i_can_detect_when_no_recursive_definition(self):
|
||||||
sheerka, context, foo, bar = self.init_concepts("foo", "bar")
|
sheerka, context, foo, bar = self.init_concepts("foo", "bar")
|
||||||
evaluator = SheerkaEvaluateConcept(sheerka)
|
evaluator = SheerkaEvaluateConcept(sheerka)
|
||||||
|
|
||||||
# 'def concept foo as bar'
|
# 'def concept foo as bar'
|
||||||
return_values = [pr_ret_val(bar, parser="ExactConcept"), python_ret_val("foo")]
|
return_values = [pr_ret_val(bar, parser=exact), python_ret_val("foo")]
|
||||||
|
|
||||||
res = evaluator.get_recursive_definitions(context, foo, return_values)
|
res = evaluator.get_recursive_definitions(context, foo, return_values)
|
||||||
|
|
||||||
@@ -980,7 +982,7 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
|||||||
evaluator = SheerkaEvaluateConcept(sheerka)
|
evaluator = SheerkaEvaluateConcept(sheerka)
|
||||||
|
|
||||||
# i dunno how to construct the return value
|
# i dunno how to construct the return value
|
||||||
return_values = [pr_ret_val(q, parser="ExactConcept")]
|
return_values = [pr_ret_val(q, parser=exact)]
|
||||||
|
|
||||||
res = evaluator.get_recursive_definitions(context, q, return_values)
|
res = evaluator.get_recursive_definitions(context, q, return_values)
|
||||||
|
|
||||||
@@ -1004,7 +1006,7 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
|||||||
assert evaluated.get_compiled()["y"][0].body.body.get_hints().use_copy
|
assert evaluated.get_compiled()["y"][0].body.body.get_hints().use_copy
|
||||||
|
|
||||||
# get the body
|
# get the body
|
||||||
evaluated = evaluator.evaluate_concept(context, concept, eval_body=True)
|
evaluated = evaluator.evaluate_concept(context, concept, hints=EvaluationHints(eval_body=True))
|
||||||
assert evaluated.get_compiled()["x"][0].body.body.get_hints().use_copy
|
assert evaluated.get_compiled()["x"][0].body.body.get_hints().use_copy
|
||||||
assert evaluated.get_compiled()["y"][0].body.body.get_hints().use_copy
|
assert evaluated.get_compiled()["y"][0].body.body.get_hints().use_copy
|
||||||
assert not evaluated.get_value("x").get_hints().use_copy
|
assert not evaluated.get_value("x").get_hints().use_copy
|
||||||
@@ -1025,7 +1027,7 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
|||||||
|
|
||||||
# get the body
|
# get the body
|
||||||
context.add_to_protected_hints(BuiltinConcepts.EVAL_QUESTION_REQUESTED)
|
context.add_to_protected_hints(BuiltinConcepts.EVAL_QUESTION_REQUESTED)
|
||||||
evaluated = evaluator.evaluate_concept(context, concept, eval_body=True)
|
evaluated = evaluator.evaluate_concept(context, concept, hints=EvaluationHints(eval_body=True))
|
||||||
assert evaluated.get_compiled()["x"][0].body.body.get_hints().use_copy
|
assert evaluated.get_compiled()["x"][0].body.body.get_hints().use_copy
|
||||||
assert evaluated.get_compiled()["y"][0].body.body.get_hints().use_copy
|
assert evaluated.get_compiled()["y"][0].body.body.get_hints().use_copy
|
||||||
assert not evaluated.get_value("x").get_hints().use_copy
|
assert not evaluated.get_value("x").get_hints().use_copy
|
||||||
@@ -1043,7 +1045,9 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
|||||||
|
|
||||||
# Sanity check for normal behaviour
|
# Sanity check for normal behaviour
|
||||||
to_evaluate1 = parsed_ret_val.body.body[0].concept.copy()
|
to_evaluate1 = parsed_ret_val.body.body[0].concept.copy()
|
||||||
evaluated1 = sheerka.evaluate_concept(context, to_evaluate1, eval_body=True, validation_only=False)
|
evaluated1 = sheerka.evaluate_concept(context,
|
||||||
|
to_evaluate1,
|
||||||
|
hints=EvaluationHints(eval_body=True, expression_only=False))
|
||||||
|
|
||||||
assert sheerka.isinstance(evaluated1, shirt)
|
assert sheerka.isinstance(evaluated1, shirt)
|
||||||
assert evaluated1.get_value("body_ax_is_evaluated") == True
|
assert evaluated1.get_value("body_ax_is_evaluated") == True
|
||||||
@@ -1053,7 +1057,9 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
|||||||
|
|
||||||
# check validation_only behaviour
|
# check validation_only behaviour
|
||||||
to_evaluate2 = parsed_ret_val.body.body[0].concept.copy()
|
to_evaluate2 = parsed_ret_val.body.body[0].concept.copy()
|
||||||
evaluated2 = sheerka.evaluate_concept(context, to_evaluate2, eval_body=True, validation_only=True)
|
evaluated2 = sheerka.evaluate_concept(context,
|
||||||
|
to_evaluate2,
|
||||||
|
hints=EvaluationHints(eval_body=True, expression_only=True))
|
||||||
|
|
||||||
assert sheerka.isinstance(evaluated2, shirt)
|
assert sheerka.isinstance(evaluated2, shirt)
|
||||||
assert evaluated2.get_value("body_ax_is_evaluated") == NotInit
|
assert evaluated2.get_value("body_ax_is_evaluated") == NotInit
|
||||||
@@ -1073,7 +1079,7 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
|||||||
parsed_ret_val = SyaNodeParser().parse(context, ParserInput("a red shirt"))
|
parsed_ret_val = SyaNodeParser().parse(context, ParserInput("a red shirt"))
|
||||||
to_evaluate = parsed_ret_val.body.body[0].concept
|
to_evaluate = parsed_ret_val.body.body[0].concept
|
||||||
|
|
||||||
evaluated = sheerka.evaluate_concept(context, to_evaluate, eval_body=False)
|
evaluated = sheerka.evaluate_concept(context, to_evaluate, hints=EvaluationHints(eval_body=False))
|
||||||
|
|
||||||
assert sheerka.isinstance(evaluated, a_x)
|
assert sheerka.isinstance(evaluated, a_x)
|
||||||
assert "x" in evaluated.get_compiled()
|
assert "x" in evaluated.get_compiled()
|
||||||
@@ -1085,7 +1091,7 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
|||||||
# sanity check
|
# sanity check
|
||||||
parsed_ret_val = SyaNodeParser().parse(context, ParserInput("a red shirt"))
|
parsed_ret_val = SyaNodeParser().parse(context, ParserInput("a red shirt"))
|
||||||
to_evaluate = parsed_ret_val.body.body[0].concept
|
to_evaluate = parsed_ret_val.body.body[0].concept
|
||||||
evaluated = sheerka.evaluate_concept(context, to_evaluate, eval_body=True)
|
evaluated = sheerka.evaluate_concept(context, to_evaluate, hints=EvaluationHints(eval_body=True))
|
||||||
|
|
||||||
assert sheerka.isinstance(evaluated, shirt)
|
assert sheerka.isinstance(evaluated, shirt)
|
||||||
assert evaluated.get_value("body_ax_is_evaluated") == True
|
assert evaluated.get_value("body_ax_is_evaluated") == True
|
||||||
@@ -1095,7 +1101,7 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
|||||||
def test_concept_is_not_evaluated_when_method_access_error(self):
|
def test_concept_is_not_evaluated_when_method_access_error(self):
|
||||||
sheerka, context, foo = self.init_concepts(Concept("foo", body="set_attr(self, 'prop_name', 'prop_value')"))
|
sheerka, context, foo = self.init_concepts(Concept("foo", body="set_attr(self, 'prop_name', 'prop_value')"))
|
||||||
|
|
||||||
evaluated = sheerka.evaluate_concept(context, foo, eval_body=True, validation_only=True)
|
evaluated = sheerka.evaluate_concept(context, foo, hints=EvaluationHints(eval_body=True, expression_only=True))
|
||||||
|
|
||||||
assert sheerka.isinstance(evaluated, foo)
|
assert sheerka.isinstance(evaluated, foo)
|
||||||
assert not foo.get_hints().is_evaluated
|
assert not foo.get_hints().is_evaluated
|
||||||
|
|||||||
@@ -1,14 +1,22 @@
|
|||||||
import ast
|
import ast
|
||||||
|
|
||||||
from core.builtin_concepts import ReturnValueConcept, BuiltinConcepts, ParserResultConcept
|
from core.builtin_concepts import BuiltinConcepts, ParserResultConcept, ReturnValueConcept
|
||||||
from core.concept import Concept
|
from core.concept import Concept
|
||||||
from evaluators.BaseEvaluator import BaseEvaluator
|
from evaluators.BaseEvaluator import BaseEvaluator
|
||||||
from parsers.BaseParser import BaseParser
|
from parsers.BaseNodeParser import ConceptNode
|
||||||
from parsers.PythonParser import PythonNode
|
from parsers.ExactConceptParser import ExactConceptParser
|
||||||
|
from parsers.PythonParser import PythonNode, PythonParser
|
||||||
|
from parsers.SequenceNodeParser import SequenceNodeParser
|
||||||
|
from parsers.SyaNodeParser import SyaNodeParser
|
||||||
|
|
||||||
reduced_requested = ReturnValueConcept("Sheerka", True, Concept(name=BuiltinConcepts.REDUCE_REQUESTED,
|
reduced_requested = ReturnValueConcept("Sheerka", True, Concept(name=BuiltinConcepts.REDUCE_REQUESTED,
|
||||||
key=BuiltinConcepts.REDUCE_REQUESTED))
|
key=BuiltinConcepts.REDUCE_REQUESTED))
|
||||||
|
|
||||||
|
sequence = SequenceNodeParser()
|
||||||
|
sya = SyaNodeParser()
|
||||||
|
exact = ExactConceptParser()
|
||||||
|
python = PythonParser()
|
||||||
|
|
||||||
|
|
||||||
def ret_val(value="value", who="who", status=True):
|
def ret_val(value="value", who="who", status=True):
|
||||||
"""
|
"""
|
||||||
@@ -21,7 +29,7 @@ def ret_val(value="value", who="who", status=True):
|
|||||||
return ReturnValueConcept(who, status, value)
|
return ReturnValueConcept(who, status, value)
|
||||||
|
|
||||||
|
|
||||||
def p_ret_val(value="value", parser="parser", status=True):
|
def p_ret_val(value="value", parser=exact, status=True):
|
||||||
"""
|
"""
|
||||||
ReturnValueConcept from parser
|
ReturnValueConcept from parser
|
||||||
:param value:
|
:param value:
|
||||||
@@ -29,7 +37,7 @@ def p_ret_val(value="value", parser="parser", status=True):
|
|||||||
:param status:
|
:param status:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return ReturnValueConcept(BaseParser.get_name(parser), status, value)
|
return ReturnValueConcept(parser.name, status, value)
|
||||||
|
|
||||||
|
|
||||||
def e_ret_val(value="value", evaluator="evaluator", status=True):
|
def e_ret_val(value="value", evaluator="evaluator", status=True):
|
||||||
@@ -43,7 +51,7 @@ def e_ret_val(value="value", evaluator="evaluator", status=True):
|
|||||||
return ReturnValueConcept(BaseEvaluator.PREFIX + evaluator, status, value)
|
return ReturnValueConcept(BaseEvaluator.PREFIX + evaluator, status, value)
|
||||||
|
|
||||||
|
|
||||||
def p_ret_val_false(value="value", parser="parser"):
|
def p_ret_val_false(value="value", parser=exact):
|
||||||
"""
|
"""
|
||||||
Failed ReturnValueConcept from parser
|
Failed ReturnValueConcept from parser
|
||||||
:param value:
|
:param value:
|
||||||
@@ -53,7 +61,7 @@ def p_ret_val_false(value="value", parser="parser"):
|
|||||||
return p_ret_val(value, parser, status=False)
|
return p_ret_val(value, parser, status=False)
|
||||||
|
|
||||||
|
|
||||||
def p_ret_val_true(value="value", parser="parser"):
|
def p_ret_val_true(value="value", parser=exact):
|
||||||
"""
|
"""
|
||||||
Successful ReturnValueConcept from parser
|
Successful ReturnValueConcept from parser
|
||||||
:param value:
|
:param value:
|
||||||
@@ -63,24 +71,24 @@ def p_ret_val_true(value="value", parser="parser"):
|
|||||||
return p_ret_val(value, parser, status=True)
|
return p_ret_val(value, parser, status=True)
|
||||||
|
|
||||||
|
|
||||||
def e_ret_val_false(value="value", parser="parser"):
|
def e_ret_val_false(value="value", evaluator="evaluator"):
|
||||||
"""
|
"""
|
||||||
Failed ReturnValueConcept from evaluator
|
Failed ReturnValueConcept from evaluator
|
||||||
:param value:
|
:param value:
|
||||||
:param parser:
|
:param evaluator:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return e_ret_val(value, parser, status=False)
|
return e_ret_val(value, evaluator, status=False)
|
||||||
|
|
||||||
|
|
||||||
def e_ret_val_true(value="value", parser="parser"):
|
def e_ret_val_true(value="value", evaluator="evaluator"):
|
||||||
"""
|
"""
|
||||||
Successful ReturnValueConcept from evaluator
|
Successful ReturnValueConcept from evaluator
|
||||||
:param value:
|
:param value:
|
||||||
:param parser:
|
:param evaluator:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return e_ret_val(value, parser, status=True)
|
return e_ret_val(value, evaluator, status=True)
|
||||||
|
|
||||||
|
|
||||||
def e_ret_val_new(key, evaluator="evaluator", status=True, **kwargs):
|
def e_ret_val_new(key, evaluator="evaluator", status=True, **kwargs):
|
||||||
@@ -96,7 +104,7 @@ def e_ret_val_new(key, evaluator="evaluator", status=True, **kwargs):
|
|||||||
return e_ret_val(body, evaluator, status)
|
return e_ret_val(body, evaluator, status)
|
||||||
|
|
||||||
|
|
||||||
def pr_ret_val(value, parser="parser", source=None, status=True):
|
def pr_ret_val(value, parser=exact, source=None, status=True):
|
||||||
"""
|
"""
|
||||||
ParserResult ReturnValue
|
ParserResult ReturnValue
|
||||||
eg: ReturnValue with a ParserResult
|
eg: ReturnValue with a ParserResult
|
||||||
@@ -107,7 +115,7 @@ def pr_ret_val(value, parser="parser", source=None, status=True):
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
source = source or (value.name if isinstance(value, Concept) else "source")
|
source = source or (value.name if isinstance(value, Concept) else "source")
|
||||||
parser_result = ParserResultConcept(BaseParser.get_name(parser), source=source, value=value)
|
parser_result = ParserResultConcept(parser, source=source, value=value)
|
||||||
return p_ret_val(value=parser_result, parser=parser, status=status)
|
return p_ret_val(value=parser_result, parser=parser, status=status)
|
||||||
|
|
||||||
|
|
||||||
@@ -117,8 +125,14 @@ def python_ret_val(source):
|
|||||||
:param source:
|
:param source:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
python_node = PythonNode(source.strip(), ast.parse(source.strip(), f"<source>", 'eval'))
|
python_node = PythonNode(source.lstrip(), ast.parse(source.strip(), f"<source>", 'eval'))
|
||||||
return pr_ret_val(python_node, parser="Python", source=source)
|
return pr_ret_val(python_node, parser=python, source=source)
|
||||||
|
|
||||||
|
|
||||||
|
def cnode_ret_val(concept, source=None, parser=sya):
|
||||||
|
source = source or concept.name
|
||||||
|
cnode = ConceptNode(concept, 0, 0, source=source)
|
||||||
|
return pr_ret_val([cnode], parser=parser, source=source)
|
||||||
|
|
||||||
|
|
||||||
def new_concept(key, **kwargs):
|
def new_concept(key, **kwargs):
|
||||||
@@ -129,3 +143,11 @@ def new_concept(key, **kwargs):
|
|||||||
|
|
||||||
res.get_hints().is_evaluated = True
|
res.get_hints().is_evaluated = True
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def new_plural(name):
|
||||||
|
name_stripped_s = name.lstrip("s")
|
||||||
|
single = Concept(name_stripped_s)
|
||||||
|
concept = Concept(key=name, name=name, id=f"{name}:{BuiltinConcepts.PLURAL}", is_builtin=False, is_unique=False)
|
||||||
|
concept.set_prop(BuiltinConcepts.PLURAL, single)
|
||||||
|
return concept
|
||||||
|
|||||||
@@ -449,6 +449,18 @@ class TestDefConceptEvaluator(TestUsingMemoryBasedSheerka):
|
|||||||
|
|
||||||
assert sheerka.get_property(created_concept, BuiltinConcepts.ISA) == {sheerka.new(BuiltinConcepts.AUTO_EVAL)}
|
assert sheerka.get_property(created_concept, BuiltinConcepts.ISA) == {sheerka.new(BuiltinConcepts.AUTO_EVAL)}
|
||||||
|
|
||||||
|
def test_i_can_eval_when_variable_are_forced(self):
|
||||||
|
sheerka, context = self.init_test().unpack()
|
||||||
|
definition = "def concept foo from [z for x in y] def_var x def_var y def_var z"
|
||||||
|
def_ret_val = DefConceptParser().parse(context, ParserInput(definition))
|
||||||
|
evaluated = DefConceptEvaluator().eval(context, def_ret_val)
|
||||||
|
|
||||||
|
assert evaluated.status
|
||||||
|
assert context.sheerka.isinstance(evaluated.body, BuiltinConcepts.NEW_CONCEPT)
|
||||||
|
created_concept = evaluated.body.body
|
||||||
|
|
||||||
|
assert created_concept.get_metadata().parameters == ["z", "x", "y"]
|
||||||
|
|
||||||
def test_i_cannot_eval_bnf_concept_with_unknown_variable(self):
|
def test_i_cannot_eval_bnf_concept_with_unknown_variable(self):
|
||||||
# testing MandatoryVariable
|
# testing MandatoryVariable
|
||||||
context = self.get_context()
|
context = self.get_context()
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from core.concept import Concept
|
||||||
|
from evaluators.ResolveMultiplePluralAmbiguityEvaluator import ResolveMultiplePluralAmbiguityEvaluator
|
||||||
|
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||||
|
from tests.evaluators.EvaluatorTestsUtils import cnode_ret_val, exact, new_plural, pr_ret_val, python_ret_val, sequence, \
|
||||||
|
sya
|
||||||
|
|
||||||
|
|
||||||
|
class TestResolveMultiplePluralAmbiguityEvaluator(TestUsingMemoryBasedSheerka):
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("return_values, expected", [
|
||||||
|
([python_ret_val("numbers"), cnode_ret_val(new_plural("numbers"), parser=sequence)], True),
|
||||||
|
([python_ret_val("numbers"), cnode_ret_val(new_plural("numbers"), parser=sequence), pr_ret_val("other")], True),
|
||||||
|
([python_ret_val("numbers"), cnode_ret_val(new_plural("numbers"), parser=sya)], False),
|
||||||
|
([python_ret_val("numbers"), cnode_ret_val(Concept("numbers"), parser=sequence)], False),
|
||||||
|
([python_ret_val("numbers"), pr_ret_val(new_plural("numbers"), parser=exact)], False),
|
||||||
|
])
|
||||||
|
def test_i_can_match(self, return_values, expected):
|
||||||
|
context = self.get_context()
|
||||||
|
assert ResolveMultiplePluralAmbiguityEvaluator().matches(context, return_values) == expected
|
||||||
|
|
||||||
|
def test_i_can_eval_when_nothing_in_memory(self):
|
||||||
|
sheerka, context = self.init_test().unpack()
|
||||||
|
|
||||||
|
return_values = [
|
||||||
|
python_ret_val("numbers"),
|
||||||
|
cnode_ret_val(new_plural("numbers"), source="source", parser=sequence)
|
||||||
|
]
|
||||||
|
|
||||||
|
evaluator = ResolveMultiplePluralAmbiguityEvaluator()
|
||||||
|
assert evaluator.matches(context, return_values)
|
||||||
|
|
||||||
|
rets = evaluator.eval(context, return_values)
|
||||||
|
assert len(rets) == 1
|
||||||
|
ret = rets[0]
|
||||||
|
assert ret.who == evaluator.name
|
||||||
|
assert ret.status == return_values[1].status
|
||||||
|
assert ret.value == return_values[1].value
|
||||||
|
assert ret.parents == return_values
|
||||||
|
|
||||||
|
def test_i_can_eval_when_plural_in_memory(self):
|
||||||
|
sheerka, context = self.init_test().unpack()
|
||||||
|
sheerka.add_to_memory(context, "numbers", "something")
|
||||||
|
|
||||||
|
return_values = [
|
||||||
|
python_ret_val("numbers"),
|
||||||
|
cnode_ret_val(new_plural("numbers"), source="source", parser=sequence)
|
||||||
|
]
|
||||||
|
|
||||||
|
evaluator = ResolveMultiplePluralAmbiguityEvaluator()
|
||||||
|
assert evaluator.matches(context, return_values)
|
||||||
|
|
||||||
|
rets = evaluator.eval(context, return_values)
|
||||||
|
assert len(rets) == 1
|
||||||
|
ret = rets[0]
|
||||||
|
assert ret.who == evaluator.name
|
||||||
|
assert ret.status == return_values[0].status
|
||||||
|
assert ret.value == return_values[0].value
|
||||||
|
assert ret.parents == return_values
|
||||||
@@ -45,13 +45,35 @@ class TestSheerkaNonRegMemory2(TestUsingMemoryBasedSheerka):
|
|||||||
assert res[0].status
|
assert res[0].status
|
||||||
assert sheerka.isa(sheerka.new("one"), sheerka.new("number"))
|
assert sheerka.isa(sheerka.new("one"), sheerka.new("number"))
|
||||||
|
|
||||||
# def test_i_can_define_plural(self):
|
def test_i_can_get_sequence_when_evaluation_plural(self):
|
||||||
# init = [
|
init = [
|
||||||
# "def concept man",
|
"def concept one",
|
||||||
# "def concept men as set_plural(man) ret man auto_eval True",
|
"def concept two",
|
||||||
# ]
|
"def concept number",
|
||||||
# sheerka = self.init_scenario(init)
|
"global_truth(set_isa(one, number))",
|
||||||
#
|
"global_truth(set_isa(two, number))",
|
||||||
# res = sheerka.evaluate_user_input("men")
|
]
|
||||||
# assert res[0].status
|
sheerka = self.init_scenario(init)
|
||||||
|
|
||||||
|
res = sheerka.evaluate_user_input("eval numbers")
|
||||||
|
|
||||||
|
assert res[0].status
|
||||||
|
assert set(res[0].body) == {sheerka.new("one"), sheerka.new("two")}
|
||||||
|
|
||||||
|
def test_i_can_use_list_comprehension(self):
|
||||||
|
init = [
|
||||||
|
"def concept rex",
|
||||||
|
"def concept rantanplan",
|
||||||
|
"def concept dog",
|
||||||
|
"def concept x is a y as set_isa(x, y)",
|
||||||
|
]
|
||||||
|
sheerka = self.init_scenario(init)
|
||||||
|
|
||||||
|
res = sheerka.evaluate_user_input("global_truth([ x is a dog for x in [rex, rantanplan]])")
|
||||||
|
|
||||||
|
assert len(res) == 1
|
||||||
|
assert res[0].status
|
||||||
|
|
||||||
|
rex = sheerka.new("rex")
|
||||||
|
dog = sheerka.new("dog")
|
||||||
|
assert sheerka.isa(rex, dog)
|
||||||
|
|||||||
@@ -180,3 +180,24 @@ two: (1002)two
|
|||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert " : test()" in captured.out
|
assert " : test()" in captured.out
|
||||||
assert " : history()" in captured.out
|
assert " : history()" in captured.out
|
||||||
|
|
||||||
|
def test_i_can_list_debug_settings(self, capsys):
|
||||||
|
init = [
|
||||||
|
"set_debug_var('Sya.parsers.*', 45)",
|
||||||
|
"set_debug_concept('c:|1015', '13+')",
|
||||||
|
"set_debug_rule('Out')",
|
||||||
|
]
|
||||||
|
sheerka = self.init_scenario(init)
|
||||||
|
capsys.readouterr()
|
||||||
|
|
||||||
|
sheerka.enable_process_return_values = True
|
||||||
|
res = sheerka.evaluate_user_input(f"list_debug_settings()")
|
||||||
|
|
||||||
|
assert len(res) == 1
|
||||||
|
assert res[0].status
|
||||||
|
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert captured.out == """DebugItem(type=vars, setting=Sya.parsers.*, context_id=45, debug_id=None, context_children=False, debug_children=False (enabled=True))
|
||||||
|
DebugItem(type=concepts, setting=c:|1015.*.*, context_id=13, debug_id=None, context_children=True, debug_children=False (enabled=True))
|
||||||
|
DebugItem(type=rules, setting=Out.*.*, context_id=None, debug_id=None, context_children=False, debug_children=False (enabled=True))
|
||||||
|
"""
|
||||||
|
|||||||
+272
-152
@@ -1,24 +1,23 @@
|
|||||||
import ast
|
import ast
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Union, List
|
from typing import List, Union
|
||||||
|
|
||||||
from core.builtin_concepts import ReturnValueConcept
|
from core.builtin_concepts import ReturnValueConcept
|
||||||
from core.builtin_helpers import CreateObjectIdentifiers
|
from core.builtin_helpers import CreateObjectIdentifiers
|
||||||
from core.concept import Concept, ConceptParts, DoNotResolve, AllConceptParts
|
from core.concept import AllConceptParts, Concept, ConceptParts, DoNotResolve
|
||||||
from core.rule import Rule
|
from core.rule import Rule
|
||||||
from core.tokenizer import Tokenizer, TokenKind, Token
|
from core.tokenizer import Token, TokenKind, Tokenizer
|
||||||
from core.utils import get_text_from_tokens, tokens_index, str_concept
|
from core.utils import get_text_from_tokens, str_concept, tokens_index
|
||||||
from parsers.BaseExpressionParser import NameExprNode, AndNode, OrNode, NotNode, VariableNode, ComparisonNode, \
|
from parsers.BaseExpressionParser import AndNode, ComparisonNode, ComparisonType, Comprehension, FunctionParameter, \
|
||||||
ComparisonType, \
|
ListComprehensionNode, ListNode, NameExprNode, \
|
||||||
FunctionParameter
|
NotNode, OrNode, VariableNode, comma
|
||||||
from parsers.BaseNodeParser import UnrecognizedTokensNode, SourceCodeNode, RuleNode, ConceptNode, \
|
from parsers.BaseNodeParser import ConceptNode, RuleNode, SourceCodeNode, SourceCodeWithConceptNode, \
|
||||||
SourceCodeWithConceptNode
|
UnrecognizedTokensNode
|
||||||
from parsers.FunctionParser import FunctionNode
|
from parsers.FunctionParser import FunctionNode
|
||||||
from parsers.PythonParser import PythonNode
|
from parsers.PythonParser import PythonNode
|
||||||
from parsers.SyaNodeParser import SyaConceptParserHelper
|
|
||||||
from sheerkapython.python_wrapper import sheerka_globals
|
from sheerkapython.python_wrapper import sheerka_globals
|
||||||
from sheerkarete.common import V
|
from sheerkarete.common import V
|
||||||
from sheerkarete.conditions import Condition, AndConditions, NegatedCondition, NegatedConjunctiveConditions
|
from sheerkarete.conditions import AndConditions, Condition, NegatedCondition, NegatedConjunctiveConditions
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -29,104 +28,254 @@ class Obj:
|
|||||||
parent: object = None
|
parent: object = None
|
||||||
|
|
||||||
|
|
||||||
class AND:
|
class ExprTestObj:
|
||||||
|
@staticmethod
|
||||||
|
def get_pos(nodes):
|
||||||
|
start, end = None, None
|
||||||
|
for n in nodes:
|
||||||
|
if start is None or start > n.start:
|
||||||
|
start = n.start
|
||||||
|
if end is None or end < n.end:
|
||||||
|
end = n.end
|
||||||
|
return start, end
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_pos_from_source(source, full_text_as_tokens):
|
||||||
|
if isinstance(source, tuple):
|
||||||
|
source, to_skip = source[0], source[1]
|
||||||
|
else:
|
||||||
|
to_skip = 0
|
||||||
|
|
||||||
|
source_as_node = list(Tokenizer(source, yield_eof=False))
|
||||||
|
start = tokens_index(full_text_as_tokens, source_as_node, skip=to_skip)
|
||||||
|
end = start + len(source_as_node) - 1
|
||||||
|
return start, end
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def as_tokens(source):
|
||||||
|
if isinstance(source, tuple):
|
||||||
|
source, to_skip = source
|
||||||
|
else:
|
||||||
|
source, to_skip = source, 0
|
||||||
|
|
||||||
|
return list(Tokenizer(source, yield_eof=False)), to_skip
|
||||||
|
|
||||||
|
def get_expr_node(self, full_text_as_tokens=None):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def safe_get_expr_node(obj, full_text_as_tokens):
|
||||||
|
if obj is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
obj = EXPR(obj) if isinstance(obj, (str, tuple)) else obj
|
||||||
|
return obj.get_expr_node(full_text_as_tokens)
|
||||||
|
|
||||||
|
|
||||||
|
class AND(ExprTestObj):
|
||||||
""" Test class for AndNode"""
|
""" Test class for AndNode"""
|
||||||
|
|
||||||
def __init__(self, *parts, source=None):
|
def __init__(self, *parts, source=None):
|
||||||
self.parts = parts
|
self.parts = parts
|
||||||
self.source = source
|
self.source = source
|
||||||
|
|
||||||
|
def get_expr_node(self, full_text_as_tokens=None):
|
||||||
|
parts = [part.get_expr_node(full_text_as_tokens) for part in self.parts]
|
||||||
|
start, end = self.get_pos_from_source(self.source, full_text_as_tokens) if self.source else self.get_pos(parts)
|
||||||
|
return AndNode(start, end, full_text_as_tokens[start: end + 1], *parts)
|
||||||
|
|
||||||
class OR:
|
|
||||||
|
class OR(ExprTestObj):
|
||||||
""" Test class for OrNode"""
|
""" Test class for OrNode"""
|
||||||
|
|
||||||
def __init__(self, *parts, source=None):
|
def __init__(self, *parts, source=None):
|
||||||
self.parts = parts
|
self.parts = parts
|
||||||
self.source = source
|
self.source = source
|
||||||
|
|
||||||
|
def get_expr_node(self, full_text_as_tokens=None):
|
||||||
|
parts = [part.get_expr_node(full_text_as_tokens) for part in self.parts]
|
||||||
|
start, end = self.get_pos_from_source(self.source, full_text_as_tokens) if self.source else self.get_pos(parts)
|
||||||
|
return OrNode(start, end, full_text_as_tokens[start: end + 1], *parts)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class NOT:
|
class NOT(ExprTestObj):
|
||||||
""" Test class for NotNode"""
|
""" Test class for NotNode"""
|
||||||
expr: object
|
expr: ExprTestObj
|
||||||
source: str = None
|
source: str = None
|
||||||
|
|
||||||
|
def get_expr_node(self, full_text_as_tokens=None):
|
||||||
|
part = self.expr.get_expr_node(full_text_as_tokens)
|
||||||
|
start, end = self.get_pos_from_source(self.source, full_text_as_tokens) if self.source else (
|
||||||
|
part.start - 2, part.end)
|
||||||
|
return NotNode(start, end, full_text_as_tokens[start: end + 1], part)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class EXPR:
|
class EXPR(ExprTestObj):
|
||||||
"""Test class for NameNode. E stands for Expression"""
|
"""Test class for NameNode"""
|
||||||
source: str
|
source: str
|
||||||
|
|
||||||
|
def get_expr_node(self, full_text_as_tokens=None):
|
||||||
|
value_as_tokens, to_skip = self.as_tokens(self.source)
|
||||||
|
start = tokens_index(full_text_as_tokens, value_as_tokens, to_skip)
|
||||||
|
end = start + len(value_as_tokens) - 1
|
||||||
|
return NameExprNode(start, end, full_text_as_tokens[start: end + 1])
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class VAR:
|
class VAR(ExprTestObj):
|
||||||
"""Test class for VarNode"""
|
"""Test class for VarNode"""
|
||||||
|
|
||||||
full_name: str
|
full_name: str
|
||||||
source: str = None
|
source: str = None
|
||||||
|
|
||||||
|
def get_expr_node(self, full_text_as_tokens=None):
|
||||||
|
value_as_tokens = list(Tokenizer(self.source or self.full_name, yield_eof=False))
|
||||||
|
start = tokens_index(full_text_as_tokens, value_as_tokens, 0)
|
||||||
|
end = start + len(value_as_tokens) - 1
|
||||||
|
parts = self.full_name.split(".")
|
||||||
|
if len(parts) == 1:
|
||||||
|
return VariableNode(start, end, full_text_as_tokens[start: end + 1], parts[0])
|
||||||
|
else:
|
||||||
|
return VariableNode(start, end, full_text_as_tokens[start: end + 1], parts[0], *parts[1:])
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class EQ:
|
class CompExprTestObj(ExprTestObj):
|
||||||
left: object
|
"""
|
||||||
right: object
|
Test object for comparison ==, <=, ...
|
||||||
|
"""
|
||||||
|
left: ExprTestObj
|
||||||
|
right: ExprTestObj
|
||||||
source: str = None
|
source: str = None
|
||||||
|
|
||||||
|
def get_expr_node(self, full_text_as_tokens=None):
|
||||||
@dataclass
|
node_type = comparison_type_mapping[type(self).__name__]
|
||||||
class NEQ:
|
left_node = self.left.get_expr_node(full_text_as_tokens)
|
||||||
left: object
|
right_node = self.right.get_expr_node(full_text_as_tokens)
|
||||||
right: object
|
start, end = self.get_pos_from_source(self.source, full_text_as_tokens) if self.source else \
|
||||||
source: str = None
|
self.get_pos([left_node, right_node])
|
||||||
|
return ComparisonNode(start, end, full_text_as_tokens[start: end + 1], node_type, left_node, right_node)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class GT:
|
class EQ(CompExprTestObj):
|
||||||
left: object
|
pass
|
||||||
right: object
|
|
||||||
source: str = None
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class GTE:
|
class NEQ(CompExprTestObj):
|
||||||
left: object
|
pass
|
||||||
right: object
|
|
||||||
source: str = None
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class LT:
|
class GT(CompExprTestObj):
|
||||||
left: object
|
pass
|
||||||
right: object
|
|
||||||
source: str = None
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class LTE:
|
class GTE(CompExprTestObj):
|
||||||
left: object
|
pass
|
||||||
right: object
|
|
||||||
source: str = None
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class IN:
|
class LT(CompExprTestObj):
|
||||||
left: object
|
pass
|
||||||
right: object
|
|
||||||
source: str = None
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class NIN: # for NOT INT
|
class LTE(CompExprTestObj):
|
||||||
left: object
|
pass
|
||||||
right: object
|
|
||||||
source: str = None
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PAREN: # for parenthesis node
|
class IN(CompExprTestObj):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class NIN(CompExprTestObj): # for NOT INT
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PAREN(ExprTestObj): # for parenthesis node
|
||||||
node: object
|
node: object
|
||||||
source: str = None
|
source: str = None
|
||||||
|
|
||||||
|
|
||||||
|
class L_EXPR(ExprTestObj):
|
||||||
|
def __init__(self, first, last, *items, sep=None, source=None):
|
||||||
|
self.first = first
|
||||||
|
self.last = last
|
||||||
|
self.items = items
|
||||||
|
self.sep = sep or comma
|
||||||
|
self.source = source
|
||||||
|
|
||||||
|
def get_expr_node(self, full_text_as_tokens=None):
|
||||||
|
first = self.safe_get_expr_node(self.first, full_text_as_tokens)
|
||||||
|
last = self.safe_get_expr_node(self.last, full_text_as_tokens)
|
||||||
|
|
||||||
|
items = [self.safe_get_expr_node(item, full_text_as_tokens) for item in self.items]
|
||||||
|
|
||||||
|
if self.source is None:
|
||||||
|
source = self.first if self.first else ""
|
||||||
|
source += f"{self.sep.value} ".join(item.get_source() for item in items)
|
||||||
|
if self.last:
|
||||||
|
source += self.last
|
||||||
|
else:
|
||||||
|
source = self.source
|
||||||
|
|
||||||
|
start, end = self.get_pos_from_source(source, full_text_as_tokens)
|
||||||
|
return ListNode(start, end, full_text_as_tokens[start: end + 1], first, last, items, self.sep)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LCC:
|
||||||
|
"""
|
||||||
|
List comprehension comprehension
|
||||||
|
"""
|
||||||
|
target: object
|
||||||
|
iterable: object
|
||||||
|
if_expr: object
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LC(ExprTestObj): # for List Comprehension node
|
||||||
|
element: object
|
||||||
|
generators: list
|
||||||
|
source: str = None
|
||||||
|
|
||||||
|
def get_expr_node(self, full_text_as_tokens=None):
|
||||||
|
# first transform str into NameExprTestObj (ie EXPR)
|
||||||
|
if isinstance(self.element, str):
|
||||||
|
self.element = EXPR(self.element)
|
||||||
|
|
||||||
|
comprehensions = []
|
||||||
|
nodes = []
|
||||||
|
for comp in self.generators:
|
||||||
|
target = EXPR(comp[0]) if isinstance(comp[0], (str, tuple)) else comp[0]
|
||||||
|
iterable = EXPR(comp[1]) if isinstance(comp[1], (str, tuple)) else comp[1]
|
||||||
|
if_expr = EXPR(comp[2]) if isinstance(comp[2], (str, tuple)) else comp[2]
|
||||||
|
comprehensions.append(LCC(target, iterable, if_expr))
|
||||||
|
self.generators = comprehensions
|
||||||
|
|
||||||
|
# then transform into ListComprehensionNode
|
||||||
|
element = self.element.get_expr_node(full_text_as_tokens)
|
||||||
|
nodes.append(element)
|
||||||
|
comprehensions = []
|
||||||
|
for comp in self.generators:
|
||||||
|
target = comp.target.get_expr_node(full_text_as_tokens)
|
||||||
|
iterable = comp.iterable.get_expr_node(full_text_as_tokens)
|
||||||
|
if_expr = comp.if_expr.get_expr_node(full_text_as_tokens) if comp.if_expr else None
|
||||||
|
comprehensions.append(Comprehension(target, iterable, if_expr))
|
||||||
|
nodes.extend([target, iterable, if_expr])
|
||||||
|
|
||||||
|
start, end = self.get_pos_from_source(self.source, full_text_as_tokens) if self.source else self.get_pos(nodes)
|
||||||
|
return ListComprehensionNode(start, end, full_text_as_tokens[start: end + 1], element, comprehensions)
|
||||||
|
|
||||||
|
|
||||||
class CC:
|
class CC:
|
||||||
"""
|
"""
|
||||||
Concept class for test purpose
|
Concept class for test purpose
|
||||||
@@ -320,10 +469,13 @@ class CMV:
|
|||||||
Test class that only compare the key and the metadata variables
|
Test class that only compare the key and the metadata variables
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, concept, **kwargs):
|
def __init__(self, concept, source=None, **kwargs):
|
||||||
self.concept_key = concept.key if isinstance(concept, Concept) else concept
|
self.concept_key = concept.key if isinstance(concept, Concept) else concept
|
||||||
self.concept = concept if isinstance(concept, Concept) else None
|
self.concept = concept if isinstance(concept, Concept) else None
|
||||||
self.variables = kwargs
|
self.variables = kwargs
|
||||||
|
self.source = source # to use when the key is different from the sub str to search when filling start and stop
|
||||||
|
self.start = None # for debug purpose, indicate where the concept starts
|
||||||
|
self.end = None # for debug purpose, indicate where the concept ends
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if id(self) == id(other):
|
if id(self) == id(other):
|
||||||
@@ -352,6 +504,21 @@ class CMV:
|
|||||||
txt += f", {k}='{v}'"
|
txt += f", {k}='{v}'"
|
||||||
return txt + ")"
|
return txt + ")"
|
||||||
|
|
||||||
|
def fix_pos(self, node):
|
||||||
|
start = node.start if hasattr(node, "start") else \
|
||||||
|
node[0] if isinstance(node, tuple) else None
|
||||||
|
end = node.end if hasattr(node, "end") else \
|
||||||
|
node[1] if isinstance(node, tuple) else None
|
||||||
|
|
||||||
|
if start is not None:
|
||||||
|
if self.start is None or start < self.start:
|
||||||
|
self.start = start
|
||||||
|
|
||||||
|
if end is not None:
|
||||||
|
if self.end is None or end > self.end:
|
||||||
|
self.end = end
|
||||||
|
return self
|
||||||
|
|
||||||
def transform_real_obj(self, other, get_test_obj_delegate):
|
def transform_real_obj(self, other, get_test_obj_delegate):
|
||||||
if isinstance(other, CMV):
|
if isinstance(other, CMV):
|
||||||
return other
|
return other
|
||||||
@@ -730,7 +897,7 @@ class CNC(CN):
|
|||||||
|
|
||||||
self_compile_to_use = self.compiled or compiled
|
self_compile_to_use = self.compiled or compiled
|
||||||
|
|
||||||
compiled = get_test_obj_delegate(self_compile_to_use, compiled, get_test_obj_delegate)
|
compiled = get_test_obj_delegate(compiled, self_compile_to_use, get_test_obj_delegate)
|
||||||
return CNC(other.concept,
|
return CNC(other.concept,
|
||||||
other.source if self.source is not None else None,
|
other.source if self.source is not None else None,
|
||||||
other.start if self.start is not None else None,
|
other.start if self.start is not None else None,
|
||||||
@@ -865,7 +1032,7 @@ class RN(HelperWithPos):
|
|||||||
raise Exception(f"Expecting RuleNode but received {other=}")
|
raise Exception(f"Expecting RuleNode but received {other=}")
|
||||||
|
|
||||||
|
|
||||||
class FN:
|
class FN(ExprTestObj):
|
||||||
"""
|
"""
|
||||||
Test class only
|
Test class only
|
||||||
It matches with FunctionNode but with less constraints
|
It matches with FunctionNode but with less constraints
|
||||||
@@ -931,6 +1098,32 @@ class FN:
|
|||||||
|
|
||||||
raise Exception(f"Expecting FunctionNode but received {other=}")
|
raise Exception(f"Expecting FunctionNode but received {other=}")
|
||||||
|
|
||||||
|
def get_expr_node(self, full_text_as_tokens=None):
|
||||||
|
start, end = self.get_pos_from_source(self.first, full_text_as_tokens)
|
||||||
|
first = NameExprNode(start, end, full_text_as_tokens[start: end + 1])
|
||||||
|
start, end = self.get_pos_from_source(self.last, full_text_as_tokens)
|
||||||
|
last = NameExprNode(start, end, full_text_as_tokens[start: end + 1])
|
||||||
|
parameters = []
|
||||||
|
for param_value, sep in self.parameters:
|
||||||
|
if isinstance(param_value, str):
|
||||||
|
start, end = self.get_pos_from_source(param_value, full_text_as_tokens)
|
||||||
|
param_as_expr_node = NameExprNode(start, end, full_text_as_tokens[start: end + 1])
|
||||||
|
else:
|
||||||
|
param_as_expr_node = param_value.get_expr_node(full_text_as_tokens)
|
||||||
|
|
||||||
|
if sep:
|
||||||
|
sep_tokens = Tokenizer(sep, yield_eof=False)
|
||||||
|
start = param_as_expr_node.end + 1
|
||||||
|
end = start + len(list(sep_tokens)) - 1
|
||||||
|
sep_as_expr_node = NameExprNode(start, end, full_text_as_tokens[start: end + 1])
|
||||||
|
else:
|
||||||
|
sep_as_expr_node = None
|
||||||
|
|
||||||
|
parameters.append(FunctionParameter(param_as_expr_node, sep_as_expr_node))
|
||||||
|
|
||||||
|
start, end = first.start, last.end
|
||||||
|
return FunctionNode(start, end, full_text_as_tokens[start: end + 1], first, last, parameters)
|
||||||
|
|
||||||
|
|
||||||
@dataclass()
|
@dataclass()
|
||||||
class NEGCOND:
|
class NEGCOND:
|
||||||
@@ -966,94 +1159,7 @@ def get_expr_node_from_test_node(full_text, test_node):
|
|||||||
Returns EXPR, OR, NOT, AND object to ease the comparison with the real ExprNode
|
Returns EXPR, OR, NOT, AND object to ease the comparison with the real ExprNode
|
||||||
"""
|
"""
|
||||||
full_text_as_tokens = list(Tokenizer(full_text, yield_eof=False))
|
full_text_as_tokens = list(Tokenizer(full_text, yield_eof=False))
|
||||||
|
return test_node.get_expr_node(full_text_as_tokens)
|
||||||
def get_pos(nodes):
|
|
||||||
start, end = None, None
|
|
||||||
for n in nodes:
|
|
||||||
if start is None or start > n.start:
|
|
||||||
start = n.start
|
|
||||||
if end is None or end < n.end:
|
|
||||||
end = n.end
|
|
||||||
return start, end
|
|
||||||
|
|
||||||
def get_pos_from_source(source):
|
|
||||||
if isinstance(source, tuple):
|
|
||||||
source, to_skip = source[0], source[1]
|
|
||||||
else:
|
|
||||||
to_skip = 0
|
|
||||||
|
|
||||||
source_as_node = list(Tokenizer(source, yield_eof=False))
|
|
||||||
start = tokens_index(full_text_as_tokens, source_as_node, skip=to_skip)
|
|
||||||
end = start + len(source_as_node) - 1
|
|
||||||
return start, end
|
|
||||||
|
|
||||||
def get_expr_node(node):
|
|
||||||
|
|
||||||
if isinstance(node, EXPR):
|
|
||||||
value_as_tokens = list(Tokenizer(node.source, yield_eof=False))
|
|
||||||
start = tokens_index(full_text_as_tokens, value_as_tokens, 0)
|
|
||||||
end = start + len(value_as_tokens) - 1
|
|
||||||
return NameExprNode(start, end, full_text_as_tokens[start: end + 1])
|
|
||||||
|
|
||||||
if isinstance(node, AND):
|
|
||||||
parts = [get_expr_node(part) for part in node.parts]
|
|
||||||
start, end = get_pos_from_source(node.source) if node.source else get_pos(parts)
|
|
||||||
return AndNode(start, end, full_text_as_tokens[start: end + 1], *parts)
|
|
||||||
|
|
||||||
if isinstance(node, OR):
|
|
||||||
parts = [get_expr_node(part) for part in node.parts]
|
|
||||||
start, end = get_pos_from_source(node.source) if node.source else get_pos(parts)
|
|
||||||
return OrNode(start, end, full_text_as_tokens[start: end + 1], *parts)
|
|
||||||
|
|
||||||
if isinstance(node, NOT):
|
|
||||||
part = get_expr_node(node.expr)
|
|
||||||
start, end = get_pos_from_source(node.source) if node.source else (part.start - 2, part.end)
|
|
||||||
return NotNode(start, end, full_text_as_tokens[start: end + 1], part)
|
|
||||||
|
|
||||||
if isinstance(node, VAR):
|
|
||||||
value_as_tokens = list(Tokenizer(node.source or node.full_name, yield_eof=False))
|
|
||||||
start = tokens_index(full_text_as_tokens, value_as_tokens, 0)
|
|
||||||
end = start + len(value_as_tokens) - 1
|
|
||||||
parts = node.full_name.split(".")
|
|
||||||
if len(parts) == 1:
|
|
||||||
return VariableNode(start, end, full_text_as_tokens[start: end + 1], parts[0])
|
|
||||||
else:
|
|
||||||
return VariableNode(start, end, full_text_as_tokens[start: end + 1], parts[0], *parts[1:])
|
|
||||||
|
|
||||||
if isinstance(node, (EQ, NEQ, GT, GTE, LT, LTE, IN, NIN)):
|
|
||||||
node_type = comparison_type_mapping[type(node).__name__]
|
|
||||||
left_node, right_node = get_expr_node(node.left), get_expr_node(node.right)
|
|
||||||
start, end = get_pos_from_source(node.source) if node.source else get_pos([left_node, right_node])
|
|
||||||
return ComparisonNode(start, end, full_text_as_tokens[start: end + 1],
|
|
||||||
node_type, left_node, right_node)
|
|
||||||
|
|
||||||
if isinstance(node, FN):
|
|
||||||
start, end = get_pos_from_source(node.first)
|
|
||||||
first = NameExprNode(start, end, full_text_as_tokens[start: end + 1])
|
|
||||||
start, end = get_pos_from_source(node.last)
|
|
||||||
last = NameExprNode(start, end, full_text_as_tokens[start: end + 1])
|
|
||||||
parameters = []
|
|
||||||
for param_value, sep in node.parameters:
|
|
||||||
if isinstance(param_value, str):
|
|
||||||
start, end = get_pos_from_source(param_value)
|
|
||||||
param_as_expr_node = NameExprNode(start, end, full_text_as_tokens[start: end + 1])
|
|
||||||
else:
|
|
||||||
param_as_expr_node = get_expr_node(param_value)
|
|
||||||
|
|
||||||
if sep:
|
|
||||||
sep_tokens = Tokenizer(sep, yield_eof=False)
|
|
||||||
start = param_as_expr_node.end + 1
|
|
||||||
end = start + len(list(sep_tokens)) - 1
|
|
||||||
sep_as_expr_node = NameExprNode(start, end, full_text_as_tokens[start: end + 1])
|
|
||||||
else:
|
|
||||||
sep_as_expr_node = None
|
|
||||||
|
|
||||||
parameters.append(FunctionParameter(param_as_expr_node, sep_as_expr_node))
|
|
||||||
|
|
||||||
start, end = first.start, last.end
|
|
||||||
return FunctionNode(start, end, full_text_as_tokens[start: end + 1], first, last, parameters)
|
|
||||||
|
|
||||||
return get_expr_node(test_node)
|
|
||||||
|
|
||||||
|
|
||||||
def _index(tokens, expr, index):
|
def _index(tokens, expr, index):
|
||||||
@@ -1157,7 +1263,8 @@ def get_node(
|
|||||||
sub_expr.end = start + length - 1
|
sub_expr.end = start + length - 1
|
||||||
return sub_expr
|
return sub_expr
|
||||||
|
|
||||||
if isinstance(sub_expr, (CNC, CC, CN)):
|
if isinstance(sub_expr, (CNC, CC, CN, CMV)):
|
||||||
|
if sub_expr.concept is None or sub_expr.start is None or sub_expr.end is None:
|
||||||
concept_node = get_node(
|
concept_node = get_node(
|
||||||
concepts_map,
|
concepts_map,
|
||||||
expression_as_tokens,
|
expression_as_tokens,
|
||||||
@@ -1168,7 +1275,8 @@ def get_node(
|
|||||||
concept_found = concept_node.concept
|
concept_found = concept_node.concept
|
||||||
sub_expr.concept_key = concept_found.key
|
sub_expr.concept_key = concept_found.key
|
||||||
sub_expr.concept = concept_found
|
sub_expr.concept = concept_found
|
||||||
sub_expr.fix_pos((concept_node.start, concept_node.end if hasattr(concept_node, "end") else concept_node.start))
|
sub_expr.fix_pos(
|
||||||
|
(concept_node.start, concept_node.end if hasattr(concept_node, "end") else concept_node.start))
|
||||||
if hasattr(sub_expr, "compiled"):
|
if hasattr(sub_expr, "compiled"):
|
||||||
for k, v in sub_expr.compiled.items():
|
for k, v in sub_expr.compiled.items():
|
||||||
node = get_node(concepts_map, expression_as_tokens, v, sya=sya,
|
node = get_node(concepts_map, expression_as_tokens, v, sya=sya,
|
||||||
@@ -1210,9 +1318,9 @@ def get_node(
|
|||||||
concept_found = concepts_map.get(concept_key, None)
|
concept_found = concepts_map.get(concept_key, None)
|
||||||
if concept_found:
|
if concept_found:
|
||||||
concept_found = Concept().update_from(concept_found) # make a copy when massively used in tests
|
concept_found = Concept().update_from(concept_found) # make a copy when massively used in tests
|
||||||
if sya and len(concept_found.get_metadata().variables) > 0 and not is_bnf:
|
# if sya and len(concept_found.get_metadata().variables) > 0 and not is_bnf:
|
||||||
return SyaConceptParserHelper(concept_found, start, start + length - 1)
|
# return SyaConceptParserHelper(concept_found, start, start + length - 1)
|
||||||
elif init_empty_body:
|
if init_empty_body:
|
||||||
node = CNC(concept_found, sub_expr, start, start + length - 1, exclude_body=exclude_body)
|
node = CNC(concept_found, sub_expr, start, start + length - 1, exclude_body=exclude_body)
|
||||||
init_body(node, concept_found, sub_expr)
|
init_body(node, concept_found, sub_expr)
|
||||||
return node
|
return node
|
||||||
@@ -1354,8 +1462,8 @@ def get_test_obj(real_obj, test_obj, get_test_obj_delegate=None):
|
|||||||
"""
|
"""
|
||||||
From a production object (Concept, ConceptNode, ....)
|
From a production object (Concept, ConceptNode, ....)
|
||||||
Create a test object (CNC, CC ...) that can be used to validate the unit tests
|
Create a test object (CNC, CC ...) that can be used to validate the unit tests
|
||||||
:param test_obj:
|
|
||||||
:param real_obj:
|
:param real_obj:
|
||||||
|
:param test_obj: test object used as a template
|
||||||
:param get_test_obj_delegate:
|
:param get_test_obj_delegate:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
@@ -1367,13 +1475,25 @@ def get_test_obj(real_obj, test_obj, get_test_obj_delegate=None):
|
|||||||
if isinstance(test_obj, dict):
|
if isinstance(test_obj, dict):
|
||||||
if len(test_obj) != len(real_obj):
|
if len(test_obj) != len(real_obj):
|
||||||
raise Exception(f"Not the same size ! {real_obj=}, {test_obj=}")
|
raise Exception(f"Not the same size ! {real_obj=}, {test_obj=}")
|
||||||
|
|
||||||
return {k: get_test_obj(real_obj[k], v) for k, v in test_obj.items()}
|
return {k: get_test_obj(real_obj[k], v) for k, v in test_obj.items()}
|
||||||
|
|
||||||
if not hasattr(test_obj, "transform_real_obj"):
|
if hasattr(test_obj, "transform_real_obj"):
|
||||||
|
return test_obj.transform_real_obj(real_obj, get_test_obj)
|
||||||
|
|
||||||
return real_obj
|
return real_obj
|
||||||
|
|
||||||
return test_obj.transform_real_obj(real_obj, get_test_obj)
|
|
||||||
|
def prepare_nodes_comparison(concepts_map, expression, real_obj, test_obj):
|
||||||
|
if isinstance(real_obj, list):
|
||||||
|
assert len(real_obj) == len(
|
||||||
|
test_obj), f"The two lists do not have the same size {len(real_obj)} != {len(test_obj)}"
|
||||||
|
resolved_test_obj = compute_expected_array(concepts_map, expression, test_obj)
|
||||||
|
real_obj_as_test = [get_test_obj(r, t) for r, t in zip(real_obj, resolved_test_obj)]
|
||||||
|
return real_obj_as_test, resolved_test_obj
|
||||||
|
else:
|
||||||
|
resolved_test_obj = compute_expected_array(concepts_map, expression, [test_obj])[0]
|
||||||
|
real_obj_as_test = get_test_obj(real_obj, resolved_test_obj)
|
||||||
|
return real_obj_as_test, resolved_test_obj
|
||||||
|
|
||||||
|
|
||||||
def compare_with_test_object(actual, expected):
|
def compare_with_test_object(actual, expected):
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ func(a)
|
|||||||
assert parser.get_parts(["print", "when"]) is not None
|
assert parser.get_parts(["print", "when"]) is not None
|
||||||
assert len(parser.error_sink) == 1
|
assert len(parser.error_sink) == 1
|
||||||
assert isinstance(parser.error_sink[0], UnexpectedEofParsingError)
|
assert isinstance(parser.error_sink[0], UnexpectedEofParsingError)
|
||||||
assert parser.error_sink[0].message == "While parsing keyword 'print'."
|
assert parser.error_sink[0].message == "while parsing keyword 'print'"
|
||||||
|
|
||||||
def test_i_can_double_quoted_strings_are_expanded(self):
|
def test_i_can_double_quoted_strings_are_expanded(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -4,19 +4,20 @@ import pytest
|
|||||||
|
|
||||||
import tests.parsers.parsers_utils
|
import tests.parsers.parsers_utils
|
||||||
from core.builtin_concepts import BuiltinConcepts
|
from core.builtin_concepts import BuiltinConcepts
|
||||||
from core.concept import Concept, ConceptParts, DoNotResolve, DEFINITION_TYPE_BNF
|
from core.concept import Concept, ConceptParts, DEFINITION_TYPE_BNF, DoNotResolve
|
||||||
from core.global_symbols import NotInit
|
from core.global_symbols import NotInit
|
||||||
from core.sheerka.services.SheerkaConceptManager import SheerkaConceptManager
|
from core.sheerka.services.SheerkaConceptManager import SheerkaConceptManager
|
||||||
from core.sheerka.services.SheerkaExecute import ParserInput
|
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||||
|
from core.sheerka.services.SheerkaIsAManager import SheerkaIsAManager
|
||||||
from parsers.BaseNodeParser import NoMatchingTokenError
|
from parsers.BaseNodeParser import NoMatchingTokenError
|
||||||
from parsers.BnfDefinitionParser import BnfDefinitionParser
|
from parsers.BnfDefinitionParser import BnfDefinitionParser
|
||||||
from parsers.BnfNodeParser import StrMatch, TerminalNode, NonTerminalNode, Sequence, OrderedChoice, \
|
from parsers.BnfNodeParser import BnfNodeFirstTokenVisitor, BnfNodeParser, ConceptExpression, Match, NonTerminalNode, \
|
||||||
Optional, ZeroOrMore, OneOrMore, ConceptExpression, UnOrderedChoice, BnfNodeParser, RegExMatch, \
|
OneOrMore, Optional, OrderedChoice, RegExDef, RegExMatch, Sequence, StrMatch, TerminalNode, UnOrderedChoice, \
|
||||||
BnfNodeFirstTokenVisitor, Match, RegExDef, VariableExpression
|
VariableExpression, ZeroOrMore
|
||||||
from tests.BaseTest import BaseTest
|
from tests.BaseTest import BaseTest
|
||||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||||
from tests.evaluators.EvaluatorTestsUtils import python_ret_val
|
from tests.evaluators.EvaluatorTestsUtils import python_ret_val
|
||||||
from tests.parsers.parsers_utils import CNC, CN, UTN, CC, SCN, get_test_obj, compare_with_test_object
|
from tests.parsers.parsers_utils import CC, CMV, CN, CNC, SCN, UTN, compare_with_test_object, get_test_obj
|
||||||
|
|
||||||
cmap = {
|
cmap = {
|
||||||
"one": Concept("one"),
|
"one": Concept("one"),
|
||||||
@@ -1027,7 +1028,7 @@ class TestBnfNodeParser(TestUsingMemoryBasedSheerka):
|
|||||||
("one tiny but beautiful shoe",
|
("one tiny but beautiful shoe",
|
||||||
[CNC("foo",
|
[CNC("foo",
|
||||||
"one tiny but beautiful shoe",
|
"one tiny but beautiful shoe",
|
||||||
x=CC("but", source="tiny but beautiful", x="tiny", y="beautiful "))]),
|
x=CMV("but", source="tiny but beautiful", x="tiny ", y="beautiful "))]),
|
||||||
])
|
])
|
||||||
def test_i_can_match_variable_in_between(self, expr, expected):
|
def test_i_can_match_variable_in_between(self, expr, expected):
|
||||||
my_map = {
|
my_map = {
|
||||||
@@ -1896,7 +1897,7 @@ class TestBnfNodeParser(TestUsingMemoryBasedSheerka):
|
|||||||
|
|
||||||
def test_i_can_simplify_unordered_choices_that_refer_to_the_same_isa(self):
|
def test_i_can_simplify_unordered_choices_that_refer_to_the_same_isa(self):
|
||||||
my_map = {
|
my_map = {
|
||||||
"light_red": Concept("light red"),
|
"light_red": Concept("light red", key="light_red"),
|
||||||
"dark_red": Concept("dark red"),
|
"dark_red": Concept("dark red"),
|
||||||
"red colors": Concept("red colors"),
|
"red colors": Concept("red colors"),
|
||||||
"color": Concept("color"),
|
"color": Concept("color"),
|
||||||
@@ -1916,6 +1917,10 @@ class TestBnfNodeParser(TestUsingMemoryBasedSheerka):
|
|||||||
sheerka.set_isa(global_truth_context, my_map["red colors"], my_map["color"])
|
sheerka.set_isa(global_truth_context, my_map["red colors"], my_map["color"])
|
||||||
sheerka.set_isa(global_truth_context, my_map["red colors"], my_map["adjective"])
|
sheerka.set_isa(global_truth_context, my_map["red colors"], my_map["adjective"])
|
||||||
|
|
||||||
|
# hack to ease the tests
|
||||||
|
sheerka.get_by_id(my_map["light_red"].id).get_metadata().key = "light_red"
|
||||||
|
sheerka.om.clear(SheerkaIsAManager.CONCEPTS_IN_GROUPS_ENTRY)
|
||||||
|
|
||||||
text = "light red table"
|
text = "light red table"
|
||||||
|
|
||||||
expected = CNC("qualified table",
|
expected = CNC("qualified table",
|
||||||
@@ -1953,7 +1958,6 @@ class TestBnfNodeParser(TestUsingMemoryBasedSheerka):
|
|||||||
res = parser.parse(context, ParserInput(text))
|
res = parser.parse(context, ParserInput(text))
|
||||||
assert not res.status
|
assert not res.status
|
||||||
|
|
||||||
|
|
||||||
# @pytest.mark.parametrize("parser_input, expected", [
|
# @pytest.mark.parametrize("parser_input, expected", [
|
||||||
# ("one", [
|
# ("one", [
|
||||||
# (True, [CNC("bnf_one", source="one", one="one", body="one")]),
|
# (True, [CNC("bnf_one", source="one", one="one", body="one")]),
|
||||||
|
|||||||
@@ -387,8 +387,8 @@ def concept add one to a as:
|
|||||||
("def concept name from def", SyntaxErrorNode([], "Empty 'from' declaration.")),
|
("def concept name from def", SyntaxErrorNode([], "Empty 'from' declaration.")),
|
||||||
("def concept name from def ", SyntaxErrorNode([], "Empty 'from' declaration.")),
|
("def concept name from def ", SyntaxErrorNode([], "Empty 'from' declaration.")),
|
||||||
("def concept name from as True", SyntaxErrorNode([], "Empty 'from' declaration.")),
|
("def concept name from as True", SyntaxErrorNode([], "Empty 'from' declaration.")),
|
||||||
("def concept name from", UnexpectedEofParsingError("While parsing keyword 'from'.")),
|
("def concept name from", UnexpectedEofParsingError("while parsing keyword 'from'")),
|
||||||
("def concept name from ", UnexpectedEofParsingError("While parsing keyword 'from'.")),
|
("def concept name from ", UnexpectedEofParsingError("while parsing keyword 'from'")),
|
||||||
])
|
])
|
||||||
def test_i_can_detect_empty_def_declaration(self, text, error):
|
def test_i_can_detect_empty_def_declaration(self, text, error):
|
||||||
sheerka, context, parser, *concepts = self.init_parser()
|
sheerka, context, parser, *concepts = self.init_parser()
|
||||||
|
|||||||
@@ -190,8 +190,8 @@ class TestDefRuleParser(TestUsingMemoryBasedSheerka):
|
|||||||
assert sheerka.isinstance(res.body, expected_error)
|
assert sheerka.isinstance(res.body, expected_error)
|
||||||
|
|
||||||
@pytest.mark.parametrize("text, error_message", [
|
@pytest.mark.parametrize("text, error_message", [
|
||||||
("def rule rule_name as", "While parsing 'when'."),
|
("def rule rule_name as", "while parsing 'when'"),
|
||||||
("def rule rule_name as ", "While parsing 'when'."),
|
("def rule rule_name as ", "while parsing 'when'"),
|
||||||
])
|
])
|
||||||
def test_i_cannot_parse_when_unexpected_eof(self, text, error_message):
|
def test_i_cannot_parse_when_unexpected_eof(self, text, error_message):
|
||||||
sheerka, context, parser = self.init_parser()
|
sheerka, context, parser = self.init_parser()
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from core.builtin_concepts_ids import BuiltinConcepts
|
||||||
|
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||||
|
from core.tokenizer import Token, TokenKind
|
||||||
|
from parsers.BaseExpressionParser import ParenthesisMismatchError
|
||||||
|
from parsers.BaseParser import UnexpectedEofParsingError, UnexpectedTokenParsingError
|
||||||
|
from parsers.ListComprehensionParser import ElementNotFound, FailedToParse, ForNotFound, LeadingParenthesisNotFound, \
|
||||||
|
ListComprehensionParser
|
||||||
|
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||||
|
from tests.parsers.parsers_utils import LC, L_EXPR, get_expr_node_from_test_node
|
||||||
|
|
||||||
|
|
||||||
|
class TestListComprehensionParser(TestUsingMemoryBasedSheerka):
|
||||||
|
|
||||||
|
def init_parser(self):
|
||||||
|
sheerka, context = self.init_concepts()
|
||||||
|
parser = ListComprehensionParser(auto_compile=False)
|
||||||
|
return sheerka, context, parser
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("text, reason", [
|
||||||
|
("foo", LeadingParenthesisNotFound()),
|
||||||
|
("[]", ForNotFound()),
|
||||||
|
("[ x ]", ForNotFound()),
|
||||||
|
("[ x for]", FailedToParse("target", 5)),
|
||||||
|
("[ x for x]", UnexpectedEofParsingError("while parsing comprehension")),
|
||||||
|
("[ x for x in ]", UnexpectedEofParsingError("while parsing comprehension")),
|
||||||
|
("[ x for x in lst for]", FailedToParse("target", 13)),
|
||||||
|
("[", UnexpectedEofParsingError("when start parsing")),
|
||||||
|
("[]", ForNotFound()),
|
||||||
|
("[ for x in z ]", ElementNotFound()),
|
||||||
|
("[ x for in z ]", FailedToParse("target", 6)),
|
||||||
|
("[ x for x in ]", UnexpectedEofParsingError("while parsing comprehension")),
|
||||||
|
("[ x for x in z if ]", UnexpectedEofParsingError("while parsing comprehension")),
|
||||||
|
("[ x for x in z", ParenthesisMismatchError(Token(TokenKind.RBRACKET, "]", -1, -1, -1))),
|
||||||
|
("[ x for x in z if t", ParenthesisMismatchError(Token(TokenKind.RBRACKET, "]", -1, -1, -1))),
|
||||||
|
("zzz [ x for x in z if t ]", LeadingParenthesisNotFound()),
|
||||||
|
("[ x for x in z )", ParenthesisMismatchError(Token(TokenKind.RBRACKET, "]", -1, -1, -1))),
|
||||||
|
("[ x for x in z if t )", ParenthesisMismatchError(Token(TokenKind.RBRACKET, "]", -1, -1, -1))),
|
||||||
|
])
|
||||||
|
def test_i_cannot_parse_when_not_for_me(self, text, reason):
|
||||||
|
sheerka, context, parser = self.init_parser()
|
||||||
|
|
||||||
|
res = parser.parse(context, ParserInput(text))
|
||||||
|
assert not res.status
|
||||||
|
assert sheerka.isinstance(res.body, BuiltinConcepts.NOT_FOR_ME)
|
||||||
|
assert res.body.reason == [reason]
|
||||||
|
|
||||||
|
def test_i_cannot_parse_when_trailing_elements(self):
|
||||||
|
sheerka, context, parser = self.init_parser()
|
||||||
|
|
||||||
|
text = "[ x for x in z if t ] zzz"
|
||||||
|
res = parser.parse(context, ParserInput(text))
|
||||||
|
|
||||||
|
assert not res.status
|
||||||
|
assert sheerka.isinstance(res.body, BuiltinConcepts.ERROR)
|
||||||
|
assert len(res.body.body) == 1
|
||||||
|
error = res.body.body[0]
|
||||||
|
assert isinstance(error, UnexpectedTokenParsingError)
|
||||||
|
|
||||||
|
def test_i_can_parse_a_simple_expression(self):
|
||||||
|
sheerka, context, parser = self.init_parser()
|
||||||
|
|
||||||
|
expression = "[x for x in ['a', 'b'] if x == 'a']"
|
||||||
|
res = parser.parse(context, ParserInput(expression))
|
||||||
|
wrapper = res.body
|
||||||
|
lc_node = res.body.body
|
||||||
|
|
||||||
|
assert res.status
|
||||||
|
assert sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||||
|
|
||||||
|
expected = LC(L_EXPR(None, None, "x", source="x "), [(("x", 1), "['a', 'b']", "x == 'a'")], source=expression)
|
||||||
|
to_compare_to = get_expr_node_from_test_node(expression, expected)
|
||||||
|
assert lc_node == to_compare_to
|
||||||
|
|
||||||
|
def test_i_can_parse_when_no_if(self):
|
||||||
|
sheerka, context, parser = self.init_parser()
|
||||||
|
|
||||||
|
expression = "[x for x in ['a', 'b']]"
|
||||||
|
res = parser.parse(context, ParserInput(expression))
|
||||||
|
wrapper = res.body
|
||||||
|
lc_node = res.body.body
|
||||||
|
|
||||||
|
assert res.status
|
||||||
|
assert sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||||
|
|
||||||
|
expected = LC(L_EXPR(None, None, "x", source="x "), [(("x", 1), "['a', 'b']", None)], source=expression)
|
||||||
|
to_compare_to = get_expr_node_from_test_node(expression, expected)
|
||||||
|
assert lc_node == to_compare_to
|
||||||
|
|
||||||
|
def test_i_can_parse_when_element_is_a_tuple(self):
|
||||||
|
sheerka, context, parser = self.init_parser()
|
||||||
|
|
||||||
|
expression = "[(x + 1, x + 2) for x in [1, 2]]"
|
||||||
|
res = parser.parse(context, ParserInput(expression))
|
||||||
|
wrapper = res.body
|
||||||
|
lc_node = res.body.body
|
||||||
|
|
||||||
|
assert res.status
|
||||||
|
assert sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||||
|
|
||||||
|
elt = L_EXPR("(", ")", "x + 1", "x + 2")
|
||||||
|
expected = LC(elt, [(("x", 2), "[1, 2]", None)], source=expression)
|
||||||
|
to_compare_to = get_expr_node_from_test_node(expression, expected)
|
||||||
|
|
||||||
|
assert lc_node == to_compare_to
|
||||||
|
|
||||||
|
def test_i_can_parse_when_element_is_a_tuple_with_missing_parenthesis(self):
|
||||||
|
sheerka, context, parser = self.init_parser()
|
||||||
|
|
||||||
|
expression = "[x + 1, x + 2 for x in [1, 2]]"
|
||||||
|
res = parser.parse(context, ParserInput(expression))
|
||||||
|
wrapper = res.body
|
||||||
|
lc_node = res.body.body
|
||||||
|
|
||||||
|
assert res.status
|
||||||
|
assert sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||||
|
|
||||||
|
elt = L_EXPR(None, None, "x + 1", "x + 2", source="x + 1, x + 2 ")
|
||||||
|
expected = LC(elt, [(("x", 2), "[1, 2]", None)], source=expression)
|
||||||
|
to_compare_to = get_expr_node_from_test_node(expression, expected)
|
||||||
|
|
||||||
|
assert lc_node == to_compare_to
|
||||||
|
|
||||||
|
def test_i_can_parse_when_element_is_a_context_that_contains_for(self):
|
||||||
|
sheerka, context, parser = self.init_parser()
|
||||||
|
|
||||||
|
expression = "[handle x for me and for you for x in [1, 2]]"
|
||||||
|
res = parser.parse(context, ParserInput(expression))
|
||||||
|
wrapper = res.body
|
||||||
|
lc_node = res.body.body
|
||||||
|
|
||||||
|
assert res.status
|
||||||
|
assert sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||||
|
|
||||||
|
elt = L_EXPR(None, None, "handle x for me and for you", source="handle x for me and for you ")
|
||||||
|
expected = LC(elt, [(("x", 1), "[1, 2]", None)], source=expression)
|
||||||
|
to_compare_to = get_expr_node_from_test_node(expression, expected)
|
||||||
|
|
||||||
|
assert lc_node == to_compare_to
|
||||||
|
|
||||||
|
def test_i_can_parse_when_multiple_generators(self):
|
||||||
|
sheerka, context, parser = self.init_parser()
|
||||||
|
|
||||||
|
expression = "[(x, y) for x in ['a', 'b'] if x == 'a' for y in ['c', 'd'] if y == 'c']"
|
||||||
|
res = parser.parse(context, ParserInput(expression))
|
||||||
|
wrapper = res.body
|
||||||
|
lc_node = res.body.body
|
||||||
|
|
||||||
|
assert res.status
|
||||||
|
assert sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||||
|
|
||||||
|
elt = L_EXPR("(", ")", "x", "y")
|
||||||
|
expected = LC(elt,
|
||||||
|
[(("x", 1), "['a', 'b']", "x == 'a'"),
|
||||||
|
(("y", 1), "['c', 'd']", "y == 'c'")],
|
||||||
|
source=expression)
|
||||||
|
to_compare_to = get_expr_node_from_test_node(expression, expected)
|
||||||
|
|
||||||
|
assert lc_node == to_compare_to
|
||||||
|
|
||||||
|
def test_i_can_parse_when_multiple_generators_when_no_if(self):
|
||||||
|
sheerka, context, parser = self.init_parser()
|
||||||
|
|
||||||
|
expression = "[x, y for x in ['a', 'b'] for y in ['c', 'd'] if y == 'c']"
|
||||||
|
res = parser.parse(context, ParserInput(expression))
|
||||||
|
wrapper = res.body
|
||||||
|
lc_node = res.body.body
|
||||||
|
|
||||||
|
assert res.status
|
||||||
|
assert sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||||
|
|
||||||
|
elt = L_EXPR(None, None, "x", "y", source="x, y ")
|
||||||
|
expected = LC(elt,
|
||||||
|
[(("x", 1), "['a', 'b']", None),
|
||||||
|
(("y", 1), "['c', 'd']", "y == 'c'")],
|
||||||
|
source=expression)
|
||||||
|
to_compare_to = get_expr_node_from_test_node(expression, expected)
|
||||||
|
|
||||||
|
assert lc_node == to_compare_to
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from core.builtin_concepts_ids import BuiltinConcepts
|
||||||
|
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||||
|
from core.tokenizer import Token, TokenKind
|
||||||
|
from parsers.BaseExpressionParser import ParenthesisMismatchError, end_parenthesis_mapping
|
||||||
|
from parsers.BaseParser import ErrorSink
|
||||||
|
from parsers.ListParser import ListParser
|
||||||
|
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||||
|
from tests.parsers.parsers_utils import EXPR, L_EXPR, get_expr_node_from_test_node
|
||||||
|
|
||||||
|
semi_colon = Token(TokenKind.SEMICOLON, ";", -1, -1, -1)
|
||||||
|
or_token = Token(TokenKind.IDENTIFIER, "or", -1, -1, -1)
|
||||||
|
|
||||||
|
|
||||||
|
class TestListParser(TestUsingMemoryBasedSheerka):
|
||||||
|
def init_parser(self, sep=None):
|
||||||
|
sheerka, context = self.init_concepts()
|
||||||
|
parser = ListParser(sep)
|
||||||
|
return sheerka, context, parser
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("expression, sep, expected", [
|
||||||
|
("()", None, L_EXPR("(", ")")),
|
||||||
|
("(x , foo y,z)", None, L_EXPR("(", ")", EXPR("x"), EXPR("foo y"), EXPR("z"), source="(x , foo y,z)")),
|
||||||
|
("x , foo y,z", None, L_EXPR(None, None, EXPR("x"), EXPR("foo y"), EXPR("z"), source="x , foo y,z")),
|
||||||
|
("x", None, L_EXPR(None, None, EXPR("x"))),
|
||||||
|
("[x, foo y, z]", None, L_EXPR("[", "]", EXPR("x"), EXPR("foo y"), EXPR("z"))),
|
||||||
|
("{x, foo y, z}", None, L_EXPR("{", "}", EXPR("x"), EXPR("foo y"), EXPR("z"))),
|
||||||
|
("(x; y; z)", semi_colon, L_EXPR("(", ")", EXPR("x"), EXPR("y"), EXPR("z"), sep=semi_colon, source="(x; y; z)")),
|
||||||
|
("x; y; z", semi_colon, L_EXPR(None, None, EXPR("x"), EXPR("y"), EXPR("z"), sep=semi_colon, source="x; y; z")),
|
||||||
|
("x or y or z", or_token, L_EXPR(None, None, EXPR("x"), EXPR("y"), EXPR("z"), sep=or_token, source="x or y or z")),
|
||||||
|
])
|
||||||
|
def test_i_can_parse_expression(self, expression, sep, expected):
|
||||||
|
sheerka, context, parser = self.init_parser(sep)
|
||||||
|
|
||||||
|
expected = get_expr_node_from_test_node(expression, expected)
|
||||||
|
res = parser.parse(context, ParserInput(expression))
|
||||||
|
wrapper = res.body
|
||||||
|
expressions = res.body.body
|
||||||
|
|
||||||
|
assert res.status
|
||||||
|
assert sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
|
||||||
|
assert expressions == expected
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("expression, starting", [
|
||||||
|
("(", TokenKind.LPAR),
|
||||||
|
("(x, y", TokenKind.LPAR),
|
||||||
|
("{x, y", TokenKind.LBRACE),
|
||||||
|
("[x, y", TokenKind.LBRACKET),
|
||||||
|
])
|
||||||
|
def test_i_cannot_parse_when_missing_trailing_parenthesis(self, expression, starting):
|
||||||
|
sheerka, context, parser = self.init_parser()
|
||||||
|
|
||||||
|
res = parser.parse(context, ParserInput(expression))
|
||||||
|
|
||||||
|
assert not res.status
|
||||||
|
assert sheerka.isinstance(res.body, BuiltinConcepts.ERROR)
|
||||||
|
assert res.body.body == [ParenthesisMismatchError(end_parenthesis_mapping[starting])]
|
||||||
|
|
||||||
|
def test_none_is_return_when_empty_parser_input(self):
|
||||||
|
sheerka, context, parser = self.init_parser()
|
||||||
|
|
||||||
|
parser_input = ParserInput(" ").reset()
|
||||||
|
parser_input.next_token()
|
||||||
|
error_sink = ErrorSink()
|
||||||
|
|
||||||
|
parsed = parser.parse_input(context, parser_input, error_sink)
|
||||||
|
|
||||||
|
assert parsed is None
|
||||||
@@ -100,12 +100,12 @@ class TestLogicalOperatorParser(TestUsingMemoryBasedSheerka):
|
|||||||
assert expressions == expected
|
assert expressions == expected
|
||||||
|
|
||||||
@pytest.mark.parametrize("expression, expected_errors", [
|
@pytest.mark.parametrize("expression, expected_errors", [
|
||||||
("one or", [UnexpectedEofParsingError("When parsing 'or'")]),
|
("one or", [UnexpectedEofParsingError("while parsing 'or'")]),
|
||||||
("one and", [UnexpectedEofParsingError("When parsing 'and'")]),
|
("one and", [UnexpectedEofParsingError("while parsing 'and'")]),
|
||||||
("and one", [LeftPartNotFoundError()]),
|
("and one", [LeftPartNotFoundError("and", 0)]),
|
||||||
("or one", [LeftPartNotFoundError()]),
|
("or one", [LeftPartNotFoundError("or", 0)]),
|
||||||
("or", [LeftPartNotFoundError(), UnexpectedEofParsingError("When parsing 'or'")]),
|
("or", [LeftPartNotFoundError("or", 0), UnexpectedEofParsingError("while parsing 'or'")]),
|
||||||
("and", [LeftPartNotFoundError(), UnexpectedEofParsingError("When parsing 'and'")]),
|
("and", [LeftPartNotFoundError("and", 0), UnexpectedEofParsingError("while parsing 'and'")]),
|
||||||
])
|
])
|
||||||
def test_i_can_detect_error(self, expression, expected_errors):
|
def test_i_can_detect_error(self, expression, expected_errors):
|
||||||
sheerka, context, parser = self.init_parser()
|
sheerka, context, parser = self.init_parser()
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ from core.concept import Concept, DEFINITION_TYPE_DEF
|
|||||||
from core.sheerka.services.SheerkaExecute import ParserInput
|
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||||
from parsers.SequenceNodeParser import SequenceNodeParser
|
from parsers.SequenceNodeParser import SequenceNodeParser
|
||||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||||
from tests.parsers.parsers_utils import compute_expected_array, CN, SCN, get_test_obj, compare_with_test_object, \
|
from tests.parsers.parsers_utils import CN, SCN, UTN, compare_with_test_object, compute_expected_array, get_test_obj
|
||||||
UTN
|
|
||||||
|
|
||||||
|
|
||||||
class TestSequenceNodeParser(TestUsingMemoryBasedSheerka):
|
class TestSequenceNodeParser(TestUsingMemoryBasedSheerka):
|
||||||
@@ -463,3 +462,24 @@ class TestSequenceNodeParser(TestUsingMemoryBasedSheerka):
|
|||||||
assert concept_found.name == "boys"
|
assert concept_found.name == "boys"
|
||||||
assert concept_found.key == "boys"
|
assert concept_found.key == "boys"
|
||||||
assert concept_found.get_prop(BuiltinConcepts.PLURAL) == boy
|
assert concept_found.get_prop(BuiltinConcepts.PLURAL) == boy
|
||||||
|
|
||||||
|
def test_i_can_set_body_for_plurals_that_are_a_set(self):
|
||||||
|
concepts_map = {
|
||||||
|
"boy": Concept("boy"),
|
||||||
|
"girl": Concept("girl"),
|
||||||
|
"human": Concept("human"),
|
||||||
|
}
|
||||||
|
|
||||||
|
sheerka, context, parser = self.init_parser(concepts_map)
|
||||||
|
global_truth_concept = self.get_context(sheerka, global_truth=True)
|
||||||
|
sheerka.set_isa(global_truth_concept, concepts_map["boy"], concepts_map["human"])
|
||||||
|
sheerka.set_isa(global_truth_concept, concepts_map["girl"], concepts_map["human"])
|
||||||
|
|
||||||
|
res = parser.parse(context, ParserInput("humans"))
|
||||||
|
|
||||||
|
assert res.status
|
||||||
|
lexer_nodes = res.body.body
|
||||||
|
assert len(lexer_nodes) == 1
|
||||||
|
concept_found = lexer_nodes[0].concept
|
||||||
|
|
||||||
|
assert concept_found.get_metadata().body == "get_set_elements(c:|1003:)"
|
||||||
|
|||||||
+1365
-1113
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,422 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from core.builtin_concepts_ids import BuiltinConcepts
|
||||||
|
from core.concept import Concept
|
||||||
|
from core.sheerka.services.SheerkaEvaluateConcept import EvaluationHints
|
||||||
|
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||||
|
from evaluators.PythonEvaluator import PythonEvaluator
|
||||||
|
from parsers.BaseParser import ErrorSink
|
||||||
|
from parsers.ExpressionParser import ExpressionParser
|
||||||
|
from parsers.ListComprehensionParser import ListComprehensionParser
|
||||||
|
from parsers.PythonParser import PythonNode
|
||||||
|
from sheerkapython.ExprToPython import PythonExprVisitor
|
||||||
|
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||||
|
|
||||||
|
|
||||||
|
class TestExprToPython(TestUsingMemoryBasedSheerka):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_expr_node(context, expression, parser=None):
|
||||||
|
parser = parser or ExpressionParser()
|
||||||
|
error_sink = ErrorSink()
|
||||||
|
parser_input = ParserInput(expression)
|
||||||
|
parser.reset_parser_input(parser_input, error_sink)
|
||||||
|
parsed = parser.parse_input(context, parser_input, error_sink)
|
||||||
|
|
||||||
|
assert not error_sink.has_error
|
||||||
|
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def eval(context, return_value, namespace=None):
|
||||||
|
evaluator = PythonEvaluator()
|
||||||
|
assert evaluator.matches(context, return_value)
|
||||||
|
|
||||||
|
if namespace:
|
||||||
|
for k, v in namespace.items():
|
||||||
|
context.add_to_short_term_memory(k, v)
|
||||||
|
|
||||||
|
res = evaluator.eval(context, return_value)
|
||||||
|
assert res.status
|
||||||
|
return res.body
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("expression, source, objects", [
|
||||||
|
("foo w", "call_concept(__o_00__, x=w)", {"__o_00__": "foo"}),
|
||||||
|
("foo z + 2", "call_concept(__o_00__, x=z + 2)", {"__o_00__": "foo"}),
|
||||||
|
("foo a and bar b",
|
||||||
|
"call_concept(__o_00__, x=a) and call_concept(__o_01__, y=b)",
|
||||||
|
{"__o_00__": "foo", "__o_01__": "bar"}),
|
||||||
|
("foo a or bar b",
|
||||||
|
"call_concept(__o_00__, x=a) or call_concept(__o_01__, y=b)",
|
||||||
|
{"__o_00__": "foo", "__o_01__": "bar"}),
|
||||||
|
("not foo w", "not call_concept(__o_00__, x=w)", {"__o_00__": "foo"}),
|
||||||
|
("foo a >= bar b",
|
||||||
|
"call_concept(__o_00__, x=a) >= call_concept(__o_01__, y=b)",
|
||||||
|
{"__o_00__": "foo", "__o_01__": "bar"}),
|
||||||
|
("function(foo a, bar b)",
|
||||||
|
"function(call_concept(__o_00__, x=a), call_concept(__o_01__, y=b))",
|
||||||
|
{"__o_00__": "foo", "__o_01__": "bar"})
|
||||||
|
|
||||||
|
])
|
||||||
|
def test_i_can_compile_concept_when_is_question_is_false(self, expression, source, objects):
|
||||||
|
sheerka, context, foo, bar = self.init_test().with_concepts(
|
||||||
|
Concept("foo x", body="x").def_var("x"),
|
||||||
|
Concept("bar y", body="y").def_var("y"),
|
||||||
|
create_new=True
|
||||||
|
).unpack()
|
||||||
|
|
||||||
|
concepts = {
|
||||||
|
"foo": foo,
|
||||||
|
"bar": bar
|
||||||
|
}
|
||||||
|
|
||||||
|
node = self.get_expr_node(context, expression)
|
||||||
|
visitor = PythonExprVisitor(context)
|
||||||
|
|
||||||
|
ret = visitor.compile(node)
|
||||||
|
|
||||||
|
assert len(ret) == 1
|
||||||
|
python_node = ret[0].body.body
|
||||||
|
assert python_node.original_source == expression
|
||||||
|
assert python_node.source == source
|
||||||
|
|
||||||
|
for obj_name, obj_value in objects.items():
|
||||||
|
assert obj_name in python_node.objects
|
||||||
|
|
||||||
|
obj = python_node.objects[obj_name]
|
||||||
|
if isinstance(obj, Concept):
|
||||||
|
assert sheerka.isinstance(obj, concepts[obj_value])
|
||||||
|
assert obj.get_hints().use_copy
|
||||||
|
assert obj.get_hints().is_evaluated
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("expression, source, objects", [
|
||||||
|
("foo w", "evaluate_question(__o_00__, x=w)", {"__o_00__": "foo"}),
|
||||||
|
("foo z + 2", "evaluate_question(__o_00__, x=z + 2)", {"__o_00__": "foo"}),
|
||||||
|
])
|
||||||
|
def test_i_can_compile_concept_when_is_question_is_true(self, expression, source, objects):
|
||||||
|
sheerka, context, foo = self.init_test().with_concepts(
|
||||||
|
Concept("foo x", body="x", pre="is_question()").def_var("x"),
|
||||||
|
create_new=True
|
||||||
|
).unpack()
|
||||||
|
|
||||||
|
concepts = {
|
||||||
|
"foo": foo
|
||||||
|
}
|
||||||
|
|
||||||
|
node = self.get_expr_node(context, expression)
|
||||||
|
visitor = PythonExprVisitor(context)
|
||||||
|
ret = visitor.compile(node, EvaluationHints(eval_body=True, eval_question=True))
|
||||||
|
|
||||||
|
assert len(ret) == 1
|
||||||
|
python_node = ret[0].body.body
|
||||||
|
assert python_node.original_source == expression
|
||||||
|
assert python_node.source == source
|
||||||
|
|
||||||
|
for obj_name, obj_value in objects.items():
|
||||||
|
assert obj_name in python_node.objects
|
||||||
|
|
||||||
|
obj = python_node.objects[obj_name]
|
||||||
|
if isinstance(obj, Concept):
|
||||||
|
assert sheerka.isinstance(obj, concepts[obj_value])
|
||||||
|
assert obj.get_hints().use_copy
|
||||||
|
assert obj.get_hints().is_evaluated
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
def test_i_can_compile_simple_list_comprehension(self):
|
||||||
|
sheerka, context = self.init_test().unpack()
|
||||||
|
|
||||||
|
expression = "[ x for x in ['a', 'b'] if x == 'a' ]"
|
||||||
|
node = self.get_expr_node(context, expression, parser=ListComprehensionParser())
|
||||||
|
visitor = PythonExprVisitor(context)
|
||||||
|
|
||||||
|
ret = visitor.compile(node)
|
||||||
|
|
||||||
|
assert len(ret) == 1
|
||||||
|
assert sheerka.isinstance(ret[0], BuiltinConcepts.RETURN_VALUE)
|
||||||
|
assert sheerka.isinstance(ret[0].body, BuiltinConcepts.PARSER_RESULT)
|
||||||
|
assert isinstance(ret[0].body.body, PythonNode)
|
||||||
|
|
||||||
|
python_node = ret[0].body.body
|
||||||
|
assert python_node.original_source == expression
|
||||||
|
assert python_node.source == expression
|
||||||
|
|
||||||
|
assert self.eval(context, ret[0]) == ["a"]
|
||||||
|
|
||||||
|
def test_i_can_compile_simple_list_comprehension_when_no_if(self):
|
||||||
|
sheerka, context = self.init_test().unpack()
|
||||||
|
|
||||||
|
expression = "[ x for x in ['a', 'b'] ]"
|
||||||
|
node = self.get_expr_node(context, expression, parser=ListComprehensionParser())
|
||||||
|
visitor = PythonExprVisitor(context)
|
||||||
|
|
||||||
|
ret = visitor.compile(node)
|
||||||
|
|
||||||
|
assert len(ret) == 1
|
||||||
|
assert sheerka.isinstance(ret[0], BuiltinConcepts.RETURN_VALUE)
|
||||||
|
assert sheerka.isinstance(ret[0].body, BuiltinConcepts.PARSER_RESULT)
|
||||||
|
assert isinstance(ret[0].body.body, PythonNode)
|
||||||
|
|
||||||
|
python_node = ret[0].body.body
|
||||||
|
assert python_node.original_source == expression
|
||||||
|
assert python_node.source == expression
|
||||||
|
|
||||||
|
assert self.eval(context, ret[0]) == ['a', 'b']
|
||||||
|
|
||||||
|
def test_i_can_compile_list_comprehension_when_element_is_a_concept(self):
|
||||||
|
sheerka, context, foo = self.init_test().with_concepts(
|
||||||
|
Concept("foo x", body="x").def_var("x")
|
||||||
|
).unpack()
|
||||||
|
|
||||||
|
expression = "[ foo w for w in ['a', 'b'] ]"
|
||||||
|
node = self.get_expr_node(context, expression, parser=ListComprehensionParser())
|
||||||
|
visitor = PythonExprVisitor(context)
|
||||||
|
|
||||||
|
ret = visitor.compile(node)
|
||||||
|
|
||||||
|
assert len(ret) == 1
|
||||||
|
assert sheerka.isinstance(ret[0], BuiltinConcepts.RETURN_VALUE)
|
||||||
|
assert sheerka.isinstance(ret[0].body, BuiltinConcepts.PARSER_RESULT)
|
||||||
|
assert isinstance(ret[0].body.body, PythonNode)
|
||||||
|
|
||||||
|
python_node = ret[0].body.body
|
||||||
|
assert python_node.original_source == expression
|
||||||
|
assert python_node.source == "[ call_concept(__o_00__, x=w) for w in ['a', 'b'] ]"
|
||||||
|
assert "__o_00__" in python_node.objects
|
||||||
|
|
||||||
|
concept = python_node.objects["__o_00__"]
|
||||||
|
assert sheerka.isinstance(concept, foo)
|
||||||
|
assert concept.get_hints().use_copy
|
||||||
|
assert concept.get_hints().is_evaluated
|
||||||
|
|
||||||
|
assert self.eval(context, ret[0]) == ["a", "b"]
|
||||||
|
|
||||||
|
def test_i_can_compile_list_comprehension_when_concept_with_complex_parameter(self):
|
||||||
|
sheerka, context, foo = self.init_test().with_concepts(
|
||||||
|
Concept("foo x", body="x").def_var("x"),
|
||||||
|
create_new=True
|
||||||
|
).unpack()
|
||||||
|
|
||||||
|
expression = "[ foo w + 1 for w in [1, 2] ]"
|
||||||
|
node = self.get_expr_node(context, expression, parser=ListComprehensionParser())
|
||||||
|
visitor = PythonExprVisitor(context)
|
||||||
|
|
||||||
|
ret = visitor.compile(node)
|
||||||
|
|
||||||
|
assert len(ret) == 1
|
||||||
|
assert sheerka.isinstance(ret[0], BuiltinConcepts.RETURN_VALUE)
|
||||||
|
assert sheerka.isinstance(ret[0].body, BuiltinConcepts.PARSER_RESULT)
|
||||||
|
assert isinstance(ret[0].body.body, PythonNode)
|
||||||
|
|
||||||
|
python_node = ret[0].body.body
|
||||||
|
assert python_node.original_source == expression
|
||||||
|
assert python_node.source == "[ call_concept(__o_00__, x=w + 1) for w in [1, 2] ]"
|
||||||
|
assert "__o_00__" in python_node.objects
|
||||||
|
|
||||||
|
assert self.eval(context, ret[0]) == [2, 3]
|
||||||
|
|
||||||
|
def test_i_can_compile_list_comprehension_when_iter_is_a_concept(self):
|
||||||
|
sheerka, context, red, blue, color, foo = self.init_test().with_concepts(
|
||||||
|
"red",
|
||||||
|
"blue",
|
||||||
|
"color",
|
||||||
|
Concept("foo x", body="x").def_var("x")
|
||||||
|
).unpack()
|
||||||
|
|
||||||
|
global_truth_context = self.get_context(sheerka, global_truth=True)
|
||||||
|
sheerka.set_isa(global_truth_context, red, color)
|
||||||
|
sheerka.set_isa(global_truth_context, blue, color)
|
||||||
|
|
||||||
|
expression = "[ foo x for x in colors ]"
|
||||||
|
node = self.get_expr_node(context, expression, parser=ListComprehensionParser())
|
||||||
|
visitor = PythonExprVisitor(context)
|
||||||
|
|
||||||
|
ret = visitor.compile(node)
|
||||||
|
|
||||||
|
assert len(ret) == 1
|
||||||
|
assert sheerka.isinstance(ret[0], BuiltinConcepts.RETURN_VALUE)
|
||||||
|
assert sheerka.isinstance(ret[0].body, BuiltinConcepts.PARSER_RESULT)
|
||||||
|
assert isinstance(ret[0].body.body, PythonNode)
|
||||||
|
|
||||||
|
python_node = ret[0].body.body
|
||||||
|
assert python_node.original_source == expression
|
||||||
|
assert python_node.source == "[ call_concept(__o_00__, x=x) for x in call_concept(__o_01__) ]"
|
||||||
|
assert "__o_00__" in python_node.objects
|
||||||
|
assert "__o_01__" in python_node.objects
|
||||||
|
|
||||||
|
concept0 = python_node.objects["__o_00__"]
|
||||||
|
assert sheerka.isinstance(concept0, foo)
|
||||||
|
assert concept0.get_hints().use_copy
|
||||||
|
assert concept0.get_hints().is_evaluated
|
||||||
|
|
||||||
|
concept1 = python_node.objects["__o_01__"]
|
||||||
|
assert sheerka.isinstance(concept1, "colors")
|
||||||
|
assert concept1.get_hints().use_copy
|
||||||
|
assert concept1.get_hints().is_evaluated
|
||||||
|
|
||||||
|
assert set(self.eval(context, ret[0])) == {red, blue}
|
||||||
|
|
||||||
|
def test_i_can_compile_list_comprehension_when_if_expression_is_a_concept(self):
|
||||||
|
sheerka, context, red, blue, color, foo, startswith = self.init_test().with_concepts(
|
||||||
|
"red",
|
||||||
|
"blue",
|
||||||
|
"color",
|
||||||
|
Concept("foo x", body="x").def_var("x"),
|
||||||
|
Concept("x starts with y", body="x.name.startswith(y)", pre="is_question()").def_var("x").def_var("y")
|
||||||
|
).unpack()
|
||||||
|
global_truth_context = self.get_context(sheerka, global_truth=True)
|
||||||
|
sheerka.set_isa(global_truth_context, red, color)
|
||||||
|
sheerka.set_isa(global_truth_context, blue, color)
|
||||||
|
|
||||||
|
expression = "[ foo x for x in colors if x starts with 'b' ]"
|
||||||
|
|
||||||
|
node = self.get_expr_node(context, expression, parser=ListComprehensionParser())
|
||||||
|
visitor = PythonExprVisitor(context)
|
||||||
|
|
||||||
|
ret = visitor.compile(node)
|
||||||
|
|
||||||
|
assert len(ret) == 1
|
||||||
|
assert sheerka.isinstance(ret[0], BuiltinConcepts.RETURN_VALUE)
|
||||||
|
assert sheerka.isinstance(ret[0].body, BuiltinConcepts.PARSER_RESULT)
|
||||||
|
assert isinstance(ret[0].body.body, PythonNode)
|
||||||
|
|
||||||
|
python_node = ret[0].body.body
|
||||||
|
assert python_node.original_source == expression
|
||||||
|
assert python_node.source == "[ call_concept(__o_00__, x=x) for x in call_concept(__o_01__) if evaluate_question(__o_02__, x=x, y='b') ]"
|
||||||
|
assert "__o_00__" in python_node.objects
|
||||||
|
assert "__o_01__" in python_node.objects
|
||||||
|
assert "__o_02__" in python_node.objects
|
||||||
|
assert visitor.obj_counter == 3
|
||||||
|
|
||||||
|
assert set(self.eval(context, ret[0])) == {blue}
|
||||||
|
|
||||||
|
def test_i_can_compile_list_comprehension_when_multiple_concepts(self):
|
||||||
|
sheerka, context, foo1, foo2, bar1, bar2, colors1, colors2, = self.init_test().with_concepts(
|
||||||
|
Concept("foo x").def_var("x"),
|
||||||
|
Concept("foo y", ).def_var("y"),
|
||||||
|
Concept("bar x", pre="is_question()").def_var("x"),
|
||||||
|
Concept("bar y", pre="is_question()").def_var("y"),
|
||||||
|
Concept("colors", body="[1]"),
|
||||||
|
Concept("colors", body="[2]"),
|
||||||
|
).unpack()
|
||||||
|
|
||||||
|
expression = "[ foo a for a in colors if bar a ]"
|
||||||
|
|
||||||
|
node = self.get_expr_node(context, expression, parser=ListComprehensionParser())
|
||||||
|
visitor = PythonExprVisitor(context)
|
||||||
|
|
||||||
|
ret = visitor.compile(node)
|
||||||
|
|
||||||
|
assert len(ret) == 8
|
||||||
|
python_node = ret[0].body.body
|
||||||
|
object_to_compare = {k: v.name for k, v in python_node.objects.items()}
|
||||||
|
assert python_node.source == '[ call_concept(__o_00__, x=a) for a in call_concept(__o_02__) if evaluate_question(__o_04__, x=a) ]'
|
||||||
|
assert object_to_compare == {"__o_00__": "foo x", "__o_02__": "colors", "__o_04__": "bar x"}
|
||||||
|
|
||||||
|
# ...
|
||||||
|
|
||||||
|
python_node = ret[7].body.body
|
||||||
|
object_to_compare = {k: v.name for k, v in python_node.objects.items()}
|
||||||
|
assert python_node.source == '[ call_concept(__o_01__, y=a) for a in call_concept(__o_03__) if evaluate_question(__o_05__, y=a) ]'
|
||||||
|
assert object_to_compare == {"__o_01__": "foo y", "__o_03__": "colors", "__o_05__": "bar y"}
|
||||||
|
|
||||||
|
def test_i_can_compile_list_comprehension_when_missing_concept_parameter(self):
|
||||||
|
sheerka, context, foo = self.init_test().with_concepts(
|
||||||
|
Concept("foo x y", body="x").def_var("x").def_var("y")
|
||||||
|
).unpack()
|
||||||
|
|
||||||
|
expression = "[ foo x k for x in ['a', 'b'] ]"
|
||||||
|
node = self.get_expr_node(context, expression, parser=ListComprehensionParser())
|
||||||
|
visitor = PythonExprVisitor(context)
|
||||||
|
|
||||||
|
ret = visitor.compile(node)
|
||||||
|
|
||||||
|
assert len(ret) == 1
|
||||||
|
python_node = ret[0].body.body
|
||||||
|
object_to_compare = {k: v.name for k, v in python_node.objects.items()}
|
||||||
|
assert python_node.source == "[ call_concept(__o_00__, x=x, y=k) for x in ['a', 'b'] ]"
|
||||||
|
assert object_to_compare == {"__o_00__": "foo x y"}
|
||||||
|
|
||||||
|
def test_i_can_compile_simple_list_comprehension_when_multiple_for(self):
|
||||||
|
sheerka, context = self.init_test().unpack()
|
||||||
|
|
||||||
|
expression = "[ (x, y) for x in ['a', 'b'] if x == 'a' for y in ['c', 'd'] if y == 'c' ]"
|
||||||
|
node = self.get_expr_node(context, expression, parser=ListComprehensionParser())
|
||||||
|
visitor = PythonExprVisitor(context)
|
||||||
|
|
||||||
|
ret = visitor.compile(node)
|
||||||
|
|
||||||
|
assert len(ret) == 1
|
||||||
|
assert sheerka.isinstance(ret[0], BuiltinConcepts.RETURN_VALUE)
|
||||||
|
assert sheerka.isinstance(ret[0].body, BuiltinConcepts.PARSER_RESULT)
|
||||||
|
assert isinstance(ret[0].body.body, PythonNode)
|
||||||
|
|
||||||
|
python_node = ret[0].body.body
|
||||||
|
assert python_node.original_source == expression
|
||||||
|
assert python_node.source == expression
|
||||||
|
|
||||||
|
assert self.eval(context, ret[0]) == [("a", "c")]
|
||||||
|
|
||||||
|
def test_i_can_compile_and_when_multiple_results(self):
|
||||||
|
sheerka, context, foo, foo2, bar, bar2 = self.init_test().with_concepts(
|
||||||
|
Concept("foo x", body="x").def_var("x"),
|
||||||
|
Concept("foo y", body="y").def_var("y"),
|
||||||
|
Concept("bar x", body="x").def_var("x"),
|
||||||
|
Concept("bar y", body="y").def_var("y"),
|
||||||
|
create_new=True
|
||||||
|
).unpack()
|
||||||
|
|
||||||
|
node = self.get_expr_node(context, "foo a and bar b")
|
||||||
|
visitor = PythonExprVisitor(context)
|
||||||
|
ret = visitor.compile(node)
|
||||||
|
|
||||||
|
assert len(ret) == 4
|
||||||
|
python_node = ret[0].body.body
|
||||||
|
object_to_compare = {k: v.name for k, v in python_node.objects.items()}
|
||||||
|
assert python_node.source == 'call_concept(__o_00__, x=a) and call_concept(__o_02__, x=b)'
|
||||||
|
assert object_to_compare == {"__o_00__": "foo x", "__o_02__": "bar x"}
|
||||||
|
|
||||||
|
python_node = ret[1].body.body
|
||||||
|
object_to_compare = {k: v.name for k, v in python_node.objects.items()}
|
||||||
|
assert python_node.source == 'call_concept(__o_00__, x=a) and call_concept(__o_03__, y=b)'
|
||||||
|
assert object_to_compare == {"__o_00__": "foo x", "__o_03__": "bar y"}
|
||||||
|
|
||||||
|
python_node = ret[2].body.body
|
||||||
|
object_to_compare = {k: v.name for k, v in python_node.objects.items()}
|
||||||
|
assert python_node.source == 'call_concept(__o_01__, y=a) and call_concept(__o_02__, x=b)'
|
||||||
|
assert object_to_compare == {"__o_01__": "foo y", "__o_02__": "bar x"}
|
||||||
|
|
||||||
|
python_node = ret[3].body.body
|
||||||
|
object_to_compare = {k: v.name for k, v in python_node.objects.items()}
|
||||||
|
assert python_node.source == 'call_concept(__o_01__, y=a) and call_concept(__o_03__, y=b)'
|
||||||
|
assert object_to_compare == {"__o_01__": "foo y", "__o_03__": "bar y"}
|
||||||
|
|
||||||
|
def test_i_can_compile_when_element_is_missing_its_parenthesis(self):
|
||||||
|
sheerka, context, foo = self.init_test().with_concepts(
|
||||||
|
Concept("foo x", body="x").def_var("x")
|
||||||
|
).unpack()
|
||||||
|
|
||||||
|
expression = "[ w, foo w for w in ['a', 'b'] ]"
|
||||||
|
node = self.get_expr_node(context, expression, parser=ListComprehensionParser())
|
||||||
|
visitor = PythonExprVisitor(context)
|
||||||
|
|
||||||
|
ret = visitor.compile(node)
|
||||||
|
|
||||||
|
assert len(ret) == 1
|
||||||
|
assert sheerka.isinstance(ret[0], BuiltinConcepts.RETURN_VALUE)
|
||||||
|
assert sheerka.isinstance(ret[0].body, BuiltinConcepts.PARSER_RESULT)
|
||||||
|
assert isinstance(ret[0].body.body, PythonNode)
|
||||||
|
|
||||||
|
python_node = ret[0].body.body
|
||||||
|
assert python_node.original_source == expression
|
||||||
|
assert python_node.source == "[ (w, call_concept(__o_00__, x=w)) for w in ['a', 'b'] ]"
|
||||||
|
assert "__o_00__" in python_node.objects
|
||||||
|
|
||||||
|
concept = python_node.objects["__o_00__"]
|
||||||
|
assert sheerka.isinstance(concept, foo)
|
||||||
|
assert concept.get_hints().use_copy
|
||||||
|
assert concept.get_hints().is_evaluated
|
||||||
|
|
||||||
|
assert self.eval(context, ret[0]) == [("a", "a"), ("b", "b")]
|
||||||
Reference in New Issue
Block a user