Before refactoring

This commit is contained in:
Jan-Niclas Loosen
2026-01-22 10:02:16 +01:00
parent c66222050b
commit 489f385161
8 changed files with 137 additions and 232 deletions

View File

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

View File

@@ -1,11 +1,12 @@
class CFG_Node:
__counter = 1
def __init__(self, ast_node = None):
def __init__(self, ast_node=None):
self.ast_node = ast_node
self.children = set()
self.parents = set()
self.label = None # Optional label for the node
self.id = CFG_Node.__counter
CFG_Node.__counter += 1
@@ -15,26 +16,37 @@ class CFG_Node:
def get_parents(self):
return self.parents
def add_child(self, child: CFG_Node, propagate = True):
def add_child(self, child: 'CFG_Node', propagate=True):
if propagate:
child.parents.add(self)
self.children.add(child)
def add_parent(self, parent: CFG_Node, propagate = True):
def add_parent(self, parent: 'CFG_Node', propagate=True):
if propagate:
parent.add_child(self)
self.parents.add(parent)
def remove_child(self, child: CFG_Node, propagate = True):
def remove_child(self, child: 'CFG_Node', propagate=True):
if propagate:
child.parents.remove(self)
self.children.remove(child)
def remove_parent(self, parent: CFG_Node, propagate = True):
def remove_parent(self, parent: 'CFG_Node', propagate=True):
if propagate:
parent.children.remove(self)
self.parents.remove(parent)
def __str__(self):
if self.label:
return f"CFG_Node({self.id}, label='{self.label}')"
elif self.ast_node:
return f"CFG_Node({self.id}, ast={type(self.ast_node).__name__})"
else:
return f"CFG_Node({self.id})"
def __repr__(self):
return self.__str__()
class CFG_START(CFG_Node):
def dot_shape(self):