Source code for topos.graphs.cfg.object
"""
ControlFlowGraph Representation
-------------------------------
The CFG translational functor's image in the Topos E. Implements the
``Representation`` protocol and feeds the SIMPLE generator of ℋ.
Metrics emitted (namespace ``cfg.*``):
cfg.cyclomatic — McCabe complexity (E - N + 2P, with P = 1).
cfg.essential — essential complexity (structured-decomposition reduction).
cfg.nesting_depth — maximum static nesting depth.
cfg.longest_path — longest acyclic path (a rough proxy for path explosion).
"""
from __future__ import annotations
from dataclasses import dataclass, field
from topos.graphs.cfg.builder import build_cfg_from_uast
from topos.graphs.cfg.models import BasicBlock, CFGEdge
from topos.graphs.uast.models import UASTNode
[docs]
@dataclass
class ControlFlowGraph:
"""
A language-independent control-flow graph built on UAST.
Construct via :meth:`from_uast` for a fully-populated graph, or pass
pre-computed ``blocks`` and ``edges`` for tests.
"""
blocks: dict[int, BasicBlock] = field(default_factory=dict)
edges: list[CFGEdge] = field(default_factory=list)
entry_id: int = 0
exit_id: int = 1
@property
def name(self) -> str:
return "cfg"
@property
def dimension(self) -> str:
# SIMPLE generator of H(G_qual).
return "simple"
[docs]
@classmethod
def from_uast(cls, uast_root: UASTNode) -> ControlFlowGraph:
"""Build a CFG from a UAST root, covering every callable."""
blocks, edges, entry_id, exit_id = build_cfg_from_uast(uast_root)
return cls(blocks=blocks, edges=edges, entry_id=entry_id, exit_id=exit_id)
# ------------------------------------------------------------------
# Graph queries
# ------------------------------------------------------------------
[docs]
def successors(self, block_id: int) -> list[CFGEdge]:
return [e for e in self.edges if e.source == block_id]
[docs]
def predecessors(self, block_id: int) -> list[CFGEdge]:
return [e for e in self.edges if e.target == block_id]
# ------------------------------------------------------------------
# Metrics
# ------------------------------------------------------------------
[docs]
def metrics(self) -> dict[str, float]:
from topos.functors.probes.cfg.complexity import (
cyclomatic_complexity,
essential_complexity,
max_nesting_depth,
)
from topos.functors.probes.cfg.paths import longest_acyclic_path
# We delegate these probes to Rust for performance.
return {
"cfg.cyclomatic": float(cyclomatic_complexity(self)),
"cfg.essential": float(essential_complexity(self)),
"cfg.nesting_depth": float(max_nesting_depth(self)),
"cfg.longest_path": float(longest_acyclic_path(self)),
}