Hide keyboard shortcuts

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 

7 

8 

9class TreeGraphListener(ParseTreeListener): 

10 

11 """ 

12 This class is an attempt to run through the tree 

13 and to convert into a graph. 

14 

15 .. exref:: 

16 :title: Draw a grammar graph for a small code 

17 

18 :: 

19 

20 from pyensae.languages import get_parser_lexer, parse_code, get_tree_graph 

21 from pyensae.graph_helper import run_dot 

22 

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 ''' 

35 

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() 

41 

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 """ 

47 

48 def __init__(self, parser, verbose=False, fLOG=None): 

49 """ 

50 constructor 

51 

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 

62 

63 @property 

64 def Vertices(self): 

65 """ 

66 return vertices 

67 """ 

68 return self.vertices 

69 

70 @property 

71 def Edges(self): 

72 """ 

73 return edges 

74 """ 

75 return self.edges 

76 

77 def _get_key_node(self, node): 

78 line, col = node.symbol.line, node.symbol.column 

79 return line, col, "#END#" 

80 

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 

87 

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 

97 

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 

107 

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 

117 

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()) 

128 

129 def exitEveryRule(self, ctx): 

130 """ 

131 event 

132 """ 

133 if self.verbose: # pragma: no cover 

134 self.fLOG("-", type(ctx), ctx) 

135 

136 def to_networkx(self): 

137 """ 

138 convert the graph into networkx 

139 

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 

145 

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 

152 

153 def draw(self, ax=None): 

154 """ 

155 draw the graph with networkx on matplotlib 

156 

157 @param matplotlib axis 

158 """ 

159 if ax is None: 

160 import matplotlib.pyplot as plt # pylint: disable=C0415 

161 fig, ax = plt.subplots() 

162 

163 G = self.to_networkx() 

164 

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} 

169 

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) 

173 

174 def to_dot(self): 

175 """ 

176 export the graph to DOT format 

177 

178 @return string 

179 """ 

180 verti = self.Vertices 

181 edges = self.Edges 

182 

183 ids = {} 

184 for k in verti: 

185 ids[k] = len(ids) 

186 

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)