summaryrefslogtreecommitdiffstats
path: root/Tools/peg_generator/pegen/sccutils.py
diff options
context:
space:
mode:
authorPablo Galindo <Pablogsal@gmail.com>2020-04-22 22:29:27 (GMT)
committerGitHub <noreply@github.com>2020-04-22 22:29:27 (GMT)
commitc5fc15685202cda73f7c3f5c6f299b0945f58508 (patch)
tree7624ae45b95f2812e801c5879bdbdcb796f570a6 /Tools/peg_generator/pegen/sccutils.py
parenta81849b0315277bb3937271174aaaa5059c0b445 (diff)
downloadcpython-c5fc15685202cda73f7c3f5c6f299b0945f58508.zip
cpython-c5fc15685202cda73f7c3f5c6f299b0945f58508.tar.gz
cpython-c5fc15685202cda73f7c3f5c6f299b0945f58508.tar.bz2
bpo-40334: PEP 617 implementation: New PEG parser for CPython (GH-19503)
Co-authored-by: Guido van Rossum <guido@python.org> Co-authored-by: Lysandros Nikolaou <lisandrosnik@gmail.com>
Diffstat (limited to 'Tools/peg_generator/pegen/sccutils.py')
-rw-r--r--Tools/peg_generator/pegen/sccutils.py128
1 files changed, 128 insertions, 0 deletions
diff --git a/Tools/peg_generator/pegen/sccutils.py b/Tools/peg_generator/pegen/sccutils.py
new file mode 100644
index 0000000..1f0586b
--- /dev/null
+++ b/Tools/peg_generator/pegen/sccutils.py
@@ -0,0 +1,128 @@
+# Adapted from mypy (mypy/build.py) under the MIT license.
+
+from typing import *
+
+
+def strongly_connected_components(
+ vertices: AbstractSet[str], edges: Dict[str, AbstractSet[str]]
+) -> Iterator[AbstractSet[str]]:
+ """Compute Strongly Connected Components of a directed graph.
+
+ Args:
+ vertices: the labels for the vertices
+ edges: for each vertex, gives the target vertices of its outgoing edges
+
+ Returns:
+ An iterator yielding strongly connected components, each
+ represented as a set of vertices. Each input vertex will occur
+ exactly once; vertices not part of a SCC are returned as
+ singleton sets.
+
+ From http://code.activestate.com/recipes/578507/.
+ """
+ identified: Set[str] = set()
+ stack: List[str] = []
+ index: Dict[str, int] = {}
+ boundaries: List[int] = []
+
+ def dfs(v: str) -> Iterator[Set[str]]:
+ index[v] = len(stack)
+ stack.append(v)
+ boundaries.append(index[v])
+
+ for w in edges[v]:
+ if w not in index:
+ yield from dfs(w)
+ elif w not in identified:
+ while index[w] < boundaries[-1]:
+ boundaries.pop()
+
+ if boundaries[-1] == index[v]:
+ boundaries.pop()
+ scc = set(stack[index[v] :])
+ del stack[index[v] :]
+ identified.update(scc)
+ yield scc
+
+ for v in vertices:
+ if v not in index:
+ yield from dfs(v)
+
+
+def topsort(
+ data: Dict[AbstractSet[str], Set[AbstractSet[str]]]
+) -> Iterable[AbstractSet[AbstractSet[str]]]:
+ """Topological sort.
+
+ Args:
+ data: A map from SCCs (represented as frozen sets of strings) to
+ sets of SCCs, its dependencies. NOTE: This data structure
+ is modified in place -- for normalization purposes,
+ self-dependencies are removed and entries representing
+ orphans are added.
+
+ Returns:
+ An iterator yielding sets of SCCs that have an equivalent
+ ordering. NOTE: The algorithm doesn't care about the internal
+ structure of SCCs.
+
+ Example:
+ Suppose the input has the following structure:
+
+ {A: {B, C}, B: {D}, C: {D}}
+
+ This is normalized to:
+
+ {A: {B, C}, B: {D}, C: {D}, D: {}}
+
+ The algorithm will yield the following values:
+
+ {D}
+ {B, C}
+ {A}
+
+ From http://code.activestate.com/recipes/577413/.
+ """
+ # TODO: Use a faster algorithm?
+ for k, v in data.items():
+ v.discard(k) # Ignore self dependencies.
+ for item in set.union(*data.values()) - set(data.keys()):
+ data[item] = set()
+ while True:
+ ready = {item for item, dep in data.items() if not dep}
+ if not ready:
+ break
+ yield ready
+ data = {item: (dep - ready) for item, dep in data.items() if item not in ready}
+ assert not data, "A cyclic dependency exists amongst %r" % data
+
+
+def find_cycles_in_scc(
+ graph: Dict[str, AbstractSet[str]], scc: AbstractSet[str], start: str
+) -> Iterable[List[str]]:
+ """Find cycles in SCC emanating from start.
+
+ Yields lists of the form ['A', 'B', 'C', 'A'], which means there's
+ a path from A -> B -> C -> A. The first item is always the start
+ argument, but the last item may be another element, e.g. ['A',
+ 'B', 'C', 'B'] means there's a path from A to B and there's a
+ cycle from B to C and back.
+ """
+ # Basic input checks.
+ assert start in scc, (start, scc)
+ assert scc <= graph.keys(), scc - graph.keys()
+
+ # Reduce the graph to nodes in the SCC.
+ graph = {src: {dst for dst in dsts if dst in scc} for src, dsts in graph.items() if src in scc}
+ assert start in graph
+
+ # Recursive helper that yields cycles.
+ def dfs(node: str, path: List[str]) -> Iterator[List[str]]:
+ if node in path:
+ yield path + [node]
+ return
+ path = path + [node] # TODO: Make this not quadratic.
+ for child in graph[node]:
+ yield from dfs(child, path)
+
+ yield from dfs(start, [])