├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.rst ├── doc ├── .nojekyll ├── Makefile ├── _static │ ├── Kreon-Bold.woff2 │ ├── Kreon-Light.woff2 │ ├── Kreon-Regular.woff2 │ └── project.css ├── _templates │ ├── download.html │ ├── layout.html │ ├── page.html │ └── projects.html ├── apidoc │ ├── hdlparse.rst │ └── modules.rst ├── conf.py ├── index.rst └── make.bat ├── hdlparse ├── __init__.py ├── minilexer.py ├── verilog_parser.py └── vhdl_parser.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | build/ 3 | *.egg-info 4 | 5 | .idea/ 6 | 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2017 Kevin Thibedeau 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | include ChangeLog.txt 3 | include LICENSE 4 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | 2 | ======== 3 | Hdlparse 4 | ======== 5 | 6 | Hdlparse is a simple package implementing a rudimentary parser for VHDL and Verilog. It is not capable of fully parsing the entire language. Rather, it is meant to extract enough key information from a source file to create generated documentation. 7 | 8 | This library is forked from `kevinpt `_ via `zhelnio `_. The aim of this fork is to provide some bug fixes and additional features to zhelnio's version of Hdlparse. A list of changes is included at the bottom of this README. 9 | 10 | For VHDL this library can extract component, subprogram, type, subtype, and constant declarations from a package. For Verilog it can extract module declarations (both 1995 and 2001 syntax). 11 | 12 | 13 | Requirements 14 | ------------ 15 | 16 | Hdlparse requires Python 3.x and no additional libraries. This version of Hdlparse is not compatible with Python2. 17 | 18 | 19 | Download 20 | -------- 21 | 22 | You can access the Hdlparse Git repository from `Github 23 | `_. You can install direct from PyPI with the "pip" 24 | command if you have it available. 25 | 26 | Installation 27 | ------------ 28 | 29 | Hdlparse is a Python library. You must have Python installed first to use it. Most modern Linux distributions and OS/X have it available by default. There are a number of options available for Windows. If you don't already have a favorite, I recommend getting one of the `"full-stack" Python distros `_ that are geared toward scientific computing such as Anaconda or Python(x,y). 30 | 31 | You need to have the Python setuptools installed first. If your OS has a package manager, it may be preferable to install setuptools through that tool. Otherwise you can use Pip: 32 | 33 | .. code-block:: sh 34 | 35 | > pip install setuptools 36 | 37 | If you don't have ``pip`` you may have the ``easy_install`` command available which can be used to install ``pip`` on your system: 38 | 39 | .. code-block:: sh 40 | 41 | > easy_install pip 42 | 43 | You can use ``pip`` to get the latest development code from Github: 44 | 45 | .. code-block:: sh 46 | 47 | > pip install --upgrade https://github.com/hdl/pyhdlparser/tarball/master 48 | 49 | If you manually downloaded a source package or created a clone with Git you can install with the following command run from the base Hdlparse directory: 50 | 51 | .. code-block:: sh 52 | 53 | > python setup.py install 54 | 55 | On Linux systems you may need to install with root privileges using the *sudo* command. 56 | 57 | After a successful install the Hdlparse library will be available. 58 | 59 | 60 | Documentation 61 | ------------- 62 | 63 | The full documentation is available online at the `main Hdlparse site 64 | `_. 65 | 66 | 67 | Changes 68 | ------- 69 | 70 | A list of changes compared to the upstream is listed here: 71 | 72 | * Improved support for VHDL and Verilog generics 73 | * PEP8 compatible formatting 74 | * Remove Python2 support 75 | * Provide debugging information using the Python logging module 76 | -------------------------------------------------------------------------------- /doc/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdl/pyHDLParser/e1153ace8ca1e25f9fb53350c41058ef8eb8dacf/doc/.nojekyll -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = HdlParse 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /doc/_static/Kreon-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdl/pyHDLParser/e1153ace8ca1e25f9fb53350c41058ef8eb8dacf/doc/_static/Kreon-Bold.woff2 -------------------------------------------------------------------------------- /doc/_static/Kreon-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdl/pyHDLParser/e1153ace8ca1e25f9fb53350c41058ef8eb8dacf/doc/_static/Kreon-Light.woff2 -------------------------------------------------------------------------------- /doc/_static/Kreon-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdl/pyHDLParser/e1153ace8ca1e25f9fb53350c41058ef8eb8dacf/doc/_static/Kreon-Regular.woff2 -------------------------------------------------------------------------------- /doc/_static/project.css: -------------------------------------------------------------------------------- 1 | 2 | @font-face { 3 | font-family: 'Kreon'; 4 | font-style: normal; 5 | font-weight: 300; 6 | src: local('Kreon Light'), local('Kreon-Light'), url('Kreon-Light.woff2') format('woff2'); 7 | } 8 | @font-face { 9 | font-family: 'Kreon'; 10 | font-style: normal; 11 | font-weight: 400; 12 | src: local('Kreon Regular'), local('Kreon-Regular'), url('Kreon-Regular.woff2') format('woff2'); 13 | } 14 | @font-face { 15 | font-family: 'Kreon'; 16 | font-style: normal; 17 | font-weight: 700; 18 | src: local('Kreon Bold'), local('Kreon-Bold'), url('Kreon-Bold.woff2') format('woff2'); 19 | } 20 | 21 | 22 | body { 23 | font-size: medium; 24 | background-color: white; 25 | color: #000; 26 | margin: 0; 27 | padding: 0 1.1em 0 0; 28 | } 29 | 30 | div.document { 31 | width: 100%; 32 | max-width: 1200px; 33 | margin: 0 auto 0 auto; 34 | padding: 0 0em 0 1em; 35 | } 36 | 37 | pre, code { 38 | font-size: small; 39 | } 40 | 41 | 42 | div.body h1, 43 | div.body h2, 44 | div.body h3, 45 | div.body h4 { 46 | font-family: "Kreon", "goudy old style", "minion pro", "bell mt", Georgia, "Hiragino Mincho Pro", serif; 47 | } 48 | 49 | div.body h1, 50 | div.body h2 { 51 | font-weight: bold; 52 | } 53 | 54 | div.sphinxsidebar p, 55 | div.sphinxsidebar span, 56 | div.sphinxsidebar h1, 57 | div.sphinxsidebar h2, 58 | div.sphinxsidebar h3, 59 | div.sphinxsidebar h4 { 60 | 61 | font-family: "Kreon", "goudy old style", "minion pro", "bell mt", Georgia, "Hiragino Mincho Pro", serif; 62 | } 63 | 64 | table.docutils { 65 | border-left: 1px solid #aaa; 66 | border-right: 1px solid #aaa; 67 | border-top: 2px solid #92a6c4; 68 | border-bottom: 4px solid #92a6c4; 69 | -moz-box-shadow: 2px 2px 4px #eee; 70 | -webkit-box-shadow: 2px 2px 4px #eee; 71 | box-shadow: 2px 2px 4px #eee; 72 | margin-bottom: 2em; 73 | } 74 | 75 | table.docutils td, table.docutils th { 76 | border-style: none; 77 | padding: 0.25em 0.7em; 78 | } 79 | 80 | table.docutils th { 81 | border-bottom: 1px solid #92a6c4; 82 | } 83 | 84 | 85 | tt.descname { 86 | font-size: 1.25em; 87 | } 88 | 89 | pre { 90 | background: #E9ECF5; 91 | 92 | -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); 93 | -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); 94 | box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); 95 | } 96 | 97 | dl.macro { 98 | margin-top: 5em; 99 | } 100 | 101 | 102 | div.dl-button a, div.dl-button a:hover { 103 | display: block; 104 | height: 36px; 105 | background: #008c00; 106 | 107 | color: white; 108 | font: 16px/36px Helvetica, Verdana, sans-serif; 109 | text-decoration: none; 110 | text-align: center; 111 | text-transform: uppercase; 112 | 113 | border: 4px solid white; 114 | margin: 2px; 115 | 116 | -webkit-border-radius: 10px; 117 | -moz-border-radius: 10px; 118 | border-radius: 10px; 119 | 120 | -webkit-box-shadow: 0 2px 7px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); 121 | -moz-box-shadow: 0 2px 7px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); 122 | box-shadow: 0 2px 7px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); 123 | } 124 | 125 | div.dl-button a:hover { 126 | 127 | /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#008c00+0,007200+100 */ 128 | background: #008c00; /* Old browsers */ 129 | background: -moz-linear-gradient(top, #008c00 0%, #007200 100%); /* FF3.6+ */ 130 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#008c00), color-stop(100%,#007200)); /* Chrome,Safari4+ */ 131 | background: -webkit-linear-gradient(top, #008c00 0%,#007200 100%); /* Chrome10+,Safari5.1+ */ 132 | background: -o-linear-gradient(top, #008c00 0%,#007200 100%); /* Opera 11.10+ */ 133 | background: -ms-linear-gradient(top, #008c00 0%,#007200 100%); /* IE10+ */ 134 | background: linear-gradient(to bottom, #008c00 0%,#007200 100%); /* W3C */ 135 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#008c00', endColorstr='#007200',GradientType=0 ); /* IE6-9 */ 136 | 137 | } 138 | 139 | p.caption { 140 | font-family: "Kreon", "goudy old style", "minion pro", "bell mt", Georgia, "Hiragino Mincho Pro", serif; 141 | font-size: medium; 142 | font-weight: bold; 143 | } 144 | -------------------------------------------------------------------------------- /doc/_templates/download.html: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /doc/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | 3 | {% block extrahead %} 4 | {{ super() }} 5 | 6 | 7 | 8 | 9 | 18 | 19 | {% endblock %} 20 | 21 | 22 | -------------------------------------------------------------------------------- /doc/_templates/page.html: -------------------------------------------------------------------------------- 1 | {% extends "!page.html" %} 2 | 3 | {% set css_files = css_files + ["_static/project.css", "_static/tty/tty-player.min.css"] %} 4 | 5 | 6 | -------------------------------------------------------------------------------- /doc/_templates/projects.html: -------------------------------------------------------------------------------- 1 |

