Added first version of DebugManager. Implemented draft of the rule engine

This commit is contained in:
2020-11-20 13:41:45 +01:00
parent cd066881b4
commit 315f8ea09b
156 changed files with 8388 additions and 2852 deletions
+175
View File
@@ -0,0 +1,175 @@
import core.utils
from core.ast_helpers import UnreferencedVariablesVisitor
from core.builtin_concepts import ParserResultConcept, ReturnValueConcept, BuiltinConcepts
from core.concept import Concept, DEFINITION_TYPE_BNF, DEFINITION_TYPE_DEF
from core.sheerka.services.SheerkaExecute import ParserInput
from core.tokenizer import TokenKind, Tokenizer
from evaluators.BaseEvaluator import OneReturnValueEvaluator
from parsers.BaseParser import NotInitializedNode
from parsers.BnfNodeParser import ParsingExpression, ParsingExpressionVisitor
from parsers.DefConceptParser import DefConceptNode, NameNode
from parsers.PythonParser import PythonNode
class ConceptOrRuleNameVisitor(ParsingExpressionVisitor):
"""
Gets the concepts referenced by BNF
If a rule_name is given, it will also be considered as a potential property
"""
def __init__(self):
super().__init__()
self.names = set()
def visit_ConceptExpression(self, node):
if node.rule_name:
self.names.add(node.rule_name)
elif isinstance(node.concept, Concept):
self.names.add(node.concept.name)
else:
self.names.add(node.concept)
def visit_all(self, node):
if node.rule_name:
self.names.add(node.rule_name)
class DefConceptEvaluator(OneReturnValueEvaluator):
"""
Used to add a new concept
"""
NAME = "DefConcept"
def __init__(self):
super().__init__(self.NAME, [BuiltinConcepts.EVALUATION], 50)
def matches(self, context, return_value):
debugger = context.get_debugger(self.NAME, "matches")
debugger.debug_entering(return_value=return_value)
return return_value.status and \
isinstance(return_value.value, ParserResultConcept) and \
isinstance(return_value.value.value, DefConceptNode)
def eval(self, context, return_value):
context.log("Adding a new concept", self.name)
def_concept_node = return_value.value.value
sheerka = context.sheerka
debugger = context.get_debugger(self.NAME, "eval")
debugger.debug_entering(def_concept=def_concept_node)
# validate the node
variables_found = set()
concept = Concept(str(def_concept_node.name))
concept.get_metadata().definition_type = def_concept_node.definition_type
name_to_use = self.get_name_to_use(def_concept_node)
for prop in ("definition", "where", "pre", "post", "body", "ret"):
part_ret_val = getattr(def_concept_node, prop)
# put back the sources
if isinstance(part_ret_val, NotInitializedNode):
continue
elif isinstance(part_ret_val, NameNode):
source = str(part_ret_val)
elif isinstance(part_ret_val, ReturnValueConcept) and part_ret_val.status:
source = part_ret_val.value.source.as_text() if isinstance(part_ret_val.value.source,
ParserInput) else part_ret_val.value.source
else:
raise Exception("Unexpected")
setattr(concept.get_metadata(), prop, source)
# Do not try to resolve variables from itself
if prop == "definition" and concept.get_metadata().definition_type == DEFINITION_TYPE_DEF:
continue
# try to find what can be a property
for p in self.get_variables(context, part_ret_val, name_to_use):
variables_found.add(p)
# add variables by order of appearance when possible
for name_part in name_to_use:
if name_part in variables_found:
concept.def_var(name_part, None)
# add the remaining properties
# They mainly come from BNF definition
for p in variables_found:
if p not in concept.values():
concept.def_var(p, None)
# initialize the key
key_source = def_concept_node.definition.tokens if \
def_concept_node.definition_type == DEFINITION_TYPE_DEF else \
def_concept_node.name.tokens
concept.init_key(key_source)
# update the bnf definition if needed
if not isinstance(def_concept_node.definition, NotInitializedNode) and \
def_concept_node.definition_type == DEFINITION_TYPE_BNF:
concept.set_bnf(def_concept_node.definition.value.value)
ret = sheerka.create_new_concept(context, concept)
if not ret.status:
error_cause = sheerka.objvalue(ret.body)
context.log(f"Failed to add concept '{concept.name}'. Reason: {error_cause}", self.name)
return sheerka.ret(self.name, ret.status, ret.value, parents=[return_value])
@staticmethod
def get_name_to_use(node):
source = node.definition if node.definition_type == DEFINITION_TYPE_DEF else node.name
return [part.str_value for part in core.utils.strip_tokens(source.tokens, True)]
@staticmethod
def get_variables(context, ret_value, concept_name):
"""
Try to find out the variables
This function can only be a draft, as there may be tons of different situations
I guess that it can only be complete when will we have access to Sheerka memory
"""
#
# Case of NameNode
#
if isinstance(ret_value, NameNode):
names = [str(t.value) for t in ret_value.tokens if t.type in (
TokenKind.IDENTIFIER, TokenKind.STRING, TokenKind.KEYWORD)]
variables = filter(lambda x: x in concept_name, names)
return set(variables)
#
# case of BNF
#
if isinstance(ret_value.value, ParserResultConcept) and isinstance(ret_value.value.value, ParsingExpression):
visitor = ConceptOrRuleNameVisitor()
visitor.visit(ret_value.value.value)
return set(visitor.names)
#
# Case of python code
#
if isinstance(ret_value.value, ParserResultConcept) and isinstance(ret_value.value.value, PythonNode):
if len(concept_name) > 1:
python_node = ret_value.value.value
visitor = UnreferencedVariablesVisitor(context)
names = visitor.get_names(python_node.ast_)
variables = filter(lambda x: x in concept_name, names)
return set(variables)
#
# Concept
#
if isinstance(ret_value.value, ParserResultConcept) and len(concept_name) > 1:
variables = set()
source = ret_value.value.source.as_text() if isinstance(ret_value.value.source,
ParserInput) else ret_value.value.source
tokens = ret_value.value.tokens or list(Tokenizer(source, yield_eof=False))
tokens = [t.str_value for t in tokens]
for identifier in [i for i in concept_name if str(i).isalnum()]:
if identifier in tokens:
variables.add(identifier)
return variables
return []