Source code for pyGHDL.dom.formatting.prettyprint

# =============================================================================
#               ____ _   _ ____  _          _
#  _ __  _   _ / ___| | | |  _ \| |      __| | ___  _ __ ___
# | '_ \| | | | |  _| |_| | | | | |     / _` |/ _ \| '_ ` _ \
# | |_) | |_| | |_| |  _  | |_| | |___ | (_| | (_) | | | | | |
# | .__/ \__, |\____|_| |_|____/|_____(_)__,_|\___/|_| |_| |_|
# |_|    |___/
# =============================================================================
# Authors:
#   Patrick Lehmann
#
# Package module:   A pretty printer to format the DOM as a tree in text form.
#
# License:
# ============================================================================
#  Copyright (C) 2019-2021 Tristan Gingold
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <gnu.org/licenses>.
#
# SPDX-License-Identifier: GPL-2.0-or-later
# ============================================================================
from typing import List, Union

from pyTooling.Decorators import export

from pyVHDLModel.Base import NamedEntityMixin
from pyVHDLModel.Interface import GenericInterfaceItem, PortInterfaceItem
from pyVHDLModel.Subprogram import Function
from pyVHDLModel.Object import BaseConstant, WithDefaultExpressionMixin
from pyVHDLModel.Type import BaseType, FullType
from pyVHDLModel.Concurrent import ConcurrentStatement

from pyGHDL import GHDLBaseException
from pyGHDL.dom.NonStandard import Document, Design, Library
from pyGHDL.dom.Concurrent import (
    ConcurrentBlockStatement,
    ProcessStatement,
    IfGenerateStatement,
    CaseGenerateStatement,
    ForGenerateStatement,
    ComponentInstantiation,
    ConfigurationInstantiation,
    EntityInstantiation,
    ConcurrentProcedureCall,
)
from pyGHDL.dom.DesignUnit import (
    Entity,
    Architecture,
    Package,
    PackageBody,
    Configuration,
    Context,
    Component,
    UseClause,
    PackageInstantiation,
)
from pyGHDL.dom.Symbol import SimpleSubtypeSymbol, ConstrainedCompositeSubtypeSymbol
from pyGHDL.dom.Type import (
    IntegerType,
    Subtype,
    ArrayType,
    RecordType,
    AccessType,
    EnumeratedType,
    FileType,
    ProtectedType,
    ProtectedTypeBody,
    PhysicalType,
    IncompleteType,
)
from pyGHDL.dom.InterfaceItem import (
    GenericConstantInterfaceItem,
    PortSignalInterfaceItem,
    GenericTypeInterfaceItem,
)
from pyGHDL.dom.Object import Constant, Signal, SharedVariable, File
from pyGHDL.dom.Attribute import Attribute, AttributeSpecification
from pyGHDL.dom.Subprogram import Procedure
from pyGHDL.dom.Misc import Alias
from pyGHDL.dom.PSL import DefaultClock


StringBuffer = List[str]


[docs]@export class PrettyPrintException(GHDLBaseException): pass
[docs]@export class PrettyPrint: # _buffer: StringBuffer # # def __init__(self): # self._buffer = [] def CleanupDocumentationBlocks(self, documentationContent: str, level: int = 0): prefix = " " * level if documentationContent is None: return prefix documentationLines = documentationContent.split("\n") return f"{prefix}{documentationLines[0][2:].lstrip()}" def formatDesign(self, design: Design, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level buffer.append(f"{prefix}Libraries ({len(design.Libraries)}):") for library in design.Libraries.values(): buffer.append(f"{prefix} - Name: {library.Identifier}") for line in self.formatLibrary(library, level + 2): buffer.append(line) buffer.append(f"{prefix}Documents ({len(design.Documents)}):") for document in design.Documents: buffer.append(f"{prefix} - Path: '{document.Path}':") for line in self.formatDocument(document, level + 2): buffer.append(line) return buffer def formatLibrary(self, library: Library, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level buffer.append(f"{prefix}Contexts ({len(library.Contexts)}):") for context in library.Contexts.values(): buffer.append(f"{prefix} - {context.Identifier}") buffer.append(f"{prefix}Packages ({len(library.Packages)}):") for package in library.Packages.values(): if isinstance(package, Package): buffer.append(f"{prefix} - {package.Identifier}") elif isinstance(package, PackageInstantiation): buffer.append(f"{prefix} - {package.Identifier} instantiate from {package.PackageReference}") buffer.append(f"{prefix}Entities ({len(library.Entities)}):") for entity in library.Entities.values(): buffer.append( f"{prefix} - {entity.Identifier}({', '.join([a.Identifier for a in entity.Architectures.values()])})" ) buffer.append(f"{prefix}Configurations ({len(library.Configurations)}):") for configuration in library.Configurations.values(): buffer.append(f"{prefix} - {configuration.Identifier}") return buffer def formatDocument(self, document: Document, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level buffer.append(f"{prefix}Contexts ({len(document.Contexts)}):") for context in document.Contexts.values(): for line in self.formatContext(context, level + 1): buffer.append(line) buffer.append(f"{prefix}Packages ({len(document.Packages)}):") for package in document.Packages.values(): if isinstance(package, Package): gen = self.formatPackage else: gen = self.formatPackageInstance for line in gen(package, level + 1): buffer.append(line) buffer.append(f"{prefix}PackageBodies ({len(document.PackageBodies)}):") for packageBodies in document.PackageBodies.values(): for line in self.formatPackageBody(packageBodies, level + 1): buffer.append(line) buffer.append(f"{prefix}Entities ({len(document.Entities)}):") for entity in document.Entities.values(): for line in self.formatEntity(entity, level + 1): buffer.append(line) buffer.append(f"{prefix}Architectures ({len(document.Architectures)}):") for architectures in document.Architectures.values(): for architecture in architectures.values(): for line in self.formatArchitecture(architecture, level + 1): buffer.append(line) buffer.append(f"{prefix}Configurations ({len(document.Configurations)}):") for configuration in document.Configurations.values(): for line in self.formatConfiguration(configuration, level + 1): buffer.append(line) return buffer def formatEntity(self, entity: Entity, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level documentationFirstLine = self.CleanupDocumentationBlocks(entity.Documentation) buffer.append( f"{prefix}- Name: {entity.Identifier}\n" f"{prefix} File: {entity.Position.Filename.name}\n" f"{prefix} Position: {entity.Position.Line}:{entity.Position.Column}\n" f"{prefix} Documentation: {documentationFirstLine}" ) buffer.append(f"{prefix} Generics:") for generic in entity.GenericItems: for line in self.formatGeneric(generic, level + 1): buffer.append(line) buffer.append(f"{prefix} Ports:") for port in entity.PortItems: for line in self.formatPort(port, level + 1): buffer.append(line) buffer.append(f"{prefix} Declared:") for item in entity.DeclaredItems: for line in self.formatDeclaredItems(item, level + 1): buffer.append(line) buffer.append(f"{prefix} Statements:") for item in entity.Statements: buffer.append(f"{prefix} ...") buffer.append(f"{prefix} Architectures:") for item in entity.Architectures.values(): buffer.append(f"{prefix} - {item.Identifier}") return buffer def formatArchitecture(self, architecture: Architecture, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level documentationFirstLine = self.CleanupDocumentationBlocks(architecture.Documentation) buffer.append( f"{prefix}- Name: {architecture.Identifier}\n" f"{prefix} File: {architecture.Position.Filename.name}\n" f"{prefix} Position: {architecture.Position.Line}:{architecture.Position.Column}\n" f"{prefix} Documentation: {documentationFirstLine}" ) buffer.append(f"{prefix} Entity: {architecture.Entity.Identifier}") buffer.append(f"{prefix} Declared:") for item in architecture.DeclaredItems: for line in self.formatDeclaredItems(item, level + 2): buffer.append(line) buffer.append(f"{prefix} Hierarchy:") for item in architecture.Statements: for line in self.formatHierarchy(item, level + 2): buffer.append(line) buffer.append(f"{prefix} Statements:") for item in architecture.Statements: buffer.append(f"{prefix} ...") # for line in self.formatStatements(item, level + 2): # buffer.append(line) return buffer def formatComponent(self, component: Component, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level documentationFirstLine = self.CleanupDocumentationBlocks(component.Documentation) buffer.append(f"{prefix}- Component: {component.Identifier}") buffer.append(f"{prefix} Generics:") for generic in component.GenericItems: for line in self.formatGeneric(generic, level + 1): buffer.append(line) buffer.append(f"{prefix} Ports:") for port in component.PortItems: for line in self.formatPort(port, level + 1): buffer.append(line) return buffer def formatPackage(self, package: Package, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level documentationFirstLine = self.CleanupDocumentationBlocks(package.Documentation) buffer.append( f"{prefix}- Name: {package.Identifier}\n" f"{prefix} File: {package.Position.Filename.name}\n" f"{prefix} Position: {package.Position.Line}:{package.Position.Column}\n" f"{prefix} Documentation: {documentationFirstLine}" ) buffer.append(f"{prefix} Declared:") for item in package.DeclaredItems: for line in self.formatDeclaredItems(item, level + 1): buffer.append(line) return buffer def formatPackageInstance(self, package: PackageInstantiation, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level documentationFirstLine = self.CleanupDocumentationBlocks(package.Documentation) buffer.append(f"{prefix}- Name: {package.Identifier}") buffer.append(f"{prefix} Package: {package.PackageReference!s}") buffer.append(f"{prefix} Generic Map: ...") # for item in package.GenericItems: # for line in self.formatGeneric(item, level + 1): # buffer.append(line) return buffer def formatPackageBody(self, packageBody: PackageBody, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level documentationFirstLine = self.CleanupDocumentationBlocks(packageBody.Documentation) buffer.append(f"{prefix}- Name: {packageBody.Identifier}\n{prefix} Documentation: {documentationFirstLine}") buffer.append(f"{prefix} Declared:") for item in packageBody.DeclaredItems: for line in self.formatDeclaredItems(item, level + 1): buffer.append(line) return buffer def formatConfiguration(self, configuration: Configuration, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level buffer.append(f"{prefix}- Name: {configuration.Identifier}") return buffer def formatContext(self, context: Context, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level buffer.append(f"{prefix}- Name: {context.Identifier}") return buffer def formatGeneric(self, generic: Union[NamedEntityMixin, GenericInterfaceItem], level: int = 0) -> StringBuffer: if isinstance(generic, GenericConstantInterfaceItem): return self.formatGenericConstant(generic, level) elif isinstance(generic, GenericTypeInterfaceItem): return self.formatGenericType(generic, level) else: raise PrettyPrintException( f"Unhandled generic kind '{generic.__class__.__name__}' for generic '{generic.Identifiers[0]}'." ) def formatPort(self, port: Union[NamedEntityMixin, PortInterfaceItem], level: int = 0) -> StringBuffer: if isinstance(port, PortSignalInterfaceItem): return self.formatPortSignal(port, level) else: raise PrettyPrintException( f"Unhandled port kind '{port.__class__.__name__}' for port '{port.Identifiers[0]}'." ) def formatGenericConstant(self, generic: GenericConstantInterfaceItem, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level subTypeIndication = self.formatSubtypeIndication(generic.Subtype, "generic", generic.Identifiers[0]) buffer.append( f"{prefix} - {', '.join(generic.Identifiers)} : {generic.Mode!s} {subTypeIndication}{self.formatInitialValue(generic)}" ) return buffer def formatGenericType(self, generic: GenericConstantInterfaceItem, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level buffer.append(f"{prefix} - type {generic.Identifier}") return buffer def formatPortSignal(self, port: PortSignalInterfaceItem, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level subTypeIndication = self.formatSubtypeIndication(port.Subtype, "port", port.Identifiers[0]) buffer.append( f"{prefix} - {', '.join(port.Identifiers)} : {port.Mode} {subTypeIndication}{self.formatInitialValue(port)}" ) return buffer def formatDeclaredItems(self, item, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level if isinstance(item, BaseConstant): subTypeIndication = self.formatSubtypeIndication(item.Subtype, "constant", item.Identifiers[0]) initValue = f" := {item.DefaultExpression}" if isinstance(item, Constant) else "" buffer.append(f"{prefix}- constant {', '.join(item.Identifiers)} : {subTypeIndication}{initValue}") elif isinstance(item, SharedVariable): subTypeIndication = self.formatSubtypeIndication(item.Subtype, "shared variable", item.Identifiers[0]) buffer.append(f"{prefix}- shared variable {', '.join(item.Identifiers)} : {subTypeIndication}") elif isinstance(item, Signal): subTypeIndication = self.formatSubtypeIndication(item.Subtype, "signal", item.Identifiers[0]) initValue = f" := {item.DefaultExpression}" if item.DefaultExpression is not None else "" buffer.append(f"{prefix}- signal {', '.join(item.Identifiers)} : {subTypeIndication}{initValue}") elif isinstance(item, File): subTypeIndication = self.formatSubtypeIndication(item.Subtype, "file", item.Identifiers[0]) buffer.append(f"{prefix}- File {', '.join(item.Identifiers)} : {subTypeIndication}") elif isinstance(item, (FullType, IncompleteType)): buffer.append(f"{prefix}- {self.formatType(item)}") elif isinstance(item, Subtype): buffer.append(f"{prefix}- subtype {item.Identifier} is ?????") elif isinstance(item, Alias): buffer.append(f"{prefix}- alias {item.Identifier} is ?????") elif isinstance(item, Function): buffer.append(f"{prefix}- function {item.Identifier} return {item.ReturnType}") elif isinstance(item, Procedure): buffer.append(f"{prefix}- procedure {item.Identifier}") elif isinstance(item, Component): for line in self.formatComponent(item, level): buffer.append(line) elif isinstance(item, Attribute): buffer.append(f"{prefix}- attribute {item.Identifier} : {item.Subtype}") elif isinstance(item, AttributeSpecification): buffer.append(f"{prefix}- attribute {item.Attribute} of {'????'} : {'????'} is {'????'}") elif isinstance(item, UseClause): buffer.append(f"{prefix}- use {', '.join([str(n) for n in item.Names])}") elif isinstance(item, Package): buffer.append(f"{prefix}- package {item.Identifier} is ..... end package") elif isinstance(item, PackageInstantiation): buffer.append(f"{prefix}- package {item.Identifier} is new {item.PackageReference} generic map (.....)") elif isinstance(item, DefaultClock): buffer.append(f"{prefix}- default {item.Identifier} is {'...'}") else: raise PrettyPrintException(f"Unhandled declared item kind '{item.__class__.__name__}'.") return buffer def formatType(self, item: BaseType) -> str: result = f"type {item.Identifier} is " if isinstance(item, IncompleteType): result += "" elif isinstance(item, IntegerType): result += f"range {item.Range!s}" elif isinstance(item, EnumeratedType): result += "(........)" elif isinstance(item, PhysicalType): result += " is range ....... units ..... end units" elif isinstance(item, ArrayType): result += "array(........) of ....." elif isinstance(item, RecordType): result += "record ..... end record" elif isinstance(item, AccessType): result += "access ....." elif isinstance(item, FileType): result += "file ....." elif isinstance(item, ProtectedType): result += "protected ..... end protected" elif isinstance(item, ProtectedTypeBody): result += "protected body ..... end protected body" else: raise PrettyPrintException(f"Unknown type '{item.__class__.__name__}'") return result def formatSubtypeIndication(self, subtypeIndication, entity: str, name: str) -> str: if isinstance(subtypeIndication, SimpleSubtypeSymbol): return f"{subtypeIndication.Identifier}" elif isinstance(subtypeIndication, ConstrainedCompositeSubtypeSymbol): constraints = [] # FIXME: disabled due to problems with symbols # for constraint in subtypeIndication.Constraints: # constraints.append(str(constraint)) return f"{subtypeIndication.Identifier}({', '.join(constraints)})" else: raise PrettyPrintException( f"Unhandled subtype kind '{subtypeIndication.__class__.__name__}' for {entity} '{name}'." ) def formatInitialValue(self, item: WithDefaultExpressionMixin) -> str: return f" := {item.DefaultExpression}" if item.DefaultExpression is not None else "" def formatHierarchy(self, statement: ConcurrentStatement, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level if isinstance(statement, ProcessStatement): buffer.append(f"{prefix}- {statement.Label}: process(...)") elif isinstance(statement, EntityInstantiation): buffer.append(f"{prefix}- {statement.Label}: entity {statement.Entity}") elif isinstance(statement, ComponentInstantiation): buffer.append(f"{prefix}- {statement.Label}: component {statement.Component}") elif isinstance(statement, ConfigurationInstantiation): buffer.append(f"{prefix}- {statement.Label}: configuration {statement.Configuration}") elif isinstance(statement, ConcurrentBlockStatement): buffer.append(f"{prefix}- {statement.Label}: block") for stmt in statement.Statements: for line in self.formatHierarchy(stmt, level + 2): buffer.append(line) elif isinstance(statement, IfGenerateStatement): buffer.append(f"{prefix}- {statement.Label}: if {statement.IfBranch.Condition} generate") for stmt in statement.IfBranch.Statements: for line in self.formatHierarchy(stmt, level + 2): buffer.append(line) for elsifBranch in statement.ElsifBranches: buffer.append(f"{prefix} {statement.Label}: elsif {elsifBranch.Condition} generate") for stmt in elsifBranch.Statements: for line in self.formatHierarchy(stmt, level + 2): buffer.append(line) if statement.ElseBranch is not None: buffer.append(f"{prefix} {statement.Label}: else generate") for stmt in statement.ElseBranch.Statements: for line in self.formatHierarchy(stmt, level + 2): buffer.append(line) elif isinstance(statement, CaseGenerateStatement): buffer.append(f"{prefix}- {statement.Label}: case {statement.SelectExpression} generate") for case in statement.Cases: buffer.append(f"{prefix} {case!s}") for stmt in case.Statements: for line in self.formatHierarchy(stmt, level + 2): buffer.append(line) elif isinstance(statement, ForGenerateStatement): buffer.append(f"{prefix}- {statement.Label}: for {statement.LoopIndex} in {statement.Range} generate") for stmt in statement.Statements: for line in self.formatHierarchy(stmt, level + 2): buffer.append(line) elif isinstance(statement, ConcurrentProcedureCall): buffer.append(f"{prefix}- {statement.Label}: {statement.Procedure!s}(...)") return buffer