Skip to content

Commit 24e3623

Browse files
ankitsaxena21lupino3
authored andcommitted
Improved comments and indentations (#32)
* Update cmd-call-graph.py * Improved comments and indentations * Update core.py * Update cmd-call-graph.py * Update core.py
1 parent 2e8c7e5 commit 24e3623

File tree

4 files changed

+67
-49
lines changed

4 files changed

+67
-49
lines changed

callgraph/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from .callgraph import main
22

3-
main()
3+
main()

callgraph/core.py

Lines changed: 54 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
Command = collections.namedtuple("Command", ["command", "target"])
1010

1111
# Line of code. Not a namedtuple because we need mutability.
12+
13+
1214
class CodeLine:
1315
def __init__(self, number, text, terminating=False, noop=False):
1416
self.number = number
@@ -24,15 +26,18 @@ def AddCommand(self, command):
2426

2527
def __repr__(self):
2628
return "[{0} (terminating: {1}, noop: {2}, commands: {3})] {4}".format(self.number, self.terminating, self.noop, self.commands, self.text)
27-
29+
2830
def __eq__(self, other):
2931
return other is not None and self.number == other.number and self.text == other.text and self.terminating == other.terminating
3032

3133
# Connection between two nodes.
32-
# dst is the name of the target node, kind is the type of connection, line_number is the line where the call/goto happens.
34+
# dst is the name of the target node, kind is the type of connection,
35+
# line_number is the line where the call/goto happens.
3336
Connection = collections.namedtuple("Connection", ["dst", "kind", "line_number"])
3437

3538
# Node in the call graph.
39+
40+
3641
class Node:
3742
def __init__(self, name):
3843
self.name = name
@@ -46,38 +51,38 @@ def __init__(self, name):
4651

4752
def AddConnection(self, dst, kind, line_number=NO_LINE_NUMBER):
4853
self.connections.add(Connection(dst, kind, line_number))
49-
54+
5055
def AddCodeLine(self, line_number, code):
5156
self.code.append(CodeLine(line_number, code.strip().lower(), False))
5257
self.loc += 1
53-
58+
5459
def GetCommandCount(self):
5560
node_counter = collections.Counter()
5661

5762
for line in self.code:
5863
for command, count in line.commands_counter.items():
5964
node_counter[command] += count
60-
6165
return node_counter
62-
66+
6367
def __repr__(self):
6468
return "{0}. {1}, {2}".format(self.name, self.code, self.connections)
6569

6670
def __lt__(self, other):
67-
if other == None:
71+
if other is None:
6872
return False
6973
return self.name < other.name
7074

75+
7176
class CallGraph:
7277
def __init__(self, log_file=sys.stderr):
7378
self.nodes = {}
7479
self.log_file = log_file
7580
self.first_node = None
76-
81+
7782
def GetOrCreateNode(self, name):
7883
if name in self.nodes:
7984
return self.nodes[name]
80-
85+
8186
node = Node(name)
8287
self.nodes[name] = node
8388
return node
@@ -86,7 +91,8 @@ def _MarkExitNodes(self):
8691
# A node is an exit node if:
8792
# 1. it contains an "exit" command with no target
8893
# or
89-
# 2. it's reached from the starting node via "goto" or "nested" connections
94+
# 2. it's reached from the starting node via "goto" or
95+
# "nested" connections
9096
# and it contains an exit command or a "goto eof" command.
9197

9298
# Identify all nodes with an exit command with no targets.
@@ -98,7 +104,7 @@ def _MarkExitNodes(self):
98104

99105
# Visit the call graph to find nodes satisfying condition #2.
100106
q = [self.first_node]
101-
visited = set() # Used to avoid loops, since the call graph is not acyclic.
107+
visited = set() # Used to avoid loops, since the call graph is not acyclic.
102108

103109
while q:
104110
cur = q.pop()
@@ -119,19 +125,22 @@ def _MarkExitNodes(self):
119125
continue
120126
if connection.kind == "nested" or connection.kind == "goto":
121127
q.append(self.nodes[connection.dst])
122-
123128

124-
# Adds to each node information depending on the contents of the code, such as connections
125-
# deriving from goto/call commands and whether the node is terminating or not.
129+
# Adds to each node information depending on the
130+
# contents of the code, such as connections
131+
# deriving from goto/call commands and
132+
# whether the node is terminating or not.
126133
def _AnnotateNode(self, node):
127134
print(u"Annotating node {0} (line {1})".format(node.original_name, node.line_number), file=self.log_file)
128135
for i in range(len(node.code)):
129136
line = node.code[i]
130137
line_number = line.number
131138
text = line.text
132139

133-
# Tokenize the line of code, and store all elements that warrant augmenting the
134-
# node in a list (interesting_commands), which will then be processed later.
140+
# Tokenize the line of code, and store all elements that
141+
# warrant augmenting the
142+
# node in a list (interesting_commands), which will then
143+
# be processed later.
135144
tokens = text.strip().lower().split()
136145
if not tokens:
137146
line.noop = True
@@ -164,31 +173,32 @@ def _AnnotateNode(self, node):
164173
continue
165174
line.AddCommand(Command("call", block_name))
166175
continue
167-
176+
168177
if token == "exit" or token == "@exit":
169178
target = ""
170179
if i+1 < len(tokens):
171180
target = tokens[i+1]
172181
line.AddCommand(Command("exit", target))
173-
182+
174183
for command, target in line.commands:
175184
if command == "call" or command == "goto":
176185
node.AddConnection(target, command, line_number)
177186
print(u"Line {} has a goto towards: <{}>. Current block: {}".format(line_number, target, node.name), file=self.log_file)
178-
187+
179188
if (command == "goto" and target == "eof") or command == "exit":
180189
line.terminating = True
181190

182191
if command == "exit" and target == "":
183192
line.terminating = True
184-
193+
185194
@staticmethod
186195
def Build(input_file, log_file=sys.stderr):
187196
call_graph = CallGraph._ParseSource(input_file, log_file)
188197
for node in call_graph.nodes.values():
189198
call_graph._AnnotateNode(node)
190-
191-
# Prune away EOF if it is a virtual node (no line number) and there are no call/nested connections to it.
199+
200+
# Prune away EOF if it is a virtual node (no line number) and
201+
# there are no call/nested connections to it.
192202
eof = call_graph.GetOrCreateNode("eof")
193203
all_connections = itertools.chain.from_iterable(n.connections for n in call_graph.nodes.values())
194204
destinations = set((c.dst, c.kind) for c in all_connections)
@@ -200,8 +210,9 @@ def Build(input_file, log_file=sys.stderr):
200210
print(u"Removing {} eof connections in node {}".format(len(eof_connections), node.name), file=log_file)
201211
for c in eof_connections:
202212
node.connections.remove(c)
203-
204-
# Warn the user if there are goto connections to eof, which will not be executed by CMD.
213+
214+
# Warn the user if there are goto connections to eof
215+
# which will not be executed by CMD.
205216
if eof.line_number != NO_LINE_NUMBER and ("eof", "goto") in destinations:
206217
print(u"WARNING: there are goto connections to eof, but CMD will not execute that code via goto.", file=log_file)
207218

@@ -212,7 +223,8 @@ def Build(input_file, log_file=sys.stderr):
212223
cur_node = nodes_by_line_number[i]
213224
prev_node = nodes_by_line_number[i-1]
214225

215-
# Special case: the previous node has no code or all lines are comments / empty lines.
226+
# Special case: the previous node has no code or all lines are
227+
# comments / empty lines.
216228
all_noop = all(line.noop for line in prev_node.code)
217229
if not prev_node.code or all_noop:
218230
print(u"Adding nested connection between {0} and {1} because all_noop ({2}) or empty code ({3})".format(
@@ -221,15 +233,16 @@ def Build(input_file, log_file=sys.stderr):
221233
break
222234

223235
# Heuristic for "nested" connections:
224-
# iterate the previous node's commands, and create a nested connection
225-
# only if the command that logically precedes the current node does not
226-
# contain a goto or an exit (which would mean that the current node is not reached
227-
# by "flowing" from the previous node to the current node.)
236+
# iterate the previous node's commands, and create a nested
237+
# connection only if the command that logically precedes the
238+
# current node does not contain a goto or an exit (which would mean
239+
# that the current node is not reached by "flowing" from the
240+
# previous node to the current node.)
228241
for line in reversed(prev_node.code):
229242
# Skip comments and empty lines.
230243
if line.noop:
231244
continue
232-
245+
233246
commands = set(c.command for c in line.commands)
234247
if "exit" not in commands and "goto" not in commands:
235248
print(u"Adding nested connection between {0} and {1} because there is a non-exit or non-goto command.".format(
@@ -246,10 +259,10 @@ def Build(input_file, log_file=sys.stderr):
246259

247260
return call_graph
248261

249-
# Creates a call graph from an input file, parsing the file in blocks and creating
250-
# one node for each block. Note that the nodes don't contain any information that
251-
# depend on the contents of the node, as this is just the starting point for the
252-
# processing.
262+
# Creates a call graph from an input file, parsing the file in blocks and
263+
# creating one node for each block. Note that the nodes don't contain any
264+
# information that depend on the contents of the node, as this is just the
265+
# starting point for the processing.
253266
@staticmethod
254267
def _ParseSource(input_file, log_file=sys.stderr):
255268
call_graph = CallGraph(log_file)
@@ -267,10 +280,12 @@ def _ParseSource(input_file, log_file=sys.stderr):
267280

268281
# Start of new block.
269282
if line.startswith(":") and not line.startswith("::"):
270-
# In the off chance that there are multiple words, cmd considers the first word the label name.
283+
# In the off chance that there are multiple words,
284+
# cmd considers the first word the label name.
271285
original_block_name = line[1:].split()[0].strip()
272286

273-
# Since cmd is case-insensitive, let's convert block names to lowercase.
287+
# Since cmd is case-insensitive, let's convert block names to
288+
# lowercase.
274289
block_name = original_block_name.lower()
275290

276291
print(u"Line {} defines a new block: <{}>".format(line_number, block_name), file=log_file)
@@ -279,14 +294,15 @@ def _ParseSource(input_file, log_file=sys.stderr):
279294
next_node.line_number = line_number
280295
next_node.original_name = original_block_name
281296

282-
# If this node is defined on line one, remove __begin__, so we avoid having two
297+
# If this node is defined on line one, remove __begin__,
298+
# so we avoid having two
283299
# nodes with the same line number.
284300
if line_number == 1:
285301
del call_graph.nodes["__begin__"]
286302
call_graph.first_node = next_node
287303

288304
cur_node = next_node
289-
305+
290306
cur_node.AddCodeLine(line_number, line)
291307

292308
return call_graph

callgraph/render.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44

55
from . import core
66

7+
78
def _Escape(input_string):
89
return input_string.replace("%", r"\%")
910

1011

1112
COLORS = {
12-
'goto': '"#d83b01"', # Orange
13-
'nested': '"#008575"', # Teal
14-
'call': '"#0078d4"', # Blue
15-
'terminating': '"#e6e6e6"', # Light gray
13+
'goto': '"#d83b01"', # Orange
14+
'nested': '"#008575"', # Teal
15+
'call': '"#0078d4"', # Blue
16+
'terminating': '"#e6e6e6"', # Light gray
1617
}
1718

1819
def PrintDot(call_graph, out_file=sys.stdout, log_file=sys.stderr, show_all_calls=True, show_node_stats=False, nodes_to_hide=None):
@@ -28,7 +29,7 @@ def PrintDot(call_graph, out_file=sys.stdout, log_file=sys.stderr, show_all_call
2829
pretty_name = name
2930
if node.original_name != "":
3031
pretty_name = node.original_name
31-
32+
3233
print(u"Processing node {0} (using name: {1})".format(node.name, pretty_name), file=log_file)
3334

3435
attributes = []
@@ -45,7 +46,7 @@ def PrintDot(call_graph, out_file=sys.stdout, log_file=sys.stderr, show_all_call
4546
if external_call_count > 0:
4647
text = "call" if external_call_count == 1 else "calls"
4748
label_lines.append("<sub>[{} external {}]</sub>".format(external_call_count, text))
48-
49+
4950
if node.is_exit_node:
5051
attributes.append("color={}".format(COLORS["terminating"]))
5152
attributes.append("style=filled")
@@ -56,11 +57,12 @@ def PrintDot(call_graph, out_file=sys.stdout, log_file=sys.stderr, show_all_call
5657
if attributes:
5758
print(u"\"{}\" [{}]".format(name, ",".join(attributes)), file=out_file)
5859

59-
# De-duplicate connections by line number if show_all_calls is set to False.
60+
# De-duplicate connections by line number if show_all_calls is set to
61+
# False.
6062
connections = node.connections
6163
if not show_all_calls:
6264
connections = list(set(core.Connection(c.dst, c.kind, core.NO_LINE_NUMBER) for c in connections))
63-
65+
6466
for c in sorted(connections):
6567
# Remove EOF connections if necessary.
6668
if nodes_to_hide and (c.dst in nodes_to_hide):

scripts/generate-sample-cmd.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@
2323
for function in functions:
2424
code.append(":{}".format(function))
2525
loc = random.randint(1, MAX_LENGTH)
26-
26+
2727
for i in range(loc):
2828
if random.random() < CALL_PROBABILITY:
2929
target = random.choice(functions)
3030
code.append(u" call :{}".format(target))
3131
else:
3232
code.append(u" ; some code goes here.")
33-
33+
3434
if random.random() > NESTED_PROBABILITY:
3535
code.append(u"exit /b 0")
3636

0 commit comments

Comments
 (0)