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 ReturnStmt nodes wire into the exit block. This keeps the connected-component count P = 1 so cyclomatic complexity is E - 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 / continue are 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: object

Mutable 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.