Files
Django_kube/Webcal-python/venv/lib/python3.11/site-packages/tatsu/diagrams.py
T

283 lines
7.1 KiB
Python

from __future__ import annotations
import itertools
import pygraphviz as pgv # pylint: disable=E0401
from .walkers import NodeWalker
__all__ = ['draw']
def draw(filename, grammar):
traverser = GraphvizWalker()
traverser.walk(grammar)
traverser.draw(filename)
class GraphvizWalker(NodeWalker):
def __init__(self):
super().__init__()
self.top_graph = pgv.AGraph(
directed=True, rankdir='LR', packMode='clust', splines='true',
)
self.stack = [self.top_graph]
self.node_count = 0
@property
def graph(self):
return self.stack[-1]
def draw(self, filename):
self.graph.layout(prog='dot')
# WARNING: neato generated graphics hang my GPU
# self.graph.layout(prog='neato')
self.graph.draw(filename)
def push_graph(self, name=None, **attr):
if name is None:
self.node_count += 1
name = 'g%d' % self.node_count
self.stack.append(self.graph.add_subgraph(name, **attr))
return self.graph
def pop_graph(self):
self.stack.pop()
def node(self, name, id=None, **attr):
if id is None:
self.node_count += 1
id = 'n%d' % self.node_count
else:
try:
return self.graph.get_node(id)
except KeyError:
pass
self.graph.add_node(id, **attr)
n = self.graph.get_node(id)
n.attr['label'] = name
# n.attr['shape'] = 'circle'
return n
def tnode(self, name, **attr):
return self.node(name, **attr)
def dot(self):
n = self.node('')
n.attr['shape'] = 'point'
n.attr['size'] = 0.0000000001
n.attr['label'] = ''
return n
def start_node(self):
return self.dot()
def ref_node(self, name):
n = self.node(name)
n.attr['shape'] = 'box'
return n
def rule_node(self, name, **attr):
n = self.node(name, **attr)
n.attr['shape'] = 'parallelogram'
return n
def end_node(self):
n = self.node('')
n.attr['shape'] = 'point'
n.attr['width'] = 0.1
return n
def edge(self, s, e, **attr):
self.graph.add_edge(s, e, **attr)
edge = self.graph.get_edge(s, e)
# edge.attr['arrowhead'] = 'normal'
edge.attr['arrowhead'] = 'none'
return edge
def redge(self, s, e):
edge = self.edge(s, e)
edge.attr['dir'] = 'back'
return edge
def zedge(self, s, e):
return self.edge(s, e, len=0.000001)
def nedge(self, s, e):
return self.edge(s, e, style='invisible', dir='none')
def path(self, p):
self.graph.add_path(p)
def subgraph(self, name, bunch):
self.top_graph.add_subgraph(name)
def concat(self, *args):
return list(itertools.chain(*args))
def _walk_decorator(self, d):
return self.walk(d.exp)
def walk_default(self, node):
raise NotImplementedError('No walking for ', type(node).__name__)
def walk__decorator(self, d):
return self.walk(d.exp)
def walk__grammar(self, g):
self.push_graph(g.name + '0')
try:
vrules = [self.walk(r) for r in reversed(g.rules)]
finally:
self.pop_graph()
self.push_graph(g.name + '1')
try:
pass
# link all rule starting nodes with invisible edges
# starts = [self.node(r.name, id=r.name) for r in g.rules]
# for n1, n2 in zip(starts, starts[1:]):
# self.nedge(n1, n2)
finally:
self.pop_graph()
s, t = vrules[0][0], vrules[-1][1]
return (s, t)
def walk__rule(self, r):
self.push_graph(r.name)
try:
i, e = self.walk(r.exp)
s = self.rule_node(r.name, id=r.name)
self.edge(s, i)
t = self.end_node()
self.edge(e, t)
return (s, t)
finally:
self.pop_graph()
def walk__based_rule(self, r):
return self.walk__rule(r)
def walk__rule_ref(self, rr):
n = self.ref_node(rr.name)
return (n, n)
def walk__special(self, s):
n = self.node(s.special)
return (n, n)
def walk__override(self, o):
return self._walk_decorator(o)
def walk__named(self, n):
return self._walk_decorator(n)
def walk__named_list(self, n):
return self._walk_decorator(n)
def walk__cut(self, c):
# c = self.node('>>')
# return (c, c)
return None
def walk__optional(self, o):
i, e = self._walk_decorator(o)
ni = self.dot()
ne = self.dot()
self.zedge(ni, i)
self.edge(ni, ne)
self.zedge(e, ne)
return (ni, ne)
def walk__closure(self, r):
self.push_graph(rankdir='TB')
try:
i, e = self._walk_decorator(r)
ni = self.dot()
self.edge(ni, i)
self.edge(e, ni)
return (ni, ni)
finally:
self.pop_graph()
def walk__positive_closure(self, r):
i, e = self._walk_decorator(r)
if i == e:
self.redge(e, i)
else:
self.edge(e, i)
return (i, e)
def walk__join(self, r):
i, e = self._walk_decorator(r)
n = self.tnode(r.sep)
self.edge(i, n)
self.edge(n, e)
return (i, e)
def walk__group(self, g):
return self._walk_decorator(g)
def walk__choice(self, c):
vopt = [self.walk(o) for o in c.options]
vopt = [o for o in vopt if o is not None]
ni = self.dot()
ne = self.dot()
for i, e in vopt:
self.edge(ni, i)
self.edge(e, ne)
return (ni, ne)
def walk__sequence(self, s):
vseq = [self.walk(x) for x in s.sequence]
vseq = [x for x in vseq if x is not None]
i, _ = vseq[0]
_, e = vseq[-1]
if i != e:
bunch = zip(
[a for _x, a in vseq[:-1]],
[b for b, _y in vseq[1:]],
strict=False,
)
for n, n1 in bunch:
self.edge(n, n1)
return (i, e)
def walk__lookahead(self, la):
_, e = self._walk_decorator(la)
n = self.node('&')
self.edge(n, e)
return (n, e)
def walk__negative_lookahead(self, la):
_, e = self._walk_decorator(la)
n = self.node('!')
self.edge(n, e)
return (n, e)
def walk__rule_include(self, la):
_, e = self._walk_decorator(la)
n = self.node('>')
self.edge(n, e)
return (n, e)
def walk__pattern(self, p):
n = self.tnode(p.pattern)
return (n, n)
def walk__token(self, t):
n = self.tnode(t.token)
return (n, n)
def walk__void(self, v):
n = self.dot()
return (n, n)
def walk__constant(self, t):
n = self.tnode(f'`{t.ast}`')
return (n, n)
def walk__eof(self, v):
# n = self.node('$')
# return (n, n)
return None