Allow pgen to produce a DOT format dump of the grammar (GH-18005)

Originally suggested by Anthony Shaw.
diff --git a/Parser/pgen/automata.py b/Parser/pgen/automata.py
index 545a737..d04ca7c 100644
--- a/Parser/pgen/automata.py
+++ b/Parser/pgen/automata.py
@@ -48,6 +48,26 @@
                 else:
                     writer("    %s -> %d" % (label, j))
 
+    def dump_graph(self, writer):
+        """Dump a DOT representation of the NFA"""
+        writer('digraph %s_nfa {\n' % self.name)
+        todo = [self.start]
+        for i, state in enumerate(todo):
+            writer(' %d [label="State %d %s"];\n' % (i, i, state is self.end and "(final)" or ""))
+            for arc in state.arcs:
+                label = arc.label
+                next = arc.target
+                if next in todo:
+                    j = todo.index(next)
+                else:
+                    j = len(todo)
+                    todo.append(next)
+                if label is None:
+                    writer(" %d -> %d [style=dotted label=ε];\n" % (i, j))
+                else:
+                    writer(" %d -> %d [label=%s];\n" % (i, j, label.replace("'", '"')))
+        writer('}\n')
+
 
 class NFAArc:
     """An arc representing a transition between two NFA states.
@@ -301,6 +321,15 @@
             for label, next in sorted(state.arcs.items()):
                 writer("    %s -> %d" % (label, self.states.index(next)))
 
+    def dump_graph(self, writer):
+        """Dump a DOT representation of the DFA"""
+        writer('digraph %s_dfa {\n' % self.name)
+        for i, state in enumerate(self.states):
+            writer(' %d [label="State %d %s"];\n' % (i, i, state.is_final and "(final)" or ""))
+            for label, next in sorted(state.arcs.items()):
+                writer(" %d -> %d [label=%s];\n" % (i, self.states.index(next), label.replace("'", '"')))
+        writer('}\n')
+
 
 class DFAState(object):
     """A state of a DFA