from core.builtin_concepts import BuiltinConcepts, ListConcept from core.concept import Concept import ast import core.utils import logging log = logging.getLogger(__name__) class NodeParent: """ Class that represent the ancestor of a Node For example, the 'For' nodes has three fields (target, iter and body) So, for a node under For.iter node -> For field -> iter """ def __init__(self, node, field): self.node = node self.field = field def __repr__(self): if self.node is None: return None if self.field is None: return self.node.get_node_type() return self.node.get_node_type() + "." + self.field def __eq__(self, other): # I can compare with type for simplification if isinstance(other, tuple): return self.node.get_node_type() == other[0] and self.field == other[1] # normal equals implementation if not isinstance(other, NodeParent): return False return self.node.get_node_type() == other.node.get_node_type() and self.field == other.field def __hash__(self): return hash((self.node.get_node_type(), self.field)) class NodeConcept(Concept): def __init__(self, key, node_type, parent: NodeParent): super().__init__(key, True, False, key) self.parent = parent self.node_type = node_type def get_node_type(self): return self.node_type class GenericNodeConcept(NodeConcept): def __init__(self, node_type, parent): super().__init__(BuiltinConcepts.GENERIC_NODE, node_type, parent) def __repr__(self): return "Generic:" + self.node_type def get_node_type(self): return self.node_type def get_value(self): if self.node_type == "Name": return self.get_prop("id") if self.node_type == "arg": return self.get_prop("arg") return self.body class IdentifierNodeConcept(NodeConcept): def __init__(self, parent, name): super().__init__(BuiltinConcepts.IDENTIFIER_NODE, "Name", parent) self.body = name class CallNodeConcept(NodeConcept): def __init__(self, parent=None): super().__init__(BuiltinConcepts.IDENTIFIER_NODE, "Call", parent) def get_args_names(self, sheerka): return sheerka.get_values(self.get_prop("args")) def python_to_concept(python_node): """ Transform Python AST node into concept nodes for better usage :param python_node: :return: """ def _transform(node, parent): node_type = node.__class__.__name__ concept = GenericNodeConcept(node_type, parent).init_key() for field in node._fields: if not hasattr(node, field): continue value = getattr(node, field) concept.def_prop(field) if isinstance(value, list): lst = ListConcept().init_key() for i in value: lst.append(_transform(i, NodeParent(concept, field))) concept.set_prop(field, lst) elif isinstance(value, ast.AST): concept.set_prop(field, _transform(value, NodeParent(concept, field))) else: concept.set_prop(field, value) concept.metadata.is_evaluated = True return concept return _transform(python_node, None) def concept_to_python(concept_node): """ Transform back concept_node to Python AST node :param concept_node: :return: """ def _transform(node): node_type = node.get_node_type() ast_object = core.utils.new_object("_ast." + node_type) for field in node.props: if field not in ast_object._fields: continue value = node.get_prop(field) if isinstance(value, list) or isinstance(value, Concept) and value.key == str(BuiltinConcepts.LIST): lst = [] for i in value.body: lst.append(_transform(i)) setattr(ast_object, field, lst) elif isinstance(value, NodeConcept): setattr(ast_object, field, _transform(value)) else: setattr(ast_object, field, value) return ast_object res = _transform(concept_node) return res