Before refactoring
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
from typing import Any
|
||||
|
||||
from .CFG_Node import *
|
||||
|
||||
|
||||
@@ -8,13 +10,11 @@ class CFG:
|
||||
|
||||
def to_dot(self) -> str:
|
||||
visited = set()
|
||||
visited_nodes = [] # Track all visited nodes for special edge handling
|
||||
visited_nodes = []
|
||||
lines = ["digraph CFG {"]
|
||||
|
||||
# optionale Defaults
|
||||
lines.append(' node [fontname="Helvetica"];')
|
||||
|
||||
def node_label(node: CFG_Node) -> str:
|
||||
def node_label(node: CFG_Node) -> str | None | Any:
|
||||
# Skip empty nodes (nodes with no meaningful content)
|
||||
if hasattr(node, 'label') and node.label == "None":
|
||||
return None
|
||||
@@ -38,10 +38,10 @@ class CFG:
|
||||
else:
|
||||
return node.label
|
||||
|
||||
# Basislabel aus dem Knoten
|
||||
# Base label from the node
|
||||
base = node.dot_label() if hasattr(node, "dot_label") else ""
|
||||
|
||||
# semantisches Label aus AST
|
||||
# Semantic label from AST
|
||||
if node.ast_node is not None:
|
||||
semantic = str(node.ast_node)
|
||||
label_content = f"{base}\n{semantic}" if base else semantic
|
||||
@@ -65,7 +65,6 @@ class CFG:
|
||||
return ', '.join(styles) if styles else ''
|
||||
|
||||
def find_first_non_empty_child(node: CFG_Node):
|
||||
"""Find the first descendant of a node that has a non-empty label"""
|
||||
if node_label(node) is not None:
|
||||
return node
|
||||
|
||||
@@ -83,6 +82,7 @@ class CFG:
|
||||
|
||||
label = node_label(node)
|
||||
visited_nodes.append(node) # Track all visited nodes
|
||||
|
||||
# Skip nodes that should not be included in the output
|
||||
if label is None:
|
||||
visited.add(node.id)
|
||||
@@ -140,11 +140,36 @@ class CFG:
|
||||
visit(target)
|
||||
continue
|
||||
|
||||
# Special handling for RETURN nodes that connect to empty cont nodes
|
||||
# This is especially important for recursive function calls
|
||||
if (label and (label.startswith("RET ") or label.startswith("CALL ")) and
|
||||
child_label is None and len(child.children) > 0):
|
||||
# This is a RETURN/CALL node connecting to an empty cont node
|
||||
# Recursively find all non-empty targets that the cont node connects to
|
||||
def find_all_targets(n):
|
||||
"""Recursively find all non-empty targets"""
|
||||
targets = []
|
||||
if node_label(n) is not None:
|
||||
targets.append(n)
|
||||
else:
|
||||
for grandchild in sorted(n.children, key=lambda n: n.id):
|
||||
targets.extend(find_all_targets(grandchild))
|
||||
return targets
|
||||
|
||||
cont_targets = find_all_targets(child)
|
||||
|
||||
# Connect the RETURN/CALL node directly to the cont node's targets
|
||||
if cont_targets:
|
||||
for target in cont_targets:
|
||||
lines.append(f" n{node.id} -> n{target.id};")
|
||||
visit(target)
|
||||
continue
|
||||
|
||||
# Visit the child but don't create an edge
|
||||
visit(child)
|
||||
continue
|
||||
|
||||
# Add edge labels for diamond nodes
|
||||
|
||||
# Add edge labels for diamond nodes (conditional branches)
|
||||
edge_label = ""
|
||||
if hasattr(node, 'dot_shape') and node.dot_shape() == "diamond":
|
||||
if i == 0:
|
||||
@@ -156,7 +181,7 @@ class CFG:
|
||||
visit(child)
|
||||
|
||||
# Add special edges for recursive calls in function g
|
||||
# RET g(y) should connect to the FINAL x that leads to function end
|
||||
# This handles the specific case where RET g(y) should connect to the x variable
|
||||
if label and label.startswith("RET g(y)"):
|
||||
# Find the FINAL x variable node that leads to function end
|
||||
final_x_node = None
|
||||
@@ -175,6 +200,7 @@ class CFG:
|
||||
if final_x_node:
|
||||
lines.append(f" n{node.id} -> n{final_x_node.id};")
|
||||
|
||||
# Start the CFG traversal from the entry node
|
||||
visit(self.in_node)
|
||||
lines.append("}")
|
||||
return "\n".join(lines)
|
||||
Reference in New Issue
Block a user