Other projects

2 | 3 |
4 |

5 | {% if project|lower != "opbasm" %}Opbasm
{% endif %} 6 | {% if project|lower != "ripyl" %}Ripyl
{% endif %} 7 | {% if project|lower != "vertcl" %}Vertcl
{% endif %} 8 | {% if project|lower != "vhdl-extras" %}Vhdl-extras
{% endif %} 9 | {% if project|lower != "lecroy colorizer" %}Lecroy-colorizer{% endif %} 10 |

11 |
12 | 13 | 78 | -------------------------------------------------------------------------------- /doc/apidoc/hdlparse.rst: -------------------------------------------------------------------------------- 1 | hdlparse package 2 | ================ 3 | 4 | Submodules 5 | ---------- 6 | 7 | hdlparse\.minilexer module 8 | -------------------------- 9 | 10 | .. automodule:: hdlparse.minilexer 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | hdlparse\.verilog\_parser module 16 | -------------------------------- 17 | 18 | .. automodule:: hdlparse.verilog_parser 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | hdlparse\.vhdl\_parser module 24 | ----------------------------- 25 | 26 | .. automodule:: hdlparse.vhdl_parser 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | 32 | Module contents 33 | --------------- 34 | 35 | .. automodule:: hdlparse 36 | :members: 37 | :undoc-members: 38 | :show-inheritance: 39 | -------------------------------------------------------------------------------- /doc/apidoc/modules.rst: -------------------------------------------------------------------------------- 1 | hdlparse 2 | ======== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | hdlparse 8 | -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # HdlParse documentation build configuration file, created by 4 | # sphinx-quickstart on Sun Oct 15 14:19:42 2017. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | # If extensions (or modules to document with autodoc) are in another directory, 16 | # add these directories to sys.path here. If the directory is relative to the 17 | # documentation root, use os.path.abspath to make it absolute, like shown here. 18 | # 19 | import os 20 | import sys 21 | sys.path.insert(0, os.path.abspath('..')) 22 | 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | # 28 | # needs_sphinx = '1.0' 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = ['sphinx.ext.githubpages', 'sphinx.ext.autodoc', 'sphinx.ext.napoleon'] 34 | 35 | # Add any paths that contain templates here, relative to this directory. 36 | templates_path = ['_templates'] 37 | 38 | # The suffix(es) of source filenames. 39 | # You can specify multiple suffix as a list of string: 40 | # 41 | # source_suffix = ['.rst', '.md'] 42 | source_suffix = '.rst' 43 | 44 | # The master toctree document. 45 | master_doc = 'index' 46 | 47 | # General information about the project. 48 | project = u'Hdlparse' 49 | copyright = u'2017, Kevin Thibedeau' 50 | author = u'Kevin Thibedeau' 51 | 52 | # The version info for the project you're documenting, acts as replacement for 53 | # |version| and |release|, also used in various other places throughout the 54 | # built documents. 55 | # 56 | 57 | def get_package_version(verfile): 58 | '''Scan the script for the version string''' 59 | version = None 60 | with open(verfile) as fh: 61 | try: 62 | version = [line.split('=')[1].strip().strip("'") for line in fh if \ 63 | line.startswith('__version__')][0] 64 | except IndexError: 65 | pass 66 | return version 67 | 68 | # The short X.Y version. 69 | version = get_package_version('../hdlparse/minilexer.py') 70 | # The full version, including alpha/beta/rc tags. 71 | release = version 72 | 73 | # The language for content autogenerated by Sphinx. Refer to documentation 74 | # for a list of supported languages. 75 | # 76 | # This is also used if you do content translation via gettext catalogs. 77 | # Usually you set "language" from the command line for these cases. 78 | language = None 79 | 80 | # List of patterns, relative to source directory, that match files and 81 | # directories to ignore when looking for source files. 82 | # This patterns also effect to html_static_path and html_extra_path 83 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 84 | 85 | # The name of the Pygments (syntax highlighting) style to use. 86 | pygments_style = 'sphinx' 87 | 88 | # If true, `todo` and `todoList` produce output, else they produce nothing. 89 | todo_include_todos = False 90 | 91 | 92 | # -- Options for HTML output ---------------------------------------------- 93 | 94 | # The theme to use for HTML and HTML Help pages. See the documentation for 95 | # a list of builtin themes. 96 | # 97 | html_theme = 'alabaster' 98 | 99 | # Theme options are theme-specific and customize the look and feel of a theme 100 | # further. For a list of options available for each theme, see the 101 | # documentation. 102 | # 103 | # html_theme_options = {} 104 | html_theme_options = { 105 | 'description': 'HDL parsing library', 106 | 'show_powered_by': False, 107 | 'logo_text_align': 'center', 108 | 'font_family': 'Verdana, Geneva, sans-serif', 109 | 'github_user': 'kevinpt', 110 | 'github_repo': 'hdlparse', 111 | 'github_button': True 112 | } 113 | 114 | # Add any paths that contain custom static files (such as style sheets) here, 115 | # relative to this directory. They are copied after the builtin static files, 116 | # so a file named "default.css" will overwrite the builtin "default.css". 117 | html_static_path = ['_static'] 118 | 119 | # Custom sidebar templates, must be a dictionary that maps document names 120 | # to template names. 121 | # 122 | # This is required for the alabaster theme 123 | # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars 124 | html_sidebars = { 125 | '**': [ 126 | 'about.html', 127 | 'relations.html', # needs 'show_related': True theme option to display 128 | 'localtoc.html', 129 | 'projects.html', 130 | 'searchbox.html' 131 | ], 132 | 133 | 'index': [ 134 | 'about.html', 135 | 'download.html', 136 | 'relations.html', 137 | 'localtoc.html', 138 | 'projects.html', 139 | 'searchbox.html' 140 | ] 141 | } 142 | 143 | 144 | # -- Options for HTMLHelp output ------------------------------------------ 145 | 146 | # Output file base name for HTML help builder. 147 | htmlhelp_basename = 'HdlParsedoc' 148 | 149 | 150 | # -- Options for LaTeX output --------------------------------------------- 151 | 152 | latex_elements = { 153 | # The paper size ('letterpaper' or 'a4paper'). 154 | # 155 | # 'papersize': 'letterpaper', 156 | 157 | # The font size ('10pt', '11pt' or '12pt'). 158 | # 159 | # 'pointsize': '10pt', 160 | 161 | # Additional stuff for the LaTeX preamble. 162 | # 163 | # 'preamble': '', 164 | 165 | # Latex figure (float) alignment 166 | # 167 | # 'figure_align': 'htbp', 168 | } 169 | 170 | # Grouping the document tree into LaTeX files. List of tuples 171 | # (source start file, target name, title, 172 | # author, documentclass [howto, manual, or own class]). 173 | latex_documents = [ 174 | (master_doc, 'HdlParse.tex', u'HdlParse Documentation', 175 | u'Kevin Thibedeau', 'manual'), 176 | ] 177 | 178 | 179 | # -- Options for manual page output --------------------------------------- 180 | 181 | # One entry per manual page. List of tuples 182 | # (source start file, name, description, authors, manual section). 183 | man_pages = [ 184 | (master_doc, 'hdlparse', u'HdlParse Documentation', 185 | [author], 1) 186 | ] 187 | 188 | 189 | # -- Options for Texinfo output ------------------------------------------- 190 | 191 | # Grouping the document tree into Texinfo files. List of tuples 192 | # (source start file, target name, title, author, 193 | # dir menu entry, description, category) 194 | texinfo_documents = [ 195 | (master_doc, 'HdlParse', u'HdlParse Documentation', 196 | author, 'HdlParse', 'One line description of project.', 197 | 'Miscellaneous'), 198 | ] 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | .. HdlParse documentation master file, created by 2 | sphinx-quickstart on Sun Oct 15 14:19:42 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | ======== 7 | Hdlparse 8 | ======== 9 | 10 | Hdlparse is a simple package implementing a rudimentary parser for VHDL and Verilog. It is not capable of fully parsing the entire language. Rather, it is meant to extract enough key information from a source file to create generated documentation. 11 | 12 | This library is used by the `Symbolator `_ diagram generator. 13 | 14 | For VHDL this library can extract component, subprogram, type, subtype, and constant declarations from a package. For Verilog it can extract module declarations (both 1995 and 2001 syntax). 15 | 16 | 17 | Requirements 18 | ------------ 19 | 20 | Hdlparse requires either Python 2.7 or Python 3.x and no additional libraries. 21 | 22 | The installation script depends on setuptools. The source is written in 23 | Python 2.7 syntax but will convert cleanly to Python 3 when the installer 24 | passes it through ``2to3``. 25 | 26 | 27 | Licensing 28 | --------- 29 | 30 | Opbasm and the included VHDL source is licensed for free commercial and non-commercial use under the terms of the MIT license. 31 | 32 | 33 | Download 34 | -------- 35 | 36 | You can access the Hdlparse Git repository from `Github 37 | `_. You can install direct from PyPI with the ``pip`` 38 | command if you have it available. 39 | 40 | Installation 41 | ------------ 42 | 43 | Hdlparse is a Python library. You must have Python installed first to use it. Most modern Linux distributions and OS/X have it available by default. There are a number of options available for Windows. If you don't already have a favorite, I recommend getting one of the `"full-stack" Python distros `_ that are geared toward scientific computing such as Anaconda or Python(x,y). 44 | 45 | You need to have the Python setuptools installed first. If your OS has a package manager, it may be preferable to install setuptools through that tool. Otherwise you can use Pip: 46 | 47 | .. code-block:: sh 48 | 49 | > pip install setuptools 50 | 51 | The easiest way to install Hdlparse is from `PyPI `_. 52 | 53 | .. code-block:: sh 54 | 55 | > pip install --upgrade hdlparse 56 | 57 | This will download and install the latest release, upgrading if you already have it installed. If you don't have ``pip`` you may have the ``easy_install`` command available which can be used to install ``pip`` on your system: 58 | 59 | .. code-block:: sh 60 | 61 | > easy_install pip 62 | 63 | 64 | You can also use ``pip`` to get the latest development code from Github: 65 | 66 | .. code-block:: sh 67 | 68 | > pip install --upgrade https://github.com/kevinpt/hdlparse/tarball/master 69 | 70 | If you manually downloaded a source package or created a clone with Git you can install with the following command run from the base Hdlparse directory: 71 | 72 | .. code-block:: sh 73 | 74 | > python setup.py install 75 | 76 | On Linux systems you may need to install with root privileges using the *sudo* command. 77 | 78 | After a successful install the Hdlparse library will be available. 79 | 80 | 81 | Using Hdlparse 82 | -------------- 83 | 84 | The Hdlparse library has two main modules :py:mod:`~hdlparse.vhdl_parser` and :py:mod:`~hdlparse.verilog_parser`. You import one or both of them as needed. 85 | 86 | .. code-block:: python 87 | 88 | import hdlparse.vhdl_parser as vhdl 89 | import hdlparse.verilog_parser as vlog 90 | 91 | Within each module are extractor classes :py:class:`~hdlparse.vhdl_parser.VhdlExtractor` and :py:class:`~hdlparse.verilog_parser.VerilogExtractor` that are the central mechanisms for using Hdlparse. 92 | 93 | .. code-block:: python 94 | 95 | vhdl_ex = vhdl.VhdlExtractor() 96 | vlog_ex = vlog.VerilogExtractor() 97 | 98 | 99 | VHDL 100 | ~~~~ 101 | 102 | The VHDL parser can extract a variety of different objects from sourec code. It can be used to access package definitions and component declarations,type and subtype definitions, functions, and procedures found within a package. It will not process entity declarations or nested subprograms and types. 103 | 104 | Extraction proceeds as follows: 105 | 106 | .. code-block:: python 107 | 108 | with io.open(fname, 'rt', encoding='latin-1') as fh: 109 | code = fh.read() 110 | vhdl_objs = vhdl_ex.extract_objects_from_source(code) 111 | 112 | vhdl_objs = vhdl_ex.extract_objects(fname) 113 | 114 | These will extract a list of all supported object types. The result is a list of objects subclassed from :py:class:`~hdlparse.vhdl_parser.VhdlObject`. You can pass an optional subclass of ``VhdlObject`` to filter the results for just that type. Repeated calls are more efficient when using :py:meth:`~hdlparse.vhdl_parser.VhdlExtractor.extract_objects` which maintains a cache of all previously parsed objects in the extractor. 115 | 116 | .. code-block:: vhdl 117 | 118 | package example is 119 | component demo is 120 | generic ( 121 | GENERIC1: boolean := false; 122 | GENERIC2: integer := 100 123 | ); 124 | port ( 125 | a, b : in std_ulogic := '1'; 126 | c, d : out std_ulogic_vector(7 downto 0); 127 | e, f : inout unsigned(7 downto 0) 128 | ); 129 | end component; 130 | end package; 131 | 132 | Each port and generic is an instance of :py:class:`~hdlparse.vhdl_parser.VhdlParameter` containing the name, mode (input, output, inout), and type. 133 | 134 | .. code-block:: python 135 | 136 | import hdlparse.vhdl_parser as vhdl 137 | from hdlparse.vhdl_parser import VhdlComponent 138 | 139 | vhdl_ex = vhdl.VhdlExtractor() 140 | vhdl_comps = vhdl_ex.extract_objects('example.vhdl', VhdlComponent) 141 | 142 | for c in vhdl_comps: 143 | print('Component "{}":'.format(c.name)) 144 | 145 | print(' Generics:') 146 | for p in c.generics: 147 | print('\t{:20}{:8} {}'.format(p.name, p.mode, p.data_type)) 148 | 149 | print(' Ports:') 150 | for p in c.ports: 151 | print('\t{:20}{:8} {}'.format(p.name, p.mode, p.data_type )) 152 | 153 | When run against the example code produces the following: 154 | 155 | .. code-block: console 156 | 157 | Component "demo": 158 | Generics: 159 | GENERIC1 in boolean 160 | GENERIC2 in integer 161 | Ports: 162 | a in std_ulogic 163 | b in std_ulogic 164 | c out std_ulogic_vector(7 downto 0) 165 | d out std_ulogic_vector(7 downto 0) 166 | e inout unsigned(7 downto 0) 167 | f inout unsigned(7 downto 0) 168 | 169 | 170 | VHDL arrays 171 | ~~~~~~~~~~~ 172 | 173 | It can be useful to know which data types are an array. The :py:class:`~hdlparse.vhdl_parser.VhdlExtractor` class will keep track of all visited array type definitions it sees. The :py:meth:`~hdlparse.vhdl_parser.VhdlExtractor.is_array` method lets you query the internal list to check if a type is for an array. All IEEE standard array types are supported by default. Any subtypes derived from an array type will also be considered as arrays. 174 | 175 | .. code-block:: python 176 | 177 | import hdlparse.vhdl_parser as vhdl 178 | 179 | vhdl_ex = vhdl.VhdlExtractor() 180 | 181 | code = ''' 182 | package foobar is 183 | type custom_array is array(integer range <>) of boolean; 184 | subtype custom_subtype is custom_array(1 to 10); 185 | end package; 186 | ''' 187 | vhdl_comps = vhdl_ex.extract_objects(code) 188 | 189 | # These all return true: 190 | print(vhdl_ex.is_array('unsigned')) 191 | print(vhdl_ex.is_array('custom_array')) 192 | print(vhdl_ex.is_array('custom_subtype')) 193 | 194 | Parsed array data can be saved to a file with :py:meth:`~hdlparse.vhdl_parser.VhdlExtractor.save_array_types` and restored with :py:meth:`~hdlparse.vhdl_parser.VhdlExtractor.load_array_types`. This lets you parse one set of files for type definitions and use the saved info for parsing other code at a different time. 195 | 196 | 197 | Verilog 198 | ~~~~~~~ 199 | 200 | The Verilog parser is only able to extract module definitions with a port and optional parameter list. Verilog modules are extracted using the :py:meth:`~hdlparse.verilog_parser.VerilogExtractor.extract_objects` and :py:meth:`~hdlparse.verilog_parser.VerilogExtractor.extract_objects_from_source` methods. The latter is used when you have the code in a string. The former when you want to read the Veirlog source from a file. When parsing a file, a cache of objects is maintained so you can repeatedly call :py:meth:`~hdlparse.verilog_parser.VerilogExtractor.extract_objects` without reparsing the file. 201 | 202 | .. code-block:: python 203 | 204 | with open(fname, 'rt') as fh: 205 | code = fh.read() 206 | vlog_mods = vlog_ex.extract_objects_from_source(code) 207 | 208 | vlog_mods = vlog_ex.extract_objects(fname) 209 | 210 | The result is a list of extracted :py:class:`~hdlparse.verilog_parser.VerilogModule` objects. Each instance of this class has ``name``, ``generics``, and ``ports`` attributes. The ``name`` attribute is the name of the module. The ``generics`` attribute is a list of extracted parameters and ``ports`` is a list of the ports on the module. 211 | 212 | .. code-block:: verilog 213 | 214 | module newstyle // This is a new style module def 215 | #(parameter real foo = 8, bar=1, baz=2, 216 | parameter signed [7:0] zip = 100) 217 | ( 218 | input x, x2, inout y, y2_long_output, 219 | output wire [4:1] z, z2 220 | ); 221 | endmodule 222 | 223 | Each port and generic is an instance of :py:class:`~hdlparse.verilog_parser.VerilogParameter` containing the name, mode (input, output, inout), and type. 224 | 225 | .. code-block:: python 226 | 227 | import hdlparse.verilog_parser as vlog 228 | 229 | vlog_ex = vlog.VerilogExtractor() 230 | vlog_mods = vlog_ex.extract_objects_from_source('example.v') 231 | 232 | for m in vlog_mods: 233 | print('Module "{}":'.format(m.name)) 234 | 235 | print(' Parameters:') 236 | for p in m.generics: 237 | print('\t{:20}{:8}{}'.format(p.name, p.mode, p.data_type)) 238 | 239 | print(' Ports:') 240 | for p in m.ports: 241 | print('\t{:20}{:8}{}'.format(p.name, p.mode, p.data_type)) 242 | 243 | 244 | When run against the example code produces the following: 245 | 246 | .. code-block:: console 247 | 248 | Module "newstyle": 249 | Parameters: 250 | foo in real 251 | bar in real 252 | baz in real 253 | zip in signed [7:0] 254 | Ports: 255 | x input 256 | x2 input 257 | y inout 258 | y2_long_output inout 259 | z output wire [4:1] 260 | z2 output wire [4:1] 261 | 262 | 263 | .. toctree:: 264 | :hidden: 265 | :maxdepth: 2 266 | 267 | apidoc/modules 268 | 269 | 270 | Indices and tables 271 | ------------------ 272 | 273 | * :ref:`genindex` 274 | * :ref:`search` 275 | -------------------------------------------------------------------------------- /doc/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=python -msphinx 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=HdlParse 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The Sphinx module was not found. Make sure you have Sphinx installed, 20 | echo.then set the SPHINXBUILD environment variable to point to the full 21 | echo.path of the 'sphinx-build' executable. Alternatively you may add the 22 | echo.Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /hdlparse/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdl/pyHDLParser/e1153ace8ca1e25f9fb53350c41058ef8eb8dacf/hdlparse/__init__.py -------------------------------------------------------------------------------- /hdlparse/minilexer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright © 2017 Kevin Thibedeau 3 | # Distributed under the terms of the MIT license 4 | import re 5 | import logging 6 | 7 | """Minimalistic lexer engine inspired by the PyPigments RegexLexer""" 8 | 9 | __version__ = '1.0.7' 10 | 11 | log = logging.getLogger(__name__) 12 | handler = logging.StreamHandler() 13 | handler.setFormatter(logging.Formatter('%(name)s - %(levelname)s - %(message)s')) 14 | log.addHandler(handler) 15 | 16 | 17 | class MiniLexer(object): 18 | """Simple lexer state machine with regex matching rules""" 19 | 20 | def __init__(self, tokens, flags=re.MULTILINE): 21 | """Create a new lexer 22 | 23 | Args: 24 | tokens (dict(match rules)): Hierarchical dict of states with a list of regex patterns and transitions 25 | flags (int): Optional regex flags 26 | """ 27 | self.tokens = {} 28 | 29 | # Pre-process the state definitions 30 | for state, patterns in tokens.items(): 31 | full_patterns = [] 32 | for p in patterns: 33 | pat = re.compile(p[0], flags) 34 | action = p[1] 35 | new_state = p[2] if len(p) >= 3 else None 36 | 37 | # Convert pops into an integer 38 | if new_state and new_state.startswith('#pop'): 39 | try: 40 | new_state = -int(new_state.split(':')[1]) 41 | except ValueError: 42 | new_state = -1 43 | except IndexError: 44 | new_state = -1 45 | 46 | full_patterns.append((pat, action, new_state)) 47 | self.tokens[state] = full_patterns 48 | 49 | def run(self, text): 50 | """Run lexer rules against a source text 51 | 52 | Args: 53 | text (str): Text to apply lexer to 54 | 55 | Yields: 56 | A sequence of lexer matches. 57 | """ 58 | 59 | stack = ['root'] 60 | pos = 0 61 | 62 | patterns = self.tokens[stack[-1]] 63 | 64 | while True: 65 | for pat, action, new_state in patterns: 66 | m = pat.match(text, pos) 67 | if m: 68 | if action: 69 | log.debug(f"Match: {m.group().strip()} -> {action}") 70 | 71 | yield (pos, m.end() - 1), action, m.groups() 72 | 73 | pos = m.end() 74 | 75 | if new_state: 76 | if isinstance(new_state, int): # Pop states 77 | del stack[new_state:] 78 | else: 79 | stack.append(new_state) 80 | 81 | patterns = self.tokens[stack[-1]] 82 | 83 | break 84 | 85 | else: 86 | try: 87 | if text[pos] == '\n': 88 | pos += 1 89 | continue 90 | pos += 1 91 | except IndexError: 92 | break 93 | -------------------------------------------------------------------------------- /hdlparse/verilog_parser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright © 2017 Kevin Thibedeau 3 | # Distributed under the terms of the MIT license 4 | import io 5 | import os 6 | from collections import OrderedDict 7 | 8 | from hdlparse.minilexer import MiniLexer 9 | 10 | """Verilog documentation parser""" 11 | 12 | verilog_tokens = { 13 | # state 14 | 'root': [ 15 | # patterns 16 | # pattern, action, new_state 17 | (r'\bmodule\s+(\w+)\s*', 'module', 'module'), 18 | (r'/\*', 'block_comment', 'block_comment'), 19 | (r'//#+(.*)\n', 'metacomment'), 20 | (r'//.*\n', None), 21 | ], 22 | 'module': [ 23 | (r'parameter\s*(signed|integer|realtime|real|time)?\s*(\[[^]]+\])?', 'parameter_start', 'parameters'), 24 | ( 25 | r'^[\(\s]*(input|inout|output)\s+(reg|supply0|supply1|tri|triand|trior|tri0|tri1|wire|wand|wor)?' 26 | r'\s*(signed)?\s*((\[[^]]+\])+)?', 27 | 'module_port_start', 'module_port'), 28 | (r'endmodule', 'end_module', '#pop'), 29 | (r'/\*', 'block_comment', 'block_comment'), 30 | (r'//#\s*{{(.*)}}\n', 'section_meta'), 31 | (r'//.*\n', None), 32 | ], 33 | 'parameters': [ 34 | (r'\s*parameter\s*(signed|integer|realtime|real|time)?\s*(\[[^]]+\])?', 'parameter_start'), 35 | (r'\s*(\w+)\s*=\s*((?:(?!\/\/|[,)]).)*)', 'param_item'), 36 | (r'//#+(.*)\n', 'metacomment'), 37 | (r',', None), 38 | (r'//.*\n', None), 39 | (r'[);]', None, '#pop'), 40 | ], 41 | 'module_port': [ 42 | ( 43 | r'\s*(input|inout|output)\s+(reg|supply0|supply1|tri|triand|trior|tri0|tri1|wire|wand|wor)?' 44 | r'\s*(signed)?\s*((\[[^]]+\])+)?', 45 | 'module_port_start'), 46 | (r'\s*(\w+)\s*,?', 'port_param'), 47 | (r'/\*', 'block_comment', 'block_comment'), 48 | (r'[);]', None, '#pop'), 49 | (r'//#\s*{{(.*)}}\n', 'section_meta'), 50 | (r'//#+(.*)\n', 'metacomment'), 51 | (r'//.*\n', None), 52 | ], 53 | 54 | 'block_comment': [ 55 | (r'\*/', 'end_comment', '#pop'), 56 | ], 57 | } 58 | 59 | VerilogLexer = MiniLexer(verilog_tokens) 60 | 61 | 62 | class VerilogObject: 63 | """Base class for parsed Verilog objects""" 64 | 65 | def __init__(self, name, desc=None): 66 | self.name = name 67 | self.kind = 'unknown' 68 | self.desc = [] if desc is None else desc 69 | 70 | 71 | class VerilogParameter: 72 | """Parameter and port to a module""" 73 | 74 | def __init__(self, name, mode=None, data_type=None, default_value=None, desc=None): 75 | self.name = name 76 | self.mode = mode 77 | self.data_type = data_type 78 | self.default_value = default_value 79 | self.desc = [] if desc is None else desc 80 | 81 | def __str__(self): 82 | if self.mode is not None: 83 | param = f"{self.name} : {self.mode} {self.data_type}" 84 | else: 85 | param = f"{self.name} : {self.data_type}" 86 | if self.default_value is not None: 87 | param = f"{param} := {self.default_value}" 88 | return param 89 | 90 | def __repr__(self): 91 | return f"VerilogParameter('{self.name}')" 92 | 93 | 94 | class VerilogModule(VerilogObject): 95 | """Module definition""" 96 | 97 | def __init__(self, name, ports, generics=None, sections=None, desc=None): 98 | VerilogObject.__init__(self, name, desc) 99 | self.kind = 'module' 100 | # Verilog params 101 | self.generics = generics if generics is not None else [] 102 | self.ports = ports 103 | self.sections = sections if sections is not None else {} 104 | 105 | def __repr__(self): 106 | return f"VerilogModule('{self.name}') {self.ports}" 107 | 108 | 109 | def parse_verilog_file(fname): 110 | """Parse a named Verilog file 111 | 112 | Args: 113 | fname (str): File to parse. 114 | Returns: 115 | List of parsed objects. 116 | """ 117 | with open(fname, 'rt') as fh: 118 | text = fh.read() 119 | return parse_verilog(text) 120 | 121 | 122 | def parse_verilog(text): 123 | """Parse a text buffer of Verilog code 124 | 125 | Args: 126 | text (str): Source code to parse 127 | Returns: 128 | List of parsed objects. 129 | """ 130 | lex = VerilogLexer 131 | 132 | name = None 133 | kind = None 134 | saved_type = None 135 | mode = 'input' 136 | port_type = 'wire' 137 | param_type = '' 138 | 139 | metacomments = [] 140 | parameters = [] 141 | 142 | generics = [] 143 | ports = OrderedDict() 144 | sections = [] 145 | port_param_index = 0 146 | last_item = None 147 | array_range_start_pos = 0 148 | 149 | objects = [] 150 | 151 | for pos, action, groups in lex.run(text): 152 | if action == 'metacomment': 153 | comment = groups[0].strip() 154 | if last_item is None: 155 | metacomments.append(comment) 156 | else: 157 | last_item.desc.append(comment) 158 | 159 | if action == 'section_meta': 160 | sections.append((port_param_index, groups[0])) 161 | 162 | elif action == 'module': 163 | kind = 'module' 164 | name = groups[0] 165 | generics = [] 166 | ports = OrderedDict() 167 | sections = [] 168 | port_param_index = 0 169 | 170 | elif action == 'parameter_start': 171 | net_type, vec_range = groups 172 | 173 | new_param_type = '' 174 | if net_type is not None: 175 | new_param_type += net_type 176 | 177 | if vec_range is not None: 178 | new_param_type += ' ' + vec_range 179 | 180 | param_type = new_param_type 181 | 182 | elif action == 'param_item': 183 | param_name, default_value = groups 184 | param = VerilogParameter(param_name, 'in', param_type, default_value) 185 | generics.append(param) 186 | last_item = param 187 | 188 | elif action == 'module_port_start': 189 | new_mode, net_type, signed, vec_range = groups[0:4] 190 | 191 | new_port_type = '' 192 | if net_type is not None: 193 | new_port_type += net_type 194 | 195 | if signed is not None: 196 | new_port_type += ' ' + signed 197 | 198 | if vec_range is not None: 199 | new_port_type += ' ' + vec_range 200 | 201 | # Start with new mode 202 | mode = new_mode 203 | port_type = new_port_type 204 | 205 | elif action == 'port_param': 206 | port_ident = groups[0] 207 | port_obj = VerilogParameter(port_ident, mode, port_type) 208 | ports[port_ident] = port_obj 209 | port_param_index += 1 210 | last_item = port_obj 211 | 212 | elif action == 'end_module': 213 | vobj = VerilogModule(name, ports.values(), generics, dict(sections), metacomments) 214 | objects.append(vobj) 215 | last_item = None 216 | metacomments = [] 217 | 218 | return objects 219 | 220 | 221 | def is_verilog(fname): 222 | """Identify file as Verilog by its extension 223 | 224 | Args: 225 | fname (str): File name to check 226 | Returns: 227 | True when file has a Verilog extension. 228 | """ 229 | return os.path.splitext(fname)[1].lower() in ('.vlog', '.v') 230 | 231 | 232 | class VerilogExtractor: 233 | """Utility class that caches parsed objects""" 234 | 235 | def __init__(self): 236 | self.object_cache = {} 237 | 238 | def extract_objects(self, fname, type_filter=None): 239 | """Extract objects from a source file 240 | 241 | Args: 242 | fname(str): Name of file to read from 243 | type_filter (class, optional): Object class to filter results 244 | Returns: 245 | List of objects extracted from the file. 246 | """ 247 | objects = [] 248 | if fname in self.object_cache: 249 | objects = self.object_cache[fname] 250 | else: 251 | with io.open(fname, 'rt', encoding='utf-8') as fh: 252 | text = fh.read() 253 | objects = parse_verilog(text) 254 | self.object_cache[fname] = objects 255 | 256 | if type_filter: 257 | objects = [o for o in objects if isinstance(o, type_filter)] 258 | 259 | return objects 260 | 261 | def extract_objects_from_source(self, text, type_filter=None): 262 | """Extract object declarations from a text buffer 263 | 264 | Args: 265 | text (str): Source code to parse 266 | type_filter (class, optional): Object class to filter results 267 | Returns: 268 | List of parsed objects. 269 | """ 270 | objects = parse_verilog(text) 271 | 272 | if type_filter: 273 | objects = [o for o in objects if isinstance(o, type_filter)] 274 | 275 | return objects 276 | 277 | def is_array(self, data_type): 278 | """Check if a type is an array type 279 | 280 | Args: 281 | data_type (str): Data type 282 | Returns: 283 | True when a data type is an array. 284 | """ 285 | return '[' in data_type 286 | -------------------------------------------------------------------------------- /hdlparse/vhdl_parser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright © 2017 Kevin Thibedeau 3 | # Distributed under the terms of the MIT license 4 | import ast 5 | import io 6 | import os 7 | import re 8 | from pprint import pprint 9 | from hdlparse.minilexer import MiniLexer 10 | 11 | """VHDL documentation parser""" 12 | 13 | vhdl_tokens = { 14 | 'root': [ 15 | (r'package\s+(\w+)\s+is', 'package', 'package'), 16 | (r'package\s+body\s+(\w+)\s+is', 'package_body', 'package_body'), 17 | (r'function\s+(\w+|"[^"]+")\s*\(', 'function', 'param_list'), 18 | (r'procedure\s+(\w+)\s*\(', 'procedure', 'param_list'), 19 | (r'function\s+(\w+)', 'function', 'simple_func'), 20 | (r'component\s+(\w+)\s*is', 'component', 'component'), 21 | (r'entity\s+(\w+)\s*is', 'entity', 'entity'), 22 | (r'architecture\s+(\w+)\s*of', 'architecture', 'architecture'), 23 | (r'subtype\s+(\w+)\s+is\s+(\w+)', 'subtype'), 24 | (r'type\s+(\w+)\s*is', 'type', 'type_decl'), 25 | (r'/\*', 'block_comment', 'block_comment'), 26 | (r'--.*\n', None), 27 | ], 28 | 'package': [ 29 | (r'function\s+(\w+|"[^"]+")\s*\(', 'function', 'param_list'), 30 | (r'procedure\s+(\w+)\s*\(', 'procedure', 'param_list'), 31 | (r'function\s+(\w+)', 'function', 'simple_func'), 32 | (r'component\s+(\w+)\s*is', 'component', 'component'), 33 | (r'subtype\s+(\w+)\s+is\s+(\w+)', 'subtype'), 34 | (r'constant\s+(\w+)\s+:\s+(\w+)', 'constant'), 35 | (r'type\s+(\w+)\s*is', 'type', 'type_decl'), 36 | (r'end\s+\w+\s*;', None, '#pop'), 37 | (r'--#(.*)\n', 'metacomment'), 38 | (r'/\*', 'block_comment', 'block_comment'), 39 | (r'--.*\n', None), 40 | ], 41 | 'package_body': [ 42 | (r'end\s+\w+\s*;', None, '#pop'), 43 | (r'--#(.*)\n', 'metacomment'), 44 | (r'/\*', 'block_comment', 'block_comment'), 45 | (r'--.*\n', None), 46 | ], 47 | 'type_decl': [ 48 | (r'array', 'array_type', '#pop'), 49 | (r'file', 'file_type', '#pop'), 50 | (r'access', 'access_type', '#pop'), 51 | (r'record', 'record_type', '#pop'), 52 | (r'range', 'range_type', '#pop'), 53 | (r'\(', 'enum_type', '#pop'), 54 | (r';', 'incomplete_type', '#pop'), 55 | (r'/\*', 'block_comment', 'block_comment'), 56 | (r'--.*\n', None), 57 | ], 58 | 'param_list': [ 59 | (r'\s*((?:variable|signal|constant|file)\s+)?(\w+)\s*', 'param'), 60 | (r'\s*,\s*', None), 61 | (r'\s*:\s*', None, 'param_type'), 62 | (r'/\*', 'block_comment', 'block_comment'), 63 | (r'--.*\n', None), 64 | ], 65 | 'param_type': [ 66 | (r'\s*((?:in|out|inout|buffer)\s+)?(\w+)\s*', 'param_type'), 67 | (r'\s*;\s*', None, '#pop'), 68 | (r"\s*:=\s*('.'|[^\s;)]+)", 'param_default'), 69 | (r'\)\s*(?:return\s+(\w+)\s*)?;', 'end_subprogram', '#pop:2'), 70 | (r'\)\s*(?:return\s+(\w+)\s*)?is', None, '#pop:2'), 71 | (r'/\*', 'block_comment', 'block_comment'), 72 | (r'--.*\n', None), 73 | ], 74 | 'simple_func': [ 75 | (r'\s+return\s+(\w+)\s*;', 'end_subprogram', '#pop'), 76 | (r'\s+return\s+(\w+)\s+is', None, '#pop'), 77 | (r'/\*', 'block_comment', 'block_comment'), 78 | (r'--.*\n', None), 79 | ], 80 | 'component': [ 81 | (r'generic\s*\(', None, 'generic_list'), 82 | (r'port\s*\(', None, 'port_list'), 83 | (r'end\s+component\s*\w*;', 'end_component', '#pop'), 84 | (r'/\*', 'block_comment', 'block_comment'), 85 | (r'--.*\n', None), 86 | ], 87 | 'entity': [ 88 | (r'generic\s*\(', None, 'generic_list'), 89 | (r'port\s*\(', None, 'port_list'), 90 | (r'end\s+\w+\s*;', 'end_entity', '#pop'), 91 | (r'/\*', 'block_comment', 'block_comment'), 92 | (r'--.*\n', None), 93 | ], 94 | 'architecture': [ 95 | (r'end\s+\w+\s*;', 'end_arch', '#pop'), 96 | (r'/\*', 'block_comment', 'block_comment'), 97 | (r'type\s+(\w+)\s*is', 'type', 'type_decl'), 98 | (r'--.*\n', None), 99 | ], 100 | 'generic_list': [ 101 | (r'\s*(\w+)\s*', 'generic_param'), 102 | (r'\s*,\s*', None), 103 | (r'\s*:\s*', None, 'generic_param_type'), 104 | (r'--#(.*)\n', 'metacomment'), 105 | (r'/\*', 'block_comment', 'block_comment'), 106 | (r'--.*\n', None), 107 | ], 108 | 'generic_param_type': [ 109 | (r'\s*(\w+)[ \t\r\f\v]*', 'generic_param_type'), 110 | (r'\s*;\s*', None, '#pop'), 111 | (r"\s*:=\s*([\w']+)", 'generic_param_default'), 112 | (r'\)\s*;\s*--(.*)\n', 'line_comment', '#pop:2'), 113 | (r'\n\s*\)\s*;\s*--(.*)\n', 'generic_list_comment', '#pop:2'), 114 | (r'\n\s*', None), 115 | (r'\)\s*;', 'end_generic', '#pop:2'), 116 | (r'--#(.*)\n', 'metacomment'), 117 | (r'/\*', 'block_comment', 'block_comment'), 118 | (r'--.*\n', None), 119 | ], 120 | 'port_list': [ 121 | (r'\s*(\w+)\s*', 'port_param'), 122 | (r'\s*,\s*', None), 123 | (r'\s*:\s*', None, 'port_param_type'), 124 | (r'--#\s*{{(.*)}}\n', 'section_meta'), 125 | (r'--#(.*)\n', 'metacomment'), 126 | (r'/\*', 'block_comment', 'block_comment'), 127 | (r'--(.*)\n', 'line_comment'), 128 | ], 129 | 'port_param_type': [ 130 | (r'\s*(in|out|inout|buffer)\s+(\w+)\s*\(', 'port_array_param_type', 'array_range'), 131 | (r'\s*(in|out|inout|buffer)\s+(\w+)[ \t\r\f\v]*', 'port_param_type'), 132 | (r'\s*;\s*', None, '#pop'), 133 | (r"\s*:=\s*([\w']+)", 'port_param_default'), 134 | (r'--(.*)\n', 'line_comment'), 135 | (r'\)\s*;\s*--(.*)\n', 'line_comment', '#pop:2'), 136 | (r'\n\s*\)\s*;\s*--(.*)\n', 'port_list_comment', '#pop:2'), 137 | (r'\n\s*', None), 138 | (r'\)\s*;', 'end_port', '#pop:2'), 139 | 140 | (r'--#(.*)\n', 'metacomment'), 141 | (r'/\*', 'block_comment', 'block_comment'), 142 | ], 143 | 'array_range': [ 144 | (r'\(', 'open_paren', 'nested_parens'), 145 | (r'\s*([\w\+\-\*/\s]+)(\s+(?:down)?to)\s+([\w\+\-\*/\s]+)', 'array_range_val'), 146 | (r'\)', 'array_range_end', '#pop'), 147 | ], 148 | 'nested_parens': [ 149 | (r'\(', 'open_paren', 'nested_parens'), 150 | (r'\s*([\w\+\-\*/\s]+)(\s+(?:down)?to)\s+([\w\+\-\*/\s]+)', 'array_range_val'), 151 | (r'\)', 'close_paren', '#pop'), 152 | ], 153 | 'block_comment': [ 154 | (r'\*/', 'end_comment', '#pop'), 155 | ], 156 | } 157 | 158 | VhdlLexer = MiniLexer(vhdl_tokens, flags=re.MULTILINE | re.IGNORECASE) 159 | 160 | 161 | class VhdlObject: 162 | """Base class for parsed VHDL objects 163 | 164 | Args: 165 | name (str): Name of the object 166 | desc (str): Description from object metacomments 167 | """ 168 | 169 | def __init__(self, name, desc=None): 170 | self.name = name 171 | self.kind = 'unknown' 172 | self.desc = desc 173 | 174 | 175 | class VhdlParameter: 176 | """Parameter to subprograms, ports, and generics 177 | 178 | Args: 179 | name (str): Name of the object 180 | mode (str): Direction mode for the parameter 181 | data_type (str): Type name for the parameter 182 | default_value (str): Default value of the parameter 183 | desc (str): Description from object metacomments 184 | param_desc (str): Description of the parameter 185 | """ 186 | 187 | def __init__(self, name, mode=None, data_type=None, default_value=None, desc=None, param_desc=None): 188 | self.name = name 189 | self.mode = mode 190 | self.data_type = data_type 191 | self.default_value = default_value 192 | self.desc = desc 193 | self.param_desc = None 194 | 195 | def __str__(self): 196 | if self.mode is not None: 197 | param = f"{self.name} : {self.mode} {self.data_type.name + self.data_type.arange}" 198 | else: 199 | param = f"{self.name} : {self.data_type.name + self.data_type.arange}" 200 | 201 | if self.default_value is not None: 202 | param = f"{param} := {self.default_value}" 203 | 204 | if self.param_desc is not None: 205 | param = f"{param} --{self.param_desc}" 206 | 207 | return param 208 | 209 | def __repr__(self): 210 | return f"VhdlParameter('{self.name}', '{self.mode}', '{self.data_type.name + self.data_type.arange}')" 211 | 212 | 213 | class VhdlParameterType: 214 | """Parameter type definition 215 | 216 | Args: 217 | name (str): Name of the type 218 | direction(str): "to" or "downto" 219 | r_bound (str): A simple expression based on digits or variable names 220 | l_bound (str): A simple expression based on digits or variable names 221 | arange (str): Original array range string 222 | """ 223 | 224 | def __init__(self, name, direction="", r_bound="", l_bound="", arange=""): 225 | self.name = name 226 | self.direction = direction.strip() 227 | self.r_bound = r_bound.strip() 228 | self.l_bound = l_bound.strip() 229 | self.arange = arange 230 | 231 | def __repr__(self): 232 | return f"VhdlParameterType('{self.name}','{self.arange}')" 233 | 234 | 235 | class VhdlPackage(VhdlObject): 236 | """Package declaration 237 | 238 | Args: 239 | name (str): Name of the package 240 | desc (str): Description from object metacomments 241 | """ 242 | 243 | def __init__(self, name, desc=None): 244 | VhdlObject.__init__(self, name, desc) 245 | self.kind = 'package' 246 | 247 | 248 | class VhdlType(VhdlObject): 249 | """Type definition 250 | 251 | Args: 252 | name (str): Name of the type 253 | package (str): Package containing the type 254 | type_of (str): Object type of this type definition 255 | desc (str, optional): Description from object metacomments 256 | """ 257 | 258 | def __init__(self, name, package, type_of, desc=None): 259 | VhdlObject.__init__(self, name, desc) 260 | self.kind = 'type' 261 | self.package = package 262 | self.type_of = type_of 263 | 264 | def __repr__(self): 265 | return f"VhdlType('{self.name}', '{self.type_of}')" 266 | 267 | 268 | class VhdlSubtype(VhdlObject): 269 | """Subtype definition 270 | 271 | Args: 272 | name (str): Name of the subtype 273 | package (str): Package containing the subtype 274 | base_type (str): Base type name derived from 275 | desc (str, optional): Description from object metacomments 276 | """ 277 | 278 | def __init__(self, name, package, base_type, desc=None): 279 | VhdlObject.__init__(self, name, desc) 280 | self.kind = 'subtype' 281 | self.package = package 282 | self.base_type = base_type 283 | 284 | def __repr__(self): 285 | return f"VhdlSubtype('{self.name}', '{self.base_type}')" 286 | 287 | 288 | class VhdlConstant(VhdlObject): 289 | """Constant definition 290 | 291 | Args: 292 | name (str): Name of the constant 293 | package (str): Package containing the constant 294 | base_type (str): Type fo the constant 295 | desc (str, optional): Description from object metacomments 296 | """ 297 | 298 | def __init__(self, name, package, base_type, desc=None): 299 | VhdlObject.__init__(self, name, desc) 300 | self.kind = 'constant' 301 | self.package = package 302 | self.base_type = base_type 303 | 304 | def __repr__(self): 305 | return f"VhdlConstant('{self.name}', '{self.base_type}')" 306 | 307 | 308 | class VhdlFunction(VhdlObject): 309 | """Function declaration 310 | 311 | Args: 312 | name (str): Name of the function 313 | package (str): Package containing the function 314 | parameters (list of VhdlParameter): Parameters to the function 315 | return_type (str, optional): Type of the return value 316 | desc (str, optional): Description from object metacomments 317 | """ 318 | 319 | def __init__(self, name, package, parameters, return_type=None, desc=None): 320 | VhdlObject.__init__(self, name, desc) 321 | self.kind = 'function' 322 | self.package = package 323 | self.parameters = parameters 324 | self.return_type = return_type 325 | 326 | def __repr__(self): 327 | return f"VhdlFunction('{self.name}')" 328 | 329 | 330 | class VhdlProcedure(VhdlObject): 331 | """Procedure declaration 332 | 333 | Args: 334 | name (str): Name of the procedure 335 | package (str): Package containing the procedure 336 | parameters (list of VhdlParameter): Parameters to the procedure 337 | desc (str, optional): Description from object metacomments 338 | """ 339 | 340 | def __init__(self, name, package, parameters, desc=None): 341 | VhdlObject.__init__(self, name, desc) 342 | self.kind = 'procedure' 343 | self.package = package 344 | self.parameters = parameters 345 | 346 | def __repr__(self): 347 | return f"VhdlProcedure('{self.name}')" 348 | 349 | 350 | class VhdlEntity(VhdlObject): 351 | """Entity declaration 352 | Args: 353 | name (str): Name of the entity 354 | ports (list of VhdlParameter): Port parameters to the entity 355 | generics (list of VhdlParameter): Generic parameters to the entity 356 | sections (list of str): Metacomment sections 357 | desc (str, optional): Description from object metacomments 358 | """ 359 | 360 | def __init__(self, name, ports, generics=None, sections=None, desc=None): 361 | VhdlObject.__init__(self, name, desc) 362 | self.kind = 'entity' 363 | self.generics = generics if generics is not None else [] 364 | self.ports = ports 365 | self.sections = sections if sections is not None else {} 366 | 367 | def __repr__(self): 368 | return f"VhdlEntity('{self.name}')" 369 | 370 | def dump(self): 371 | print(f"VHDL entity: {self.name}") 372 | for p in self.ports: 373 | print(f"\t{p.name} ({type(p.name)}), {p.data_type} ({type(p.data_type)})") 374 | 375 | 376 | class VhdlComponent(VhdlObject): 377 | """Component declaration 378 | 379 | Args: 380 | name (str): Name of the component 381 | package (str): Package containing the component 382 | ports (list of VhdlParameter): Port parameters to the component 383 | generics (list of VhdlParameter): Generic parameters to the component 384 | sections (list of str): Metacomment sections 385 | desc (str, optional): Description from object metacomments 386 | """ 387 | 388 | def __init__(self, name, package, ports, generics=None, sections=None, desc=None): 389 | VhdlObject.__init__(self, name, desc) 390 | self.kind = 'component' 391 | self.package = package 392 | self.generics = generics if generics is not None else [] 393 | self.ports = ports 394 | self.sections = sections if sections is not None else {} 395 | 396 | def __repr__(self): 397 | return f"VhdlComponent('{self.name}')" 398 | 399 | def dump(self): 400 | print(f"VHDL component: {self.name}") 401 | for p in self.ports: 402 | print(f"\t{p.name} ({type(p.name)}), {p.data_type} ({type(p.data_type)})") 403 | 404 | 405 | def parse_vhdl_file(fname): 406 | """Parse a named VHDL file 407 | 408 | Args: 409 | fname(str): Name of file to parse 410 | Returns: 411 | Parsed objects. 412 | """ 413 | with open(fname, 'rt') as fh: 414 | text = fh.read() 415 | return parse_vhdl(text) 416 | 417 | 418 | def parse_vhdl(text): 419 | """Parse a text buffer of VHDL code 420 | 421 | Args: 422 | text(str): Source code to parse 423 | Returns: 424 | Parsed objects. 425 | """ 426 | lex = VhdlLexer 427 | 428 | name = None 429 | kind = None 430 | saved_type = None 431 | end_param_group = False 432 | cur_package = None 433 | 434 | metacomments = [] 435 | parameters = [] 436 | param_items = [] 437 | 438 | generics = [] 439 | ports = [] 440 | sections = [] 441 | port_param_index = 0 442 | last_items = [] 443 | array_range_start_pos = 0 444 | 445 | objects = [] 446 | 447 | for pos, action, groups in lex.run(text): 448 | if action == 'metacomment': 449 | realigned = re.sub(r'^#+', lambda m: ' ' * len(m.group(0)), groups[0]) 450 | if not last_items: 451 | metacomments.append(realigned) 452 | else: 453 | for i in last_items: 454 | i.desc = realigned 455 | if action == 'section_meta': 456 | sections.append((port_param_index, groups[0])) 457 | 458 | elif action == 'function': 459 | kind = 'function' 460 | name = groups[0] 461 | param_items = [] 462 | parameters = [] 463 | elif action == 'procedure': 464 | kind = 'procedure' 465 | name = groups[0] 466 | param_items = [] 467 | parameters = [] 468 | elif action == 'param': 469 | if end_param_group: 470 | # Complete previous parameters 471 | for i in param_items: 472 | parameters.append(i) 473 | param_items = [] 474 | end_param_group = False 475 | 476 | param_items.append(VhdlParameter(groups[1])) 477 | elif action == 'param_type': 478 | mode, ptype = groups 479 | 480 | if mode is not None: 481 | mode = mode.strip() 482 | 483 | for i in param_items: # Set mode and type for all pending parameters 484 | i.mode = mode 485 | i.data_type = ptype 486 | 487 | end_param_group = True 488 | 489 | elif action == 'param_default': 490 | for i in param_items: 491 | i.default_value = groups[0] 492 | 493 | elif action == 'end_subprogram': 494 | # Complete last parameters 495 | for i in param_items: 496 | parameters.append(i) 497 | 498 | if kind == 'function': 499 | vobj = VhdlFunction(name, cur_package, parameters, groups[0], metacomments) 500 | else: 501 | vobj = VhdlProcedure(name, cur_package, parameters, metacomments) 502 | 503 | objects.append(vobj) 504 | 505 | metacomments = [] 506 | parameters = [] 507 | param_items = [] 508 | kind = None 509 | name = None 510 | 511 | elif action == 'entity': 512 | kind = 'entity' 513 | name = groups[0] 514 | generics = [] 515 | ports = [] 516 | param_items = [] 517 | sections = [] 518 | port_param_index = 0 519 | 520 | elif action == 'component': 521 | kind = 'component' 522 | name = groups[0] 523 | generics = [] 524 | ports = [] 525 | param_items = [] 526 | sections = [] 527 | port_param_index = 0 528 | 529 | elif action == 'generic_param': 530 | param_items.append(groups[0]) 531 | 532 | elif action == 'generic_param_type': 533 | ptype = groups[0] 534 | last_items = [] 535 | for i in param_items: 536 | p = VhdlParameter(i, 'in', VhdlParameterType(ptype)) 537 | generics.append(p) 538 | last_items.append(p) 539 | 540 | param_items = [] 541 | 542 | elif action == 'generic_param_default': 543 | for i in last_items: 544 | i.default_value = groups[0] 545 | 546 | elif action == 'port_param': 547 | param_items.append(groups[0]) 548 | port_param_index += 1 549 | 550 | elif action == 'port_param_type': 551 | mode, ptype = groups 552 | 553 | last_items = [] 554 | for i in param_items: 555 | p = VhdlParameter(i, mode, VhdlParameterType(ptype)) 556 | ports.append(p) 557 | last_items.append(p) 558 | 559 | param_items = [] 560 | 561 | elif action == 'port_param_default': 562 | for i in last_items: 563 | i.default_value = groups[0] 564 | 565 | elif action == 'port_array_param_type': 566 | mode, ptype = groups 567 | array_range_start_pos = pos[1] 568 | 569 | elif action == 'array_range_val': 570 | l_bound, direction, r_bound = groups 571 | 572 | elif action == 'array_range_end': 573 | arange = text[array_range_start_pos:pos[0] + 1] 574 | 575 | last_items = [] 576 | for i in param_items: 577 | p = VhdlParameter(i, mode, VhdlParameterType(ptype, direction, r_bound, l_bound, arange)) 578 | ports.append(p) 579 | last_items.append(p) 580 | 581 | param_items = [] 582 | 583 | elif action == 'end_entity': 584 | vobj = VhdlEntity(name, ports, generics, dict(sections), metacomments) 585 | objects.append(vobj) 586 | last_items = [] 587 | metacomments = [] 588 | 589 | elif action == 'end_component': 590 | vobj = VhdlComponent(name, cur_package, ports, generics, dict(sections), metacomments) 591 | objects.append(vobj) 592 | last_items = [] 593 | metacomments = [] 594 | 595 | elif action == 'package': 596 | objects.append(VhdlPackage(groups[0])) 597 | cur_package = groups[0] 598 | kind = None 599 | name = None 600 | 601 | elif action == 'type': 602 | saved_type = groups[0] 603 | 604 | elif action in ( 605 | 'array_type', 'file_type', 'access_type', 'record_type', 'range_type', 'enum_type', 'incomplete_type'): 606 | vobj = VhdlType(saved_type, cur_package, action, metacomments) 607 | objects.append(vobj) 608 | kind = None 609 | name = None 610 | metacomments = [] 611 | 612 | elif action == 'subtype': 613 | vobj = VhdlSubtype(groups[0], cur_package, groups[1], metacomments) 614 | objects.append(vobj) 615 | kind = None 616 | name = None 617 | metacomments = [] 618 | 619 | elif action == 'constant': 620 | vobj = VhdlConstant(groups[0], cur_package, groups[1], metacomments) 621 | objects.append(vobj) 622 | kind = None 623 | name = None 624 | metacomments = [] 625 | 626 | elif action == 'line_comment': 627 | for i in last_items: 628 | if not i.param_desc: 629 | i.param_desc = groups[0] 630 | 631 | return objects 632 | 633 | 634 | def subprogram_prototype(vo): 635 | """Generate a canonical prototype string 636 | 637 | Args: 638 | vo (VhdlFunction, VhdlProcedure): Subprogram object 639 | Returns: 640 | Prototype string. 641 | """ 642 | 643 | plist = '; '.join(str(p) for p in vo.parameters) 644 | 645 | if isinstance(vo, VhdlFunction): 646 | if len(vo.parameters) > 0: 647 | proto = f"function {vo.name}({plist}) return {vo.return_type};" 648 | else: 649 | proto = f"function {vo.name} return {vo.return_type};" 650 | 651 | else: # procedure 652 | proto = f"procedure {vo.name}({plist});" 653 | 654 | return proto 655 | 656 | 657 | def subprogram_signature(vo, fullname=None): 658 | """Generate a signature string 659 | 660 | Args: 661 | vo (VhdlFunction, VhdlProcedure): Subprogram object 662 | Returns: 663 | Signature string. 664 | """ 665 | 666 | if fullname is None: 667 | fullname = vo.name 668 | 669 | if isinstance(vo, VhdlFunction): 670 | plist = ','.join(p.data_type for p in vo.parameters) 671 | sig = f"{fullname}[{plist} return {vo.return_type}]" 672 | else: # procedure 673 | plist = ','.join(p.data_type for p in vo.parameters) 674 | sig = f"{fullname}[{plist}]" 675 | 676 | return sig 677 | 678 | 679 | def is_vhdl(fname): 680 | """Identify file as VHDL by its extension 681 | 682 | Args: 683 | fname (str): File name to check 684 | Returns: 685 | True when file has a VHDL extension. 686 | """ 687 | return os.path.splitext(fname)[1].lower() in ('.vhdl', '.vhd') 688 | 689 | 690 | class VhdlExtractor: 691 | """Utility class that caches parsed objects and tracks array type definitions 692 | 693 | Args: 694 | array_types(set): Initial array types 695 | """ 696 | 697 | def __init__(self, array_types=set()): 698 | self.array_types = set(('std_ulogic_vector', 'std_logic_vector', 699 | 'signed', 'unsigned', 'bit_vector')) 700 | 701 | self.array_types |= array_types 702 | self.object_cache = {} 703 | 704 | def extract_objects(self, fname, type_filter=None): 705 | """Extract objects from a source file 706 | 707 | Args: 708 | fname (str): File to parse 709 | type_filter (class, optional): Object class to filter results 710 | Returns: 711 | List of parsed objects. 712 | """ 713 | objects = [] 714 | if fname in self.object_cache: 715 | objects = self.object_cache[fname] 716 | else: 717 | with io.open(fname, 'rt', encoding='latin-1') as fh: 718 | text = fh.read() 719 | objects = parse_vhdl(text) 720 | self.object_cache[fname] = objects 721 | self._register_array_types(objects) 722 | 723 | if type_filter: 724 | objects = [o for o in objects if isinstance(o, type_filter)] 725 | 726 | return objects 727 | 728 | def extract_objects_from_source(self, text, type_filter=None): 729 | """Extract object declarations from a text buffer 730 | 731 | Args: 732 | text (str): Source code to parse 733 | type_filter (class, optional): Object class to filter results 734 | Returns: 735 | List of parsed objects. 736 | """ 737 | objects = parse_vhdl(text) 738 | self._register_array_types(objects) 739 | 740 | if type_filter: 741 | objects = [o for o in objects if isinstance(o, type_filter)] 742 | 743 | return objects 744 | 745 | def is_array(self, data_type): 746 | """Check if a type is a known array type 747 | 748 | Args: 749 | data_type (str): Name of type to check 750 | Returns: 751 | True if ``data_type`` is a known array type. 752 | """ 753 | 754 | # Split off any brackets 755 | data_type = data_type.split('[')[0].strip() 756 | 757 | return data_type.lower() in self.array_types 758 | 759 | def _add_array_types(self, type_defs): 760 | """Add array data types to internal registry 761 | 762 | Args: 763 | type_defs (dict): Dictionary of type definitions 764 | """ 765 | if 'arrays' in type_defs: 766 | self.array_types |= set(type_defs['arrays']) 767 | 768 | def load_array_types(self, fname): 769 | """Load file of previously extracted data types 770 | 771 | Args: 772 | fname (str): Name of file to load array database from 773 | """ 774 | type_defs = '' 775 | with open(fname, 'rt') as fh: 776 | type_defs = fh.read() 777 | 778 | try: 779 | type_defs = ast.literal_eval(type_defs) 780 | except SyntaxError: 781 | type_defs = {} 782 | 783 | self._add_array_types(type_defs) 784 | 785 | def save_array_types(self, fname): 786 | """Save array type registry to a file 787 | 788 | Args: 789 | fname (str): Name of file to save array database to 790 | """ 791 | type_defs = {'arrays': sorted(list(self.array_types))} 792 | with open(fname, 'wt') as fh: 793 | pprint(type_defs, stream=fh) 794 | 795 | def _register_array_types(self, objects): 796 | """Add array type definitions to internal registry 797 | 798 | Args: 799 | objects (list of VhdlType or VhdlSubtype): Array types to track 800 | """ 801 | # Add all array types directly 802 | types = [o for o in objects if isinstance(o, VhdlType) and o.type_of == 'array_type'] 803 | for t in types: 804 | self.array_types.add(t.name) 805 | 806 | subtypes = {o.name: o.base_type for o in objects if isinstance(o, VhdlSubtype)} 807 | 808 | # Find all subtypes of an array type 809 | for k, v in subtypes.items(): 810 | while v in subtypes: # Follow subtypes of subtypes 811 | v = subtypes[v] 812 | if v in self.array_types: 813 | self.array_types.add(k) 814 | 815 | def register_array_types_from_sources(self, source_files): 816 | """Add array type definitions from a file list to internal registry 817 | 818 | Args: 819 | source_files (list of str): Files to parse for array definitions 820 | """ 821 | for fname in source_files: 822 | if is_vhdl(fname): 823 | self._register_array_types(self.extract_objects(fname)) 824 | 825 | 826 | if __name__ == '__main__': 827 | ve = VhdlExtractor() 828 | code = ''' 829 | package foo is 830 | function afunc(q,w,e : std_ulogic; h,j,k : unsigned) return std_ulogic; 831 | 832 | procedure aproc( r,t,y : in std_ulogic; u,i,o : out signed); 833 | 834 | component acomp is 835 | port ( 836 | a,b,c : in std_ulogic; -- no default value 837 | f,g,h : inout bit := '1'; -- bit ports 838 | v : in std_logic_vector(lBound -1 downto 0) -- array range 839 | ); -- port list comment 840 | 841 | end component; 842 | 843 | end package; 844 | ''' 845 | 846 | objs = ve.extract_objects_from_source(code) 847 | 848 | for o in objs: 849 | print(o.name) 850 | try: 851 | for p in o.parameters: 852 | print(p) 853 | except: 854 | pass 855 | 856 | try: 857 | for p in o.ports: 858 | print(p) 859 | except: 860 | pass 861 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | # Use README.rst for the long description 4 | with open('README.rst') as fh: 5 | long_description = fh.read() 6 | 7 | # Scan the script for the version string 8 | version_file = 'hdlparse/minilexer.py' 9 | version = None 10 | with open(version_file) as fh: 11 | try: 12 | version = [line.split('=')[1].strip().strip("'") for line in fh if 13 | line.startswith('__version__')][0] 14 | except IndexError: 15 | pass 16 | 17 | if version is None: 18 | raise RuntimeError('Unable to find version string in file: {0}'.format(version_file)) 19 | 20 | setup(name='hdlparse', 21 | version=version, 22 | author='Kevin Thibedeau', 23 | author_email='kevin.thibedeau@gmail.com', 24 | url='http://kevinpt.github.io/hdlparse', 25 | download_url='http://kevinpt.github.io/hdlparse', 26 | description='HDL parser', 27 | long_description=long_description, 28 | platforms=['Any'], 29 | install_requires=[], 30 | packages=['hdlparse'], 31 | py_modules=[], 32 | include_package_data=True, 33 | 34 | keywords='HDL parser', 35 | license='MIT', 36 | classifiers=['Development Status :: 5 - Production/Stable', 37 | 'Operating System :: OS Independent', 38 | 'Intended Audience :: Developers', 39 | 'Topic :: Text Processing :: General', 40 | 'Natural Language :: English', 41 | 'Programming Language :: Python :: 3', 42 | 'License :: OSI Approved :: MIT License' 43 | ] 44 | ) 45 | --------------------------------------------------------------------------------