| # Copyright (c) 2015-2018, 2020 Claudiu Popa <pcmanticore@gmail.com> |
| # Copyright (c) 2015 Florian Bruhin <me@the-compiler.org> |
| # Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com> |
| # Copyright (c) 2020-2021 Pierre Sassoulas <pierre.sassoulas@gmail.com> |
| # Copyright (c) 2020 hippo91 <guillaume.peillex@gmail.com> |
| # Copyright (c) 2020 Ram Rachum <ram@rachum.com> |
| # Copyright (c) 2020 谭九鼎 <109224573@qq.com> |
| # Copyright (c) 2020 Anthony Sottile <asottile@umich.edu> |
| |
| # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html |
| # For details: https://github.com/PyCQA/pylint/blob/master/LICENSE |
| |
| """Functions to generate files readable with Georg Sander's vcg |
| (Visualization of Compiler Graphs). |
| |
| You can download vcg at https://rw4.cs.uni-sb.de/~sander/html/gshome.html |
| Note that vcg exists as a debian package. |
| |
| See vcg's documentation for explanation about the different values that |
| maybe used for the functions parameters. |
| """ |
| |
| ATTRS_VAL = { |
| "algos": ( |
| "dfs", |
| "tree", |
| "minbackward", |
| "left_to_right", |
| "right_to_left", |
| "top_to_bottom", |
| "bottom_to_top", |
| "maxdepth", |
| "maxdepthslow", |
| "mindepth", |
| "mindepthslow", |
| "mindegree", |
| "minindegree", |
| "minoutdegree", |
| "maxdegree", |
| "maxindegree", |
| "maxoutdegree", |
| ), |
| "booleans": ("yes", "no"), |
| "colors": ( |
| "black", |
| "white", |
| "blue", |
| "red", |
| "green", |
| "yellow", |
| "magenta", |
| "lightgrey", |
| "cyan", |
| "darkgrey", |
| "darkblue", |
| "darkred", |
| "darkgreen", |
| "darkyellow", |
| "darkmagenta", |
| "darkcyan", |
| "gold", |
| "lightblue", |
| "lightred", |
| "lightgreen", |
| "lightyellow", |
| "lightmagenta", |
| "lightcyan", |
| "lilac", |
| "turquoise", |
| "aquamarine", |
| "khaki", |
| "purple", |
| "yellowgreen", |
| "pink", |
| "orange", |
| "orchid", |
| ), |
| "shapes": ("box", "ellipse", "rhomb", "triangle"), |
| "textmodes": ("center", "left_justify", "right_justify"), |
| "arrowstyles": ("solid", "line", "none"), |
| "linestyles": ("continuous", "dashed", "dotted", "invisible"), |
| } |
| |
| # meaning of possible values: |
| # O -> string |
| # 1 -> int |
| # list -> value in list |
| GRAPH_ATTRS = { |
| "title": 0, |
| "label": 0, |
| "color": ATTRS_VAL["colors"], |
| "textcolor": ATTRS_VAL["colors"], |
| "bordercolor": ATTRS_VAL["colors"], |
| "width": 1, |
| "height": 1, |
| "borderwidth": 1, |
| "textmode": ATTRS_VAL["textmodes"], |
| "shape": ATTRS_VAL["shapes"], |
| "shrink": 1, |
| "stretch": 1, |
| "orientation": ATTRS_VAL["algos"], |
| "vertical_order": 1, |
| "horizontal_order": 1, |
| "xspace": 1, |
| "yspace": 1, |
| "layoutalgorithm": ATTRS_VAL["algos"], |
| "late_edge_labels": ATTRS_VAL["booleans"], |
| "display_edge_labels": ATTRS_VAL["booleans"], |
| "dirty_edge_labels": ATTRS_VAL["booleans"], |
| "finetuning": ATTRS_VAL["booleans"], |
| "manhattan_edges": ATTRS_VAL["booleans"], |
| "smanhattan_edges": ATTRS_VAL["booleans"], |
| "port_sharing": ATTRS_VAL["booleans"], |
| "edges": ATTRS_VAL["booleans"], |
| "nodes": ATTRS_VAL["booleans"], |
| "splines": ATTRS_VAL["booleans"], |
| } |
| NODE_ATTRS = { |
| "title": 0, |
| "label": 0, |
| "color": ATTRS_VAL["colors"], |
| "textcolor": ATTRS_VAL["colors"], |
| "bordercolor": ATTRS_VAL["colors"], |
| "width": 1, |
| "height": 1, |
| "borderwidth": 1, |
| "textmode": ATTRS_VAL["textmodes"], |
| "shape": ATTRS_VAL["shapes"], |
| "shrink": 1, |
| "stretch": 1, |
| "vertical_order": 1, |
| "horizontal_order": 1, |
| } |
| EDGE_ATTRS = { |
| "sourcename": 0, |
| "targetname": 0, |
| "label": 0, |
| "linestyle": ATTRS_VAL["linestyles"], |
| "class": 1, |
| "thickness": 0, |
| "color": ATTRS_VAL["colors"], |
| "textcolor": ATTRS_VAL["colors"], |
| "arrowcolor": ATTRS_VAL["colors"], |
| "backarrowcolor": ATTRS_VAL["colors"], |
| "arrowsize": 1, |
| "backarrowsize": 1, |
| "arrowstyle": ATTRS_VAL["arrowstyles"], |
| "backarrowstyle": ATTRS_VAL["arrowstyles"], |
| "textmode": ATTRS_VAL["textmodes"], |
| "priority": 1, |
| "anchor": 1, |
| "horizontal_order": 1, |
| } |
| |
| |
| # Misc utilities ############################################################### |
| |
| |
| class VCGPrinter: |
| """A vcg graph writer.""" |
| |
| def __init__(self, output_stream): |
| self._stream = output_stream |
| self._indent = "" |
| |
| def open_graph(self, **args): |
| """open a vcg graph""" |
| self._stream.write("%sgraph:{\n" % self._indent) |
| self._inc_indent() |
| self._write_attributes(GRAPH_ATTRS, **args) |
| |
| def close_graph(self): |
| """close a vcg graph""" |
| self._dec_indent() |
| self._stream.write("%s}\n" % self._indent) |
| |
| def node(self, title, **args): |
| """draw a node""" |
| self._stream.write(f'{self._indent}node: {{title:"{title}"') |
| self._write_attributes(NODE_ATTRS, **args) |
| self._stream.write("}\n") |
| |
| def edge(self, from_node, to_node, edge_type="", **args): |
| """draw an edge from a node to another.""" |
| self._stream.write( |
| '%s%sedge: {sourcename:"%s" targetname:"%s"' |
| % (self._indent, edge_type, from_node, to_node) |
| ) |
| self._write_attributes(EDGE_ATTRS, **args) |
| self._stream.write("}\n") |
| |
| # private ################################################################## |
| |
| def _write_attributes(self, attributes_dict, **args): |
| """write graph, node or edge attributes""" |
| for key, value in args.items(): |
| try: |
| _type = attributes_dict[key] |
| except KeyError as e: |
| raise Exception( |
| """no such attribute %s |
| possible attributes are %s""" |
| % (key, attributes_dict.keys()) |
| ) from e |
| |
| if not _type: |
| self._stream.write(f'{self._indent}{key}:"{value}"\n') |
| elif _type == 1: |
| self._stream.write(f"{self._indent}{key}:{int(value)}\n") |
| elif value in _type: |
| self._stream.write(f"{self._indent}{key}:{value}\n") |
| else: |
| raise Exception( |
| f"""value {value} isn't correct for attribute {key} |
| correct values are {type}""" |
| ) |
| |
| def _inc_indent(self): |
| """increment indentation""" |
| self._indent = " %s" % self._indent |
| |
| def _dec_indent(self): |
| """decrement indentation""" |
| self._indent = self._indent[:-2] |