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 []