Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2@file
3@brief Helpers around language grammar.
4This module requires `antlr4 <https://pypi.python.org/pypi/antlr4-python3-runtime/>`_.
5"""
6from antlr4 import ParseTreeListener
9class TreeGraphListener(ParseTreeListener):
11 """
12 This class is an attempt to run through the tree
13 and to convert into a graph.
15 .. exref::
16 :title: Draw a grammar graph for a small code
18 ::
20 from pyensae.languages import get_parser_lexer, parse_code, get_tree_graph
21 from pyensae.graph_helper import run_dot
23 code = '''
24 namespace hello
25 {
26 public static class world
27 {
28 public static double function(double x, doubly y)
29 {
30 return x+y ;
31 }
32 }
33 }
34 '''
36 clparser, cllexer = get_parser_lexer("C#")
37 parser = parse_code(code, clparser, cllexer)
38 tree = parser.compilation_unit()
39 st = get_tree_graph(tree, parser)
40 dot = st.to_dot()
42 with open(name, "w") as f:
43 f.write(dot)
44 img = os.path.join(temp, "graph.png")
45 run_dot(name, img)
46 """
48 def __init__(self, parser, verbose=False, fLOG=None):
49 """
50 constructor
52 @param parser parser used to parse the code
53 @param verbose display information along the path
54 @param fLOG logging function
55 """
56 ParseTreeListener.__init__(self)
57 self.parser = parser
58 self.vertices = {}
59 self.edges = {}
60 self.verbose = verbose
61 self.fLOG = fLOG
63 @property
64 def Vertices(self):
65 """
66 return vertices
67 """
68 return self.vertices
70 @property
71 def Edges(self):
72 """
73 return edges
74 """
75 return self.edges
77 def _get_key_node(self, node):
78 line, col = node.symbol.line, node.symbol.column
79 return line, col, "#END#"
81 def _get_key_context(self, ctx):
82 line, col = ctx.start.line, ctx.start.column
83 class_name = ctx.__class__.__name__
84 if class_name.endswith("Context"):
85 class_name = class_name[0].lower() + class_name[1:-7]
86 return line, col, class_name
88 def visitTerminal(self, node):
89 """
90 event
91 """
92 key = self._get_key_node(node)
93 val = (node.getText(), node, node.parentCtx)
94 self.vertices[key] = val
95 kc = self._get_key_context(node.parentCtx)
96 self.edges[kc, key] = 1
98 def visitErrorNode(self, node):
99 """
100 event
101 """
102 key = self._get_key_node(node)
103 val = ("#ERROR#", node, node.parentCtx)
104 self.vertices[key] = val
105 kc = self._get_key_context(node.parentCtx)
106 self.edges[kc, key] = 1
108 def enterEveryRule(self, ctx):
109 """
110 event
111 """
112 key = self._get_key_context(ctx)
113 self.vertices[key] = (key[-1], ctx, ctx.parentCtx)
114 if ctx.parentCtx is not None:
115 kc = self._get_key_context(ctx.parentCtx)
116 self.edges[kc, key] = 1
118 if self.verbose: # pragma: no cover
119 self.fLOG("+", type(ctx), ctx)
120 self.fLOG("+", ctx.__dict__.keys())
121 self.fLOG(" +", type(ctx.start), ctx.start)
122 self.fLOG(" +", ctx.start.__dict__.keys())
123 text = ctx.getText()
124 print(" text:", text)
125 if "node" in ctx.__dict__:
126 self.fLOG(" +", type(ctx.node), ctx.node)
127 self.fLOG(" +", ctx.node.__dict__.keys())
129 def exitEveryRule(self, ctx):
130 """
131 event
132 """
133 if self.verbose: # pragma: no cover
134 self.fLOG("-", type(ctx), ctx)
136 def to_networkx(self):
137 """
138 convert the graph into networkx
140 @return `networkx Graph <https://networkx.github.io/documentation/latest/tutorial/tutorial.html?highlight=graph>`_
141 """
142 import networkx # pylint: disable=C0415
143 verti = self.Vertices
144 edges = self.Edges
146 G = networkx.Graph()
147 for v in verti:
148 G.add_node(v)
149 for k in edges:
150 G.add_edge(k[0], k[1])
151 return G
153 def draw(self, ax=None):
154 """
155 draw the graph with networkx on matplotlib
157 @param matplotlib axis
158 """
159 if ax is None:
160 import matplotlib.pyplot as plt # pylint: disable=C0415
161 fig, ax = plt.subplots()
163 G = self.to_networkx()
165 import networkx # pylint: disable=C0415
166 pos = networkx.spring_layout(G) # positions for all nodes
167 verti = self.Vertices
168 labels = {p: verti[p][0] for p in pos}
170 networkx.draw_networkx_nodes(G, pos, ax=ax)
171 networkx.draw_networkx_edges(G, pos, ax=ax)
172 networkx.draw_networkx_labels(G, pos, labels, font_size=16)
174 def to_dot(self):
175 """
176 export the graph to DOT format
178 @return string
179 """
180 verti = self.Vertices
181 edges = self.Edges
183 ids = {}
184 for k in verti:
185 ids[k] = len(ids)
187 rows = ["digraph {"]
188 for k, v in verti.items():
189 rows.append('%d [label="%s"];' % (
190 ids[k], v[0].replace("\\", "\\\\").replace('"', '\\"')))
191 for k in edges:
192 a, b = ids[k[0]], ids[k[1]]
193 rows.append("%d -> %d ;" % (a, b))
194 rows.append("}")
195 return "\n".join(rows)