Files
Sheerka-Old/src/evaluators/AddConceptEvaluator.py
T
2020-08-27 18:54:28 +02:00

154 lines
5.8 KiB
Python

import core.utils
from core.builtin_concepts import ParserResultConcept, ReturnValueConcept, BuiltinConcepts
from core.concept import Concept, DEFINITION_TYPE_BNF, DEFINITION_TYPE_DEF
from core.tokenizer import TokenKind, Tokenizer
from evaluators.BaseEvaluator import OneReturnValueEvaluator
from parsers.BaseParser import NotInitializedNode
from parsers.BnfNodeParser import ParsingExpression, ParsingExpressionVisitor
from parsers.DefaultParser import DefConceptNode, NameNode
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 AddConceptEvaluator(OneReturnValueEvaluator):
"""
Used to add a new concept
"""
NAME = "AddNewConcept"
def __init__(self):
super().__init__(self.NAME, [BuiltinConcepts.EVALUATION], 50)
def matches(self, context, 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
# validate the node
variables_found = set()
concept = Concept(def_concept_node.name)
concept.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
else:
raise Exception("Unexpected")
setattr(concept.metadata, prop, source)
# Do not try to resolve variables from itself
if prop == "definition" and concept.metadata.definition_type == DEFINITION_TYPE_DEF:
continue
# try to find what can be a property
for p in self.get_variables(sheerka, 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.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(sheerka, 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)
#
# other (python code and concept)
#
if isinstance(ret_value.value, ParserResultConcept) and len(concept_name) > 1:
variables = set()
tokens = ret_value.value.tokens or list(Tokenizer(ret_value.value.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 []