(root)/
Python-3.11.7/
Tools/
peg_generator/
pegen/
sccutils.py
       1  # Adapted from mypy (mypy/build.py) under the MIT license.
       2  
       3  from typing import *
       4  
       5  
       6  def strongly_connected_components(
       7      vertices: AbstractSet[str], edges: Dict[str, AbstractSet[str]]
       8  ) -> Iterator[AbstractSet[str]]:
       9      """Compute Strongly Connected Components of a directed graph.
      10  
      11      Args:
      12        vertices: the labels for the vertices
      13        edges: for each vertex, gives the target vertices of its outgoing edges
      14  
      15      Returns:
      16        An iterator yielding strongly connected components, each
      17        represented as a set of vertices.  Each input vertex will occur
      18        exactly once; vertices not part of a SCC are returned as
      19        singleton sets.
      20  
      21      From http://code.activestate.com/recipes/578507/.
      22      """
      23      identified: Set[str] = set()
      24      stack: List[str] = []
      25      index: Dict[str, int] = {}
      26      boundaries: List[int] = []
      27  
      28      def dfs(v: str) -> Iterator[Set[str]]:
      29          index[v] = len(stack)
      30          stack.append(v)
      31          boundaries.append(index[v])
      32  
      33          for w in edges[v]:
      34              if w not in index:
      35                  yield from dfs(w)
      36              elif w not in identified:
      37                  while index[w] < boundaries[-1]:
      38                      boundaries.pop()
      39  
      40          if boundaries[-1] == index[v]:
      41              boundaries.pop()
      42              scc = set(stack[index[v] :])
      43              del stack[index[v] :]
      44              identified.update(scc)
      45              yield scc
      46  
      47      for v in vertices:
      48          if v not in index:
      49              yield from dfs(v)
      50  
      51  
      52  def topsort(
      53      data: Dict[AbstractSet[str], Set[AbstractSet[str]]]
      54  ) -> Iterable[AbstractSet[AbstractSet[str]]]:
      55      """Topological sort.
      56  
      57      Args:
      58        data: A map from SCCs (represented as frozen sets of strings) to
      59              sets of SCCs, its dependencies.  NOTE: This data structure
      60              is modified in place -- for normalization purposes,
      61              self-dependencies are removed and entries representing
      62              orphans are added.
      63  
      64      Returns:
      65        An iterator yielding sets of SCCs that have an equivalent
      66        ordering.  NOTE: The algorithm doesn't care about the internal
      67        structure of SCCs.
      68  
      69      Example:
      70        Suppose the input has the following structure:
      71  
      72          {A: {B, C}, B: {D}, C: {D}}
      73  
      74        This is normalized to:
      75  
      76          {A: {B, C}, B: {D}, C: {D}, D: {}}
      77  
      78        The algorithm will yield the following values:
      79  
      80          {D}
      81          {B, C}
      82          {A}
      83  
      84      From http://code.activestate.com/recipes/577413/.
      85      """
      86      # TODO: Use a faster algorithm?
      87      for k, v in data.items():
      88          v.discard(k)  # Ignore self dependencies.
      89      for item in set.union(*data.values()) - set(data.keys()):
      90          data[item] = set()
      91      while True:
      92          ready = {item for item, dep in data.items() if not dep}
      93          if not ready:
      94              break
      95          yield ready
      96          data = {item: (dep - ready) for item, dep in data.items() if item not in ready}
      97      assert not data, "A cyclic dependency exists amongst %r" % data
      98  
      99  
     100  def find_cycles_in_scc(
     101      graph: Dict[str, AbstractSet[str]], scc: AbstractSet[str], start: str
     102  ) -> Iterable[List[str]]:
     103      """Find cycles in SCC emanating from start.
     104  
     105      Yields lists of the form ['A', 'B', 'C', 'A'], which means there's
     106      a path from A -> B -> C -> A.  The first item is always the start
     107      argument, but the last item may be another element, e.g.  ['A',
     108      'B', 'C', 'B'] means there's a path from A to B and there's a
     109      cycle from B to C and back.
     110      """
     111      # Basic input checks.
     112      assert start in scc, (start, scc)
     113      assert scc <= graph.keys(), scc - graph.keys()
     114  
     115      # Reduce the graph to nodes in the SCC.
     116      graph = {src: {dst for dst in dsts if dst in scc} for src, dsts in graph.items() if src in scc}
     117      assert start in graph
     118  
     119      # Recursive helper that yields cycles.
     120      def dfs(node: str, path: List[str]) -> Iterator[List[str]]:
     121          if node in path:
     122              yield path + [node]
     123              return
     124          path = path + [node]  # TODO: Make this not quadratic.
     125          for child in graph[node]:
     126              yield from dfs(child, path)
     127  
     128      yield from dfs(start, [])