topos.graphs.cfg.builder¶
CFG Builder¶
Construct a ControlFlowGraph from a UAST root. The builder walks the
language-independent UAST kind set (IfStmt, ForStmt, WhileStmt,
MatchStmt, ReturnStmt, BreakStmt, ContinueStmt,
ThrowStmt, TryStmt, …) and produces a single CFG that contains the
union of every callable’s intra-procedural flow.
Design choices¶
One synthetic entry block; one synthetic exit block. All
ReturnStmtnodes wire into the exit block. This keeps the connected-component countP = 1so cyclomatic complexity isE - N + 2.Each callable (
FunctionDecl/MethodDecl) is built independently and appended; module-level top-level statements are treated as one additional implicit callable.Decision nodes (the loop test, the if condition, the switch discriminant) are part of the basic block that ends in the branch. Branch successors are wired with labeled edges (
TRUE/FALSE/SWITCH_CASE).break/continueare resolved against the innermost enclosing loop or switch via a stack maintained during the walk.
This is intentionally a structural CFG — it does not unfold short-circuit operators, ternary expressions, or generator expressions. Those would inflate cyclomatic complexity in ways the README’s policy doesn’t currently price.
- class topos.graphs.cfg.builder.CFGBuildState(blocks=<factory>, edges=<factory>, _next_id=0, loop_stack=<factory>, exit_id=-1)[source]
Bases:
objectMutable state threaded through the recursive builder.
- blocks
- edges
- loop_stack
- exit_id = -1
- new_block(label='')[source]
- add_edge(source, target, kind=EdgeKind.UNCONDITIONAL)[source]
- topos.graphs.cfg.builder.build_cfg_from_uast(uast_root)[source]
Build a CFG covering every callable reachable from
uast_root.- Returns:
(blocks, edges, entry_id, exit_id).
The entry block dispatches by unconditional edges into each callable’s individual entry; all callables share the single synthetic exit block. Module-level top-level statements (those not inside any callable) form one additional implicit callable.