diff --git a/ast/ast_generator.py b/ast/ast_generator.py new file mode 100644 index 0000000000000000000000000000000000000000..97d790a615fed8ece98adfb5bffa6a850b032b85 --- /dev/null +++ b/ast/ast_generator.py @@ -0,0 +1,48 @@ +import ast +import sys +import pprint +from graphviz import Digraph + + +def main(): + if len(sys.argv) != 2: + exit() + file_name = sys.argv[1] + with open(file_name, 'r') as file: + data = file.read() + tree = ast.parse(data) + + pprint.pprint(ast.dump(tree)) + + # Create a Graphviz Digraph object + dot = Digraph() + + def add_node(node2, parent=None): + node_name = str(node2.__class__.__name__) + dot.node(str(id(node2)), node_name) + if parent: + dot.edge(str(id(parent)), str(id(node2))) + for child in ast.iter_child_nodes(node2): + add_node(child, node2) + + add_node(tree) + + # Render the Digraph as a PNG file + dot.format = 'png' + dot.render('my_ast', view=True) + + +if __name__ == "__main__": + main() + + +import ast +from graphviz import Digraph + +... + + +# Define a function to recursively add nodes to the Digraph + + +# Add nodes to the Digraph diff --git a/ast/astvisualizer.py b/ast/astvisualizer.py new file mode 100644 index 0000000000000000000000000000000000000000..9bac88b6629ca3ea29a458f83808236eea1d6f56 --- /dev/null +++ b/ast/astvisualizer.py @@ -0,0 +1,143 @@ +import ast +from graphviz import Digraph +import subprocess +import numbers +import re +from uuid import uuid4 as uuid +import optparse +import sys + +def main(args): + parser = optparse.OptionParser(usage="astvisualizer.py [options] [string]") + parser.add_option("-f", "--file", action="store", + help="Read a code snippet from the specified file") + parser.add_option("-l", "--label", action="store", + help="The label for the visualization") + + options, args = parser.parse_args(args) + if options.file: + with open(options.file) as instream: + code = instream.read() + label = options.file + elif len(args) == 2: + code = args[1] + label = "<code read from command line parameter>" + else: + print("Expecting Python code on stdin...") + code = sys.stdin.read() + label = "<code read from stdin>" + if options.label: + label = options.label + + code_ast = ast.parse(code) + transformed_ast = transform_ast(code_ast) + + renderer = GraphRenderer() + renderer.render(transformed_ast, label=label) + + +def transform_ast(code_ast): + if isinstance(code_ast, ast.AST): + node = {to_camelcase(k): transform_ast(getattr(code_ast, k)) for k in code_ast._fields} + node['node_type'] = to_camelcase(code_ast.__class__.__name__) + return node + elif isinstance(code_ast, list): + return [transform_ast(el) for el in code_ast] + else: + return code_ast + + +def to_camelcase(string): + return re.sub('([a-z0-9])([A-Z])', r'\1_\2', string).lower() + + +class GraphRenderer: + """ + this class is capable of rendering data structures consisting of + dicts and lists as a graph using graphviz + """ + + graphattrs = { + 'labelloc': 't', + 'fontcolor': 'white', + 'bgcolor': '#333333', + 'margin': '0', + } + + nodeattrs = { + 'color': 'white', + 'fontcolor': 'white', + 'style': 'filled', + 'fillcolor': '#006699', + } + + edgeattrs = { + 'color': 'white', + 'fontcolor': 'white', + } + + _graph = None + _rendered_nodes = None + + + @staticmethod + def _escape_dot_label(str): + return str.replace("\\", "\\\\").replace("|", "\\|").replace("<", "\\<").replace(">", "\\>") + + + def _render_node(self, node): + if isinstance(node, (str, numbers.Number)) or node is None: + node_id = uuid() + else: + node_id = id(node) + node_id = str(node_id) + + if node_id not in self._rendered_nodes: + self._rendered_nodes.add(node_id) + if isinstance(node, dict): + self._render_dict(node, node_id) + elif isinstance(node, list): + self._render_list(node, node_id) + else: + self._graph.node(node_id, label=self._escape_dot_label(str(node))) + + return node_id + + + def _render_dict(self, node, node_id): + self._graph.node(node_id, label=node.get("node_type", "[dict]")) + for key, value in node.items(): + if key == "node_type": + continue + child_node_id = self._render_node(value) + self._graph.edge(node_id, child_node_id, label=self._escape_dot_label(key)) + + + def _render_list(self, node, node_id): + self._graph.node(node_id, label="[list]") + for idx, value in enumerate(node): + child_node_id = self._render_node(value) + self._graph.edge(node_id, child_node_id, label=self._escape_dot_label(str(idx))) + + + def render(self, data, *, label=None): + # create the graph + graphattrs = self.graphattrs.copy() + if label is not None: + graphattrs['label'] = self._escape_dot_label(label) + graph = Digraph(graph_attr = graphattrs, node_attr = self.nodeattrs, edge_attr = self.edgeattrs) + + # recursively draw all the nodes and edges + self._graph = graph + self._rendered_nodes = set() + self._render_node(data) + self._graph = None + self._rendered_nodes = None + + # display the graph + graph.format = "png" + graph.render('my_ast', view=False) + # subprocess.Popen(['xdg-open', "my_ast.pdf"]) + +if __name__ == '__main__': + main(sys.argv) \ No newline at end of file diff --git a/ast/visualizer.py b/ast/visualizer.py new file mode 100644 index 0000000000000000000000000000000000000000..22e7a2b301ae0afc2da1810baf4afc474ce872dc --- /dev/null +++ b/ast/visualizer.py @@ -0,0 +1,23 @@ +import ast +from graphviz import Digraph + +def visualize(tree): + + # Create a Graphviz Digraph object + dot = Digraph() + # Add nodes to the Digraph + add_node(dot, tree) + + # Render the Digraph as a PNG file + dot.format = 'png' + dot.render('my_ast', view=True) + +# Define a function to recursively add nodes to the Digraph +def add_node(dot: Digraph, node, parent=None): + node_name = str(node.__class__.__name__) + dot.node(str(id(node)), node_name) + if parent: + dot.edge(str(id(parent)), str(id(node))) + for child in ast.iter_child_nodes(node): + add_node(child, node) +