@@ -83,11 +83,22 @@ def GetOrCreateNode(self, name):
8383 return node
8484
8585 def _MarkExitNodes (self ):
86- # Algorithm:
87- # visit all nodes via goto or nested connections, starting from __begin__
88- # mark the node as exit nodes if it contains
86+ # A node is an exit node if:
87+ # 1. it contains an "exit" command with no target
88+ # or
89+ # 2. it's reached from the starting node via "goto" or "nested" connections
90+ # and it contains an exit command or a "goto eof" command.
91+
92+ # Identify all nodes with an exit command with no targets.
93+ for node in self .nodes .values ():
94+ all_commands = set (itertools .chain .from_iterable (line .commands for line in node .code ))
95+ exit_cmd = Command ("exit" , "" )
96+ if exit_cmd in all_commands :
97+ node .is_exit_node = True
98+
99+ # Visit the call graph to find nodes satisfying condition #2.
89100 q = [self .first_node ]
90- visited = set ()
101+ visited = set () # Used to avoid loops, since the call graph is not acyclic.
91102
92103 while q :
93104 cur = q .pop ()
@@ -99,7 +110,7 @@ def _MarkExitNodes(self):
99110 else :
100111 all_commands = itertools .chain .from_iterable (line .commands for line in cur .code )
101112 for command in all_commands :
102- if command [0 ] == "exit" and command [ 1 ] == "" or command [0 ] == "goto" and command [1 ] == "eof" :
113+ if command [0 ] == "exit" or ( command [0 ] == "goto" and command [1 ] == "eof" ) :
103114 cur .is_exit_node = True
104115 break
105116
@@ -166,11 +177,6 @@ def _AnnotateNode(self, node):
166177
167178 if command == "exit" and target == "" :
168179 line .terminating = True
169-
170- # Note that exit node detection is implemented in _MarkExitNodes, but that algorithm only
171- # follows "nested" and "goto" nodes, therefore we need to mark nodes that contain naked
172- # "exit" statements here, since they are terminating even when reached via "call" statements.
173- node .is_exit_node = True
174180
175181 @staticmethod
176182 def Build (input_file , log_file = sys .stderr ):
0 commit comments