Path: blob/main/Tools/peg_generator/pegen/sccutils.py
12 views
# Adapted from mypy (mypy/build.py) under the MIT license.12from typing import *345def strongly_connected_components(6vertices: AbstractSet[str], edges: Dict[str, AbstractSet[str]]7) -> Iterator[AbstractSet[str]]:8"""Compute Strongly Connected Components of a directed graph.910Args:11vertices: the labels for the vertices12edges: for each vertex, gives the target vertices of its outgoing edges1314Returns:15An iterator yielding strongly connected components, each16represented as a set of vertices. Each input vertex will occur17exactly once; vertices not part of a SCC are returned as18singleton sets.1920From http://code.activestate.com/recipes/578507/.21"""22identified: Set[str] = set()23stack: List[str] = []24index: Dict[str, int] = {}25boundaries: List[int] = []2627def dfs(v: str) -> Iterator[Set[str]]:28index[v] = len(stack)29stack.append(v)30boundaries.append(index[v])3132for w in edges[v]:33if w not in index:34yield from dfs(w)35elif w not in identified:36while index[w] < boundaries[-1]:37boundaries.pop()3839if boundaries[-1] == index[v]:40boundaries.pop()41scc = set(stack[index[v] :])42del stack[index[v] :]43identified.update(scc)44yield scc4546for v in vertices:47if v not in index:48yield from dfs(v)495051def topsort(52data: Dict[AbstractSet[str], Set[AbstractSet[str]]]53) -> Iterable[AbstractSet[AbstractSet[str]]]:54"""Topological sort.5556Args:57data: A map from SCCs (represented as frozen sets of strings) to58sets of SCCs, its dependencies. NOTE: This data structure59is modified in place -- for normalization purposes,60self-dependencies are removed and entries representing61orphans are added.6263Returns:64An iterator yielding sets of SCCs that have an equivalent65ordering. NOTE: The algorithm doesn't care about the internal66structure of SCCs.6768Example:69Suppose the input has the following structure:7071{A: {B, C}, B: {D}, C: {D}}7273This is normalized to:7475{A: {B, C}, B: {D}, C: {D}, D: {}}7677The algorithm will yield the following values:7879{D}80{B, C}81{A}8283From http://code.activestate.com/recipes/577413/.84"""85# TODO: Use a faster algorithm?86for k, v in data.items():87v.discard(k) # Ignore self dependencies.88for item in set.union(*data.values()) - set(data.keys()):89data[item] = set()90while True:91ready = {item for item, dep in data.items() if not dep}92if not ready:93break94yield ready95data = {item: (dep - ready) for item, dep in data.items() if item not in ready}96assert not data, "A cyclic dependency exists amongst %r" % data979899def find_cycles_in_scc(100graph: Dict[str, AbstractSet[str]], scc: AbstractSet[str], start: str101) -> Iterable[List[str]]:102"""Find cycles in SCC emanating from start.103104Yields lists of the form ['A', 'B', 'C', 'A'], which means there's105a path from A -> B -> C -> A. The first item is always the start106argument, but the last item may be another element, e.g. ['A',107'B', 'C', 'B'] means there's a path from A to B and there's a108cycle from B to C and back.109"""110# Basic input checks.111assert start in scc, (start, scc)112assert scc <= graph.keys(), scc - graph.keys()113114# Reduce the graph to nodes in the SCC.115graph = {src: {dst for dst in dsts if dst in scc} for src, dsts in graph.items() if src in scc}116assert start in graph117118# Recursive helper that yields cycles.119def dfs(node: str, path: List[str]) -> Iterator[List[str]]:120if node in path:121yield path + [node]122return123path = path + [node] # TODO: Make this not quadratic.124for child in graph[node]:125yield from dfs(child, path)126127yield from dfs(start, [])128129130