# ============================================================================= # ____ _ _ ____ _ _ # _ __ _ _ / ___| | | | _ \| | __| | ___ _ __ ___ # | '_ \| | | | | _| |_| | | | | | / _` |/ _ \| '_ ` _ \ # | |_) | |_| | |_| | _ | |_| | |___ | (_| | (_) | | | | | | # | .__/ \__, |\____|_| |_|____/|_____(_)__,_|\___/|_| |_| |_| # |_| |___/ # ============================================================================= # 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 . # # SPDX-License-Identifier: GPL-2.0-or-later # ============================================================================ from typing import List, Union from pyTooling.Decorators import export from pyGHDL.dom.Concurrent import ( ConcurrentBlockStatement, ProcessStatement, IfGenerateStatement, CaseGenerateStatement, ForGenerateStatement, ComponentInstantiation, ConfigurationInstantiation, EntityInstantiation, ConcurrentProcedureCall, ) from pyVHDLModel.SyntaxModel import ( GenericInterfaceItem, NamedEntity, PortInterfaceItem, WithDefaultExpressionMixin, Function, BaseType, FullType, BaseConstant, ConcurrentStatement, ) from pyGHDL import GHDLBaseException from pyGHDL.dom.NonStandard import Document, Design, Library 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] @export class PrettyPrintException(GHDLBaseException): pass @export class PrettyPrint: # _buffer: StringBuffer # # def __init__(self): # self._buffer = [] def formatDesign(self, design: Design, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level buffer.append(f"{prefix}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:") for document in design.Documents: buffer.append(f"{prefix} - Path: '{document.Path!s}':") 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}Entities:") for entity in library.Entities: buffer.append(f"{prefix} - {entity.Identifier}({', '.join([a.Identifier for a in entity.Architectures])})") buffer.append(f"{prefix}Packages:") for package in library.Packages: 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}Configurations:") for configuration in library.Configurations: buffer.append(f"{prefix} - {configuration.Identifier}") buffer.append(f"{prefix}Contexts:") for context in library.Contexts: buffer.append(f"{prefix} - {context.Identifier}") return buffer def formatDocument(self, document: Document, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level buffer.append(f"{prefix}Entities:") for entity in document.Entities: for line in self.formatEntity(entity, level + 1): buffer.append(line) buffer.append(f"{prefix}Architectures:") for architecture in document.Architectures: for line in self.formatArchitecture(architecture, level + 1): buffer.append(line) buffer.append(f"{prefix}Packages:") for package in document.Packages: 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:") for packageBodies in document.PackageBodies: for line in self.formatPackageBody(packageBodies, level + 1): buffer.append(line) buffer.append(f"{prefix}Configurations:") for configuration in document.Configurations: for line in self.formatConfiguration(configuration, level + 1): buffer.append(line) buffer.append(f"{prefix}Contexts:") for context in document.Contexts: for line in self.formatContext(context, level + 1): buffer.append(line) return buffer def formatEntity(self, entity: Entity, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level buffer.append(f"{prefix}- Name: {entity.Identifier}\n{prefix} File: {entity.Position.Filename.name}\n{prefix} Position: {entity.Position.Line}:{entity.Position.Column}") 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: buffer.append(f"{prefix} - {item.Identifier}") return buffer def formatArchitecture(self, architecture: Architecture, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level buffer.append(f"{prefix}- Name: {architecture.Identifier}\n{prefix} File: {architecture.Position.Filename.name}\n{prefix} Position: {architecture.Position.Line}:{architecture.Position.Column}") buffer.append(f"{prefix} Entity: {architecture.Entity.SymbolName}") 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 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 buffer.append(f"{prefix}- Name: {package.Identifier}\n{prefix} File: {package.Position.Filename.name}\n{prefix} Position: {package.Position.Line}:{package.Position.Column}") 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 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 buffer.append(f"{prefix}- Name: {packageBody.Identifier}") 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[NamedEntity, 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[NamedEntity, 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 buffer.append( "{prefix} - {name} : {mode!s} {subtypeindication}{initialValue}".format( prefix=prefix, name=", ".join(generic.Identifiers), mode=generic.Mode, subtypeindication=self.formatSubtypeIndication(generic.Subtype, "generic", generic.Identifiers[0]), initialValue=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 buffer.append( "{prefix} - {name} : {mode!s} {subtypeindication}{initialValue}".format( prefix=prefix, name=", ".join(port.Identifiers), mode=port.Mode, subtypeindication=self.formatSubtypeIndication(port.Subtype, "port", port.Identifiers[0]), initialValue=self.formatInitialValue(port), ) ) return buffer def formatDeclaredItems(self, item, level: int = 0) -> StringBuffer: buffer = [] prefix = " " * level if isinstance(item, BaseConstant): if isinstance(item, Constant): default = f" := {item.DefaultExpression!s}" else: default = "" buffer.append( "{prefix}- constant {name} : {subtype}{default}".format( prefix=prefix, name=", ".join(item.Identifiers), subtype=self.formatSubtypeIndication(item.Subtype, "constant", item.Identifiers[0]), default=default, ) ) elif isinstance(item, SharedVariable): buffer.append( "{prefix}- shared variable {name} : {subtype}".format( prefix=prefix, name=", ".join(item.Identifiers), subtype=self.formatSubtypeIndication(item.Subtype, "shared variable", item.Identifiers[0]), ) ) elif isinstance(item, Signal): buffer.append( "{prefix}- signal {name} : {subtype}{initValue}".format( prefix=prefix, name=", ".join(item.Identifiers), subtype=self.formatSubtypeIndication(item.Subtype, "signal", item.Identifiers[0]), initValue=f" := {item.DefaultExpression!s}" if item.DefaultExpression is not None else "", ) ) elif isinstance(item, File): buffer.append( "{prefix}- File {name} : {subtype}".format( prefix=prefix, name=", ".join(item.Identifiers), subtype=self.formatSubtypeIndication(item.Subtype, "file", item.Identifiers[0]), ) ) 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!s}") 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!s}") elif isinstance(item, AttributeSpecification): buffer.append(f"{prefix}- attribute {item.Attribute!s} of {'????'} : {'????'} is {'????'}") elif isinstance(item, UseClause): buffer.append("{prefix}- use {names}".format(prefix=prefix, names=", ".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!s} 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.SymbolName}" elif isinstance(subtypeIndication, ConstrainedCompositeSubtypeSymbol): constraints = [] for constraint in subtypeIndication.Constraints: constraints.append(str(constraint)) return "{type}({constraints})".format(type=subtypeIndication.SymbolName, constraints=", ".join(constraints)) else: raise PrettyPrintException(f"Unhandled subtype kind '{subtypeIndication.__class__.__name__}' for {entity} '{name}'.") def formatInitialValue(self, item: WithDefaultExpressionMixin) -> str: if item.DefaultExpression is None: return "" return f" := {item.DefaultExpression!s}" 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