projetAnsible/myenv/lib/python3.12/site-packages/arpeggio/export.py
2024-12-09 06:16:28 +01:00

222 lines
6.5 KiB
Python

# -*- coding: utf-8 -*-
#######################################################################
# Name: export.py
# Purpose: Export support for arpeggio
# Author: Igor R. Dejanović <igor DOT dejanovic AT gmail DOT com>
# Copyright: (c) 2009 Igor R. Dejanović <igor DOT dejanovic AT gmail DOT com>
# License: MIT License
#######################################################################
from __future__ import unicode_literals
import io
from arpeggio import Terminal
class Exporter(object):
"""
Base class for all Exporters.
"""
def __init__(self):
super(Exporter, self).__init__()
# Export initialization
self._render_set = set() # Used in rendering to prevent
# rendering
# of the same node multiple times
self._adapter_map = {} # Used as a registry of adapters to
# ensure that the same adapter is
# returned for the same adaptee object
def export(self, obj):
"""
Export of an obj to a string.
"""
self._outf = io.StringIO()
self._export(obj)
content = self._outf.getvalue()
self._outf.close()
return content
def exportFile(self, obj, file_name):
"""
Export of obj to a file.
"""
self._outf = io.open(file_name, "w", encoding="utf-8")
self._export(obj)
self._outf.close()
def _export(self, obj):
self._outf.write(self._start())
self._render_node(obj)
self._outf.write(self._end())
def _start(self):
"""
Override this to specify the beginning of the graph representation.
"""
return ""
def _end(self):
"""
Override this to specify the end of the graph representation.
"""
return ""
class ExportAdapter(object):
"""
Base adapter class for the export support.
Adapter should be defined for every export and graph type.
Attributes:
adaptee: A node to adapt.
export: An export object used as a context of the export.
"""
def __init__(self, node, export):
self.adaptee = node # adaptee is adapted graph node
self.export = export
# -------------------------------------------------------------------------
# Support for DOT language
class DOTExportAdapter(ExportAdapter):
"""
Base adapter class for the DOT export support.
"""
@property
def id(self):
"""
Graph node unique identification.
"""
raise NotImplementedError()
@property
def desc(self):
"""
Graph node textual description.
"""
raise NotImplementedError()
@property
def neighbours(self):
"""
A set of adjacent graph nodes.
"""
raise NotImplementedError()
class PMDOTExportAdapter(DOTExportAdapter):
"""
Adapter for ParsingExpression graph types (parser model).
"""
@property
def id(self):
return id(self.adaptee)
@property
def desc(self):
return self.adaptee.desc
@property
def neighbours(self):
if not hasattr(self, "_neighbours"):
self._neighbours= []
# Registry of adapters used in this export
adapter_map = self.export._adapter_map
for c, n in enumerate(self.adaptee.nodes):
if isinstance(n, PMDOTExportAdapter):
# if the neighbour node is already adapted use that adapter
self._neighbours.append((str(c + 1), n))
elif id(n) in adapter_map:
# current node is adaptee -> there is registered adapter
self._neighbours.append((str(c + 1), adapter_map[id(n)]))
else:
# Create new adapter
adapter = PMDOTExportAdapter(n, self.export)
self._neighbours.append((str(c + 1), adapter))
adapter_map[adapter.id] = adapter
return self._neighbours
class PTDOTExportAdapter(PMDOTExportAdapter):
"""
Adapter for ParseTreeNode graph types.
"""
@property
def neighbours(self):
if isinstance(self.adaptee, Terminal):
return []
else:
if not hasattr(self, "_neighbours"):
self._neighbours = []
for c, n in enumerate(self.adaptee):
adapter = PTDOTExportAdapter(n, self.export)
self._neighbours.append((str(c + 1), adapter))
return self._neighbours
class DOTExporter(Exporter):
"""
Export to DOT language (part of GraphViz, see http://www.graphviz.org/)
"""
def _render_node(self, node):
if not node in self._render_set:
self._render_set.add(node)
self._outf.write('\n%s [label="%s"];' %
(node.id, self._dot_label_esc(node.desc)))
#TODO Comment handling
# if hasattr(node, "comments") and root.comments:
# retval += self.node(root.comments)
# retval += '\n%s->%s [label="comment"]' % \
#(id(root), id(root.comments))
for name, n in node.neighbours:
self._outf.write('\n%s->%s [label="%s"]' %
(node.id, n.id, name))
self._outf.write('\n')
self._render_node(n)
def _start(self):
return "digraph arpeggio_graph {"
def _end(self):
return "\n}"
def _dot_label_esc(self, to_esc):
to_esc = to_esc.replace("\\", "\\\\")
to_esc = to_esc.replace('\"', '\\"')
to_esc = to_esc.replace('\n', '\\n')
return to_esc
class PMDOTExporter(DOTExporter):
"""
A convenience DOTExport extension that uses ParserExpressionDOTExportAdapter
"""
def export(self, obj):
return super(PMDOTExporter, self).\
export(PMDOTExportAdapter(obj, self))
def exportFile(self, obj, file_name):
return super(PMDOTExporter, self).\
exportFile(PMDOTExportAdapter(obj, self), file_name)
class PTDOTExporter(DOTExporter):
"""
A convenience DOTExport extension that uses PTDOTExportAdapter
"""
def export(self, obj):
return super(PTDOTExporter, self).\
export(PTDOTExportAdapter(obj, self))
def exportFile(self, obj, file_name):
return super(PTDOTExporter, self).\
exportFile(PTDOTExportAdapter(obj, self), file_name)