├── .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 | -------------------------------------------------------------------------------- /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 used by the `Symbolator `_ diagram generator. 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 either Python 2.7 or Python 3.x and no additional libraries. 17 | 18 | The installation script depends on setuptools. The source is written in 19 | Python 2.7 syntax but will convert cleanly to Python 3 when the installer 20 | passes it through 2to3. 21 | 22 | 23 | Download 24 | -------- 25 | 26 | You can access the Hdlparse Git repository from `Github 27 | `_. You can install direct from PyPI with the "pip" 28 | command if you have it available. 29 | 30 | Installation 31 | ------------ 32 | 33 | 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). 34 | 35 | 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: 36 | 37 | .. code-block:: sh 38 | 39 | > pip install setuptools 40 | 41 | The easiest way to install Hdlparse is from `PyPI `_. 42 | 43 | .. code-block:: sh 44 | 45 | > pip install --upgrade hdlparse 46 | 47 | 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: 48 | 49 | .. code-block:: sh 50 | 51 | > easy_install pip 52 | 53 | 54 | You can also use ``pip`` to get the latest development code from Github: 55 | 56 | .. code-block:: sh 57 | 58 | > pip install --upgrade https://github.com/kevinpt/hdlparse/tarball/master 59 | 60 | 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: 61 | 62 | .. code-block:: sh 63 | 64 | > python setup.py install 65 | 66 | On Linux systems you may need to install with root privileges using the *sudo* command. 67 | 68 | After a successful install the Hdlparse library will be available. 69 | 70 | 71 | Documentation 72 | ------------- 73 | 74 | The full documentation is available online at the `main Hdlparse site 75 | `_. 76 | 77 | -------------------------------------------------------------------------------- /doc/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinpt/hdlparse/be7cdab08a8c18815cc4504003ce9ca7fff41022/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/kevinpt/hdlparse/be7cdab08a8c18815cc4504003ce9ca7fff41022/doc/_static/Kreon-Bold.woff2 -------------------------------------------------------------------------------- /doc/_static/Kreon-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinpt/hdlparse/be7cdab08a8c18815cc4504003ce9ca7fff41022/doc/_static/Kreon-Light.woff2 -------------------------------------------------------------------------------- /doc/_static/Kreon-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinpt/hdlparse/be7cdab08a8c18815cc4504003ce9ca7fff41022/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/kevinpt/hdlparse/be7cdab08a8c18815cc4504003ce9ca7fff41022/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 | from __future__ import print_function 5 | 6 | import re 7 | 8 | '''Minimalistic lexer engine inspired by the PyPigments RegexLexer''' 9 | 10 | __version__ = '1.0.5' 11 | 12 | class MiniLexer(object): 13 | '''Simple lexer state machine with regex matching rules''' 14 | 15 | def __init__(self, tokens, flags=re.MULTILINE): 16 | '''Create a new lexer 17 | 18 | Args: 19 | tokens (dict(match rules)): Hierarchical dict of states with a list of regex patterns and transitions 20 | flags (int): Optional regex flags 21 | ''' 22 | self.tokens = {} 23 | 24 | # Pre-process the state definitions 25 | for state, patterns in tokens.iteritems(): 26 | full_patterns = [] 27 | for p in patterns: 28 | pat = re.compile(p[0], flags) 29 | action = p[1] 30 | new_state = p[2] if len(p) >= 3 else None 31 | 32 | # Convert pops into an integer 33 | if new_state and new_state.startswith('#pop'): 34 | try: 35 | new_state = -int(new_state.split(':')[1]) 36 | except IndexError, ValueError: 37 | new_state = -1 38 | 39 | full_patterns.append((pat, action, new_state)) 40 | self.tokens[state] = full_patterns 41 | 42 | 43 | def run(self, text): 44 | '''Run lexer rules against a source text 45 | 46 | Args: 47 | text (str): Text to apply lexer to 48 | 49 | Yields: 50 | A sequence of lexer matches. 51 | ''' 52 | 53 | stack = ['root'] 54 | pos = 0 55 | 56 | patterns = self.tokens[stack[-1]] 57 | 58 | while True: 59 | for pat, action, new_state in patterns: 60 | m = pat.match(text, pos) 61 | if m: 62 | if action: 63 | #print('## MATCH: {} -> {}'.format(m.group(), action)) 64 | yield (pos, m.end()-1), action, m.groups() 65 | 66 | pos = m.end() 67 | 68 | if new_state: 69 | if isinstance(new_state, int): # Pop states 70 | del stack[new_state:] 71 | else: 72 | stack.append(new_state) 73 | 74 | #print('## CHANGE STATE:', pos, new_state, stack) 75 | patterns = self.tokens[stack[-1]] 76 | 77 | break 78 | 79 | else: 80 | try: 81 | if text[pos] == '\n': 82 | pos += 1 83 | continue 84 | pos += 1 85 | except IndexError: 86 | break 87 | 88 | -------------------------------------------------------------------------------- /hdlparse/verilog_parser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright © 2017 Kevin Thibedeau 3 | # Distributed under the terms of the MIT license 4 | from __future__ import print_function 5 | 6 | import re, os, io, ast, pprint, collections 7 | from minilexer import MiniLexer 8 | 9 | '''Verilog documentation parser''' 10 | 11 | verilog_tokens = { 12 | 'root': [ 13 | (r'\bmodule\s+(\w+)\s*', 'module', 'module'), 14 | (r'/\*', 'block_comment', 'block_comment'), 15 | (r'//#+(.*)\n', 'metacomment'), 16 | (r'//.*\n', None), 17 | ], 18 | 'module': [ 19 | (r'parameter\s*(signed|integer|realtime|real|time)?\s*(\[[^]]+\])?', 'parameter_start', 'parameters'), 20 | (r'(input|inout|output)\s*(reg|supply0|supply1|tri|triand|trior|tri0|tri1|wire|wand|wor)?\s*(signed)?\s*(\[[^]]+\])?', 'module_port_start', 'module_port'), 21 | (r'endmodule', 'end_module', '#pop'), 22 | (r'/\*', 'block_comment', 'block_comment'), 23 | (r'//#\s*{{(.*)}}\n', 'section_meta'), 24 | (r'//.*\n', None), 25 | ], 26 | 'parameters': [ 27 | (r'\s*parameter\s*(signed|integer|realtime|real|time)?\s*(\[[^]]+\])?', 'parameter_start'), 28 | (r'\s*(\w+)[^),;]*', 'param_item'), 29 | (r',', None), 30 | (r'[);]', None, '#pop'), 31 | ], 32 | 'module_port': [ 33 | (r'\s*(input|inout|output)\s*(reg|supply0|supply1|tri|triand|trior|tri0|tri1|wire|wand|wor)?\s*(signed)?\s*(\[[^]]+\])?', 'module_port_start'), 34 | (r'\s*(\w+)\s*,?', 'port_param'), 35 | (r'[);]', None, '#pop'), 36 | (r'//#\s*{{(.*)}}\n', 'section_meta'), 37 | (r'//.*\n', None), 38 | ], 39 | 40 | 'block_comment': [ 41 | (r'\*/', 'end_comment', '#pop'), 42 | ], 43 | } 44 | 45 | 46 | VerilogLexer = MiniLexer(verilog_tokens) 47 | 48 | class VerilogObject(object): 49 | '''Base class for parsed Verilog objects''' 50 | def __init__(self, name, desc=None): 51 | self.name = name 52 | self.kind = 'unknown' 53 | self.desc = desc 54 | 55 | class VerilogParameter(object): 56 | '''Parameter and port to a module''' 57 | def __init__(self, name, mode=None, data_type=None, default_value=None, desc=None): 58 | self.name = name 59 | self.mode = mode 60 | self.data_type = data_type 61 | self.default_value = default_value 62 | self.desc = desc 63 | 64 | def __str__(self): 65 | if self.mode is not None: 66 | param = '{} : {} {}'.format(self.name, self.mode, self.data_type) 67 | else: 68 | param = '{} : {}'.format(self.name, self.data_type) 69 | if self.default_value is not None: 70 | param = '{} := {}'.format(param, self.default_value) 71 | return param 72 | 73 | def __repr__(self): 74 | return "VerilogParameter('{}')".format(self.name) 75 | 76 | class VerilogModule(VerilogObject): 77 | '''Module definition''' 78 | def __init__(self, name, ports, generics=None, sections=None, desc=None): 79 | VerilogObject.__init__(self, name, desc) 80 | self.kind = 'module' 81 | # Verilog params 82 | self.generics = generics if generics is not None else [] 83 | self.ports = ports 84 | self.sections = sections if sections is not None else {} 85 | def __repr__(self): 86 | return "VerilogModule('{}') {}".format(self.name, self.ports) 87 | 88 | 89 | 90 | def parse_verilog_file(fname): 91 | '''Parse a named Verilog file 92 | 93 | Args: 94 | fname (str): File to parse. 95 | Returns: 96 | List of parsed objects. 97 | ''' 98 | with open(fname, 'rt') as fh: 99 | text = fh.read() 100 | return parse_verilog(text) 101 | 102 | def parse_verilog(text): 103 | '''Parse a text buffer of Verilog code 104 | 105 | Args: 106 | text (str): Source code to parse 107 | Returns: 108 | List of parsed objects. 109 | ''' 110 | lex = VerilogLexer 111 | 112 | name = None 113 | kind = None 114 | saved_type = None 115 | mode = 'input' 116 | ptype = 'wire' 117 | 118 | metacomments = [] 119 | parameters = [] 120 | param_items = [] 121 | 122 | generics = [] 123 | ports = collections.OrderedDict() 124 | sections = [] 125 | port_param_index = 0 126 | last_item = None 127 | array_range_start_pos = 0 128 | 129 | objects = [] 130 | 131 | for pos, action, groups in lex.run(text): 132 | if action == 'metacomment': 133 | if last_item is None: 134 | metacomments.append(groups[0]) 135 | else: 136 | last_item.desc = groups[0] 137 | 138 | if action == 'section_meta': 139 | sections.append((port_param_index, groups[0])) 140 | 141 | elif action == 'module': 142 | kind = 'module' 143 | name = groups[0] 144 | generics = [] 145 | ports = collections.OrderedDict() 146 | param_items = [] 147 | sections = [] 148 | port_param_index = 0 149 | 150 | elif action == 'parameter_start': 151 | net_type, vec_range = groups 152 | 153 | new_ptype = '' 154 | if net_type is not None: 155 | new_ptype += net_type 156 | 157 | if vec_range is not None: 158 | new_ptype += ' ' + vec_range 159 | 160 | ptype = new_ptype 161 | 162 | elif action == 'param_item': 163 | generics.append(VerilogParameter(groups[0], 'in', ptype)) 164 | 165 | elif action == 'module_port_start': 166 | new_mode, net_type, signed, vec_range = groups 167 | 168 | new_ptype = '' 169 | if net_type is not None: 170 | new_ptype += net_type 171 | 172 | if signed is not None: 173 | new_ptype += ' ' + signed 174 | 175 | if vec_range is not None: 176 | new_ptype += ' ' + vec_range 177 | 178 | # Complete pending items 179 | for i in param_items: 180 | ports[i] = VerilogParameter(i, mode, ptype) 181 | 182 | param_items = [] 183 | if len(ports) > 0: 184 | last_item = next(reversed(ports)) 185 | 186 | # Start with new mode 187 | mode = new_mode 188 | ptype = new_ptype 189 | 190 | elif action == 'port_param': 191 | ident = groups[0] 192 | 193 | param_items.append(ident) 194 | port_param_index += 1 195 | 196 | elif action == 'end_module': 197 | # Finish any pending ports 198 | for i in param_items: 199 | ports[i] = VerilogParameter(i, mode, ptype) 200 | 201 | vobj = VerilogModule(name, ports.values(), generics, dict(sections), metacomments) 202 | objects.append(vobj) 203 | last_item = None 204 | metacomments = [] 205 | 206 | return objects 207 | 208 | 209 | def is_verilog(fname): 210 | '''Identify file as Verilog by its extension 211 | 212 | Args: 213 | fname (str): File name to check 214 | Returns: 215 | True when file has a Verilog extension. 216 | ''' 217 | return os.path.splitext(fname)[1].lower() in ('.vlog', '.v') 218 | 219 | 220 | class VerilogExtractor(object): 221 | '''Utility class that caches parsed objects''' 222 | def __init__(self): 223 | self.object_cache = {} 224 | 225 | def extract_objects(self, fname, type_filter=None): 226 | '''Extract objects from a source file 227 | 228 | Args: 229 | fname(str): Name of file to read from 230 | type_filter (class, optional): Object class to filter results 231 | Returns: 232 | List of objects extracted from the file. 233 | ''' 234 | objects = [] 235 | if fname in self.object_cache: 236 | objects = self.object_cache[fname] 237 | else: 238 | with io.open(fname, 'rt', encoding='utf-8') as fh: 239 | text = fh.read() 240 | objects = parse_verilog(text) 241 | self.object_cache[fname] = objects 242 | 243 | if type_filter: 244 | objects = [o for o in objects if isinstance(o, type_filter)] 245 | 246 | return objects 247 | 248 | 249 | def extract_objects_from_source(self, text, type_filter=None): 250 | '''Extract object declarations from a text buffer 251 | 252 | Args: 253 | text (str): Source code to parse 254 | type_filter (class, optional): Object class to filter results 255 | Returns: 256 | List of parsed objects. 257 | ''' 258 | objects = parse_verilog(text) 259 | 260 | if type_filter: 261 | objects = [o for o in objects if isinstance(o, type_filter)] 262 | 263 | return objects 264 | 265 | 266 | def is_array(self, data_type): 267 | '''Check if a type is an array type 268 | 269 | Args: 270 | data_type (str): Data type 271 | Returns: 272 | True when a data type is an array. 273 | ''' 274 | return '[' in data_type 275 | 276 | -------------------------------------------------------------------------------- /hdlparse/vhdl_parser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright © 2017 Kevin Thibedeau 3 | # Distributed under the terms of the MIT license 4 | from __future__ import print_function 5 | 6 | import re, os, io, ast 7 | from pprint import pprint 8 | from minilexer import MiniLexer 9 | 10 | '''VHDL documentation parser''' 11 | 12 | vhdl_tokens = { 13 | 'root': [ 14 | (r'package\s+(\w+)\s+is', 'package', 'package'), 15 | (r'package\s+body\s+(\w+)\s+is', 'package_body', 'package_body'), 16 | (r'function\s+(\w+|"[^"]+")\s*\(', 'function', 'param_list'), 17 | (r'procedure\s+(\w+)\s*\(', 'procedure', 'param_list'), 18 | (r'function\s+(\w+)', 'function', 'simple_func'), 19 | (r'component\s+(\w+)\s*is', 'component', 'component'), 20 | (r'entity\s+(\w+)\s*is', 'entity', 'entity'), 21 | (r'architecture\s+(\w+)\s*of', 'architecture', 'architecture'), 22 | (r'subtype\s+(\w+)\s+is\s+(\w+)', 'subtype'), 23 | (r'type\s+(\w+)\s*is', 'type', 'type_decl'), 24 | (r'/\*', 'block_comment', 'block_comment'), 25 | (r'--.*\n', None), 26 | ], 27 | 'package': [ 28 | (r'function\s+(\w+|"[^"]+")\s*\(', 'function', 'param_list'), 29 | (r'procedure\s+(\w+)\s*\(', 'procedure', 'param_list'), 30 | (r'function\s+(\w+)', 'function', 'simple_func'), 31 | (r'component\s+(\w+)\s*is', 'component', 'component'), 32 | (r'subtype\s+(\w+)\s+is\s+(\w+)', 'subtype'), 33 | (r'constant\s+(\w+)\s+:\s+(\w+)', 'constant'), 34 | (r'type\s+(\w+)\s*is', 'type', 'type_decl'), 35 | (r'end\s+package', None, '#pop'), 36 | (r'--#(.*)\n', 'metacomment'), 37 | (r'/\*', 'block_comment', 'block_comment'), 38 | (r'--.*\n', None), 39 | ], 40 | 'package_body': [ 41 | (r'end\s+package\s+body', None, '#pop'), 42 | (r'--#(.*)\n', 'metacomment'), 43 | (r'/\*', 'block_comment', 'block_comment'), 44 | (r'--.*\n', None), 45 | ], 46 | 'type_decl': [ 47 | (r'array', 'array_type', '#pop'), 48 | (r'file', 'file_type', '#pop'), 49 | (r'access', 'access_type', '#pop'), 50 | (r'record', 'record_type', '#pop'), 51 | (r'range', 'range_type', '#pop'), 52 | (r'\(', 'enum_type', '#pop'), 53 | (r';', 'incomplete_type', '#pop'), 54 | (r'/\*', 'block_comment', 'block_comment'), 55 | (r'--.*\n', None), 56 | ], 57 | 'param_list': [ 58 | (r'\s*((?:variable|signal|constant|file)\s+)?(\w+)\s*', 'param'), 59 | (r'\s*,\s*', None), 60 | (r'\s*:\s*', None, 'param_type'), 61 | (r'/\*', 'block_comment', 'block_comment'), 62 | (r'--.*\n', None), 63 | ], 64 | 'param_type': [ 65 | (r'\s*((?:in|out|inout|buffer)\s+)?(\w+)\s*', 'param_type'), 66 | (r'\s*;\s*', None, '#pop'), 67 | (r"\s*:=\s*('.'|[^\s;)]+)", 'param_default'), 68 | (r'\)\s*(?:return\s+(\w+)\s*)?;', 'end_subprogram', '#pop:2'), 69 | (r'\)\s*(?:return\s+(\w+)\s*)?is', None, '#pop:2'), 70 | (r'/\*', 'block_comment', 'block_comment'), 71 | (r'--.*\n', None), 72 | ], 73 | 'simple_func': [ 74 | (r'\s+return\s+(\w+)\s*;', 'end_subprogram', '#pop'), 75 | (r'\s+return\s+(\w+)\s+is', None, '#pop'), 76 | (r'/\*', 'block_comment', 'block_comment'), 77 | (r'--.*\n', None), 78 | ], 79 | 'component': [ 80 | (r'generic\s*\(', None, 'generic_list'), 81 | (r'port\s*\(', None, 'port_list'), 82 | (r'end\s+component\s*\w*;', 'end_component', '#pop'), 83 | (r'/\*', 'block_comment', 'block_comment'), 84 | (r'--.*\n', None), 85 | ], 86 | 'entity': [ 87 | (r'end\s+entity\s*;', 'end_entity', '#pop'), 88 | (r'/\*', 'block_comment', 'block_comment'), 89 | (r'--.*\n', None), 90 | ], 91 | 'architecture': [ 92 | (r'end\s+architecture\s*;', 'end_arch', '#pop'), 93 | (r'/\*', 'block_comment', 'block_comment'), 94 | (r'--.*\n', None), 95 | ], 96 | 'generic_list': [ 97 | (r'\s*(\w+)\s*', 'generic_param'), 98 | (r'\s*,\s*', None), 99 | (r'\s*:\s*', None, 'generic_param_type'), 100 | (r'--#(.*)\n', 'metacomment'), 101 | (r'/\*', 'block_comment', 'block_comment'), 102 | (r'--.*\n', None), 103 | ], 104 | 'generic_param_type': [ 105 | (r'\s*(\w+)\s*', 'generic_param_type'), 106 | (r'\s*;\s*', None, '#pop'), 107 | (r"\s*:=\s*([\w']+)", 'generic_param_default'), 108 | (r'\)\s*;', 'end_generic', '#pop:2'), 109 | (r'--#(.*)\n', 'metacomment'), 110 | (r'/\*', 'block_comment', 'block_comment'), 111 | (r'--.*\n', None), 112 | ], 113 | 'port_list': [ 114 | (r'\s*(\w+)\s*', 'port_param'), 115 | (r'\s*,\s*', None), 116 | (r'\s*:\s*', None, 'port_param_type'), 117 | (r'--#\s*{{(.*)}}\n', 'section_meta'), 118 | (r'--#(.*)\n', 'metacomment'), 119 | (r'/\*', 'block_comment', 'block_comment'), 120 | (r'--.*\n', None), 121 | ], 122 | 'port_param_type': [ 123 | (r'\s*(in|out|inout|buffer)\s+(\w+)\s*\(', 'port_array_param_type', 'array_range'), 124 | (r'\s*(in|out|inout|buffer)\s+(\w+)\s*', 'port_param_type'), 125 | (r'\s*;\s*', None, '#pop'), 126 | (r"\s*:=\s*([\w']+)", 'port_param_default'), 127 | (r'\)\s*;', 'end_port', '#pop:2'), 128 | (r'--#(.*)\n', 'metacomment'), 129 | (r'/\*', 'block_comment', 'block_comment'), 130 | (r'--.*\n', None), 131 | ], 132 | 'array_range': [ 133 | (r'\(', 'open_paren', 'nested_parens'), 134 | (r'\)', 'array_range_end', '#pop'), 135 | ], 136 | 'nested_parens': [ 137 | (r'\(', 'open_paren', 'nested_parens'), 138 | (r'\)', 'close_paren', '#pop'), 139 | ], 140 | 'block_comment': [ 141 | (r'\*/', 'end_comment', '#pop'), 142 | ], 143 | } 144 | 145 | VhdlLexer = MiniLexer(vhdl_tokens, flags=re.MULTILINE | re.IGNORECASE) 146 | 147 | 148 | class VhdlObject(object): 149 | '''Base class for parsed VHDL objects 150 | 151 | Args: 152 | name (str): Name of the object 153 | desc (str): Description from object metacomments 154 | ''' 155 | def __init__(self, name, desc=None): 156 | self.name = name 157 | self.kind = 'unknown' 158 | self.desc = desc 159 | 160 | class VhdlParameter(object): 161 | '''Parameter to subprograms, ports, and generics 162 | 163 | Args: 164 | name (str): Name of the object 165 | mode (str): Direction mode for the parameter 166 | data_type (str): Type name for the parameter 167 | default_value (str): Default value of the parameter 168 | desc (str): Description from object metacomments 169 | ''' 170 | def __init__(self, name, mode=None, data_type=None, default_value=None, desc=None): 171 | self.name = name 172 | self.mode = mode 173 | self.data_type = data_type 174 | self.default_value = default_value 175 | self.desc = desc 176 | 177 | def __str__(self): 178 | if self.mode is not None: 179 | param = '{} : {} {}'.format(self.name, self.mode, self.data_type) 180 | else: 181 | param = '{} : {}'.format(self.name, self.data_type) 182 | if self.default_value is not None: 183 | param = '{} := {}'.format(param, self.default_value) 184 | return param 185 | 186 | def __repr__(self): 187 | return "VhdlParameter('{}', '{}', '{}')".format(self.name, self.mode, self.data_type) 188 | 189 | class VhdlPackage(VhdlObject): 190 | '''Package declaration 191 | 192 | Args: 193 | name (str): Name of the package 194 | desc (str): Description from object metacomments 195 | ''' 196 | def __init__(self, name, desc=None): 197 | VhdlObject.__init__(self, name, desc) 198 | self.kind = 'package' 199 | 200 | class VhdlType(VhdlObject): 201 | '''Type definition 202 | 203 | Args: 204 | name (str): Name of the type 205 | package (str): Package containing the type 206 | type_of (str): Object type of this type definition 207 | desc (str, optional): Description from object metacomments 208 | ''' 209 | def __init__(self, name, package, type_of, desc=None): 210 | VhdlObject.__init__(self, name, desc) 211 | self.kind = 'type' 212 | self.package = package 213 | self.type_of = type_of 214 | def __repr__(self): 215 | return "VhdlType('{}', '{}')".format(self.name, self.type_of) 216 | 217 | 218 | class VhdlSubtype(VhdlObject): 219 | '''Subtype definition 220 | 221 | Args: 222 | name (str): Name of the subtype 223 | package (str): Package containing the subtype 224 | base_type (str): Base type name derived from 225 | desc (str, optional): Description from object metacomments 226 | ''' 227 | def __init__(self, name, package, base_type, desc=None): 228 | VhdlObject.__init__(self, name, desc) 229 | self.kind = 'subtype' 230 | self.package = package 231 | self.base_type = base_type 232 | def __repr__(self): 233 | return "VhdlSubtype('{}', '{}')".format(self.name, self.base_type) 234 | 235 | 236 | class VhdlConstant(VhdlObject): 237 | '''Constant definition 238 | 239 | Args: 240 | name (str): Name of the constant 241 | package (str): Package containing the constant 242 | base_type (str): Type fo the constant 243 | desc (str, optional): Description from object metacomments 244 | ''' 245 | def __init__(self, name, package, base_type, desc=None): 246 | VhdlObject.__init__(self, name, desc) 247 | self.kind = 'constant' 248 | self.package = package 249 | self.base_type = base_type 250 | def __repr__(self): 251 | return "VhdlConstant('{}', '{}')".format(self.name, self.base_type) 252 | 253 | 254 | class VhdlFunction(VhdlObject): 255 | '''Function declaration 256 | 257 | Args: 258 | name (str): Name of the function 259 | package (str): Package containing the function 260 | parameters (list of VhdlParameter): Parameters to the function 261 | return_type (str, optional): Type of the return value 262 | desc (str, optional): Description from object metacomments 263 | ''' 264 | def __init__(self, name, package, parameters, return_type=None, desc=None): 265 | VhdlObject.__init__(self, name, desc) 266 | self.kind = 'function' 267 | self.package = package 268 | self.parameters = parameters 269 | self.return_type = return_type 270 | 271 | def __repr__(self): 272 | return "VhdlFunction('{}')".format(self.name) 273 | 274 | 275 | class VhdlProcedure(VhdlObject): 276 | '''Procedure declaration 277 | 278 | Args: 279 | name (str): Name of the procedure 280 | package (str): Package containing the procedure 281 | parameters (list of VhdlParameter): Parameters to the procedure 282 | desc (str, optional): Description from object metacomments 283 | ''' 284 | def __init__(self, name, package, parameters, desc=None): 285 | VhdlObject.__init__(self, name, desc) 286 | self.kind = 'procedure' 287 | self.package = package 288 | self.parameters = parameters 289 | 290 | def __repr__(self): 291 | return "VhdlProcedure('{}')".format(self.name) 292 | 293 | 294 | class VhdlComponent(VhdlObject): 295 | '''Component declaration 296 | 297 | Args: 298 | name (str): Name of the component 299 | package (str): Package containing the component 300 | ports (list of VhdlParameter): Port parameters to the component 301 | generics (list of VhdlParameter): Generic parameters to the component 302 | sections (list of str): Metacomment sections 303 | desc (str, optional): Description from object metacomments 304 | ''' 305 | def __init__(self, name, package, ports, generics=None, sections=None, desc=None): 306 | VhdlObject.__init__(self, name, desc) 307 | self.kind = 'component' 308 | self.package = package 309 | self.generics = generics if generics is not None else [] 310 | self.ports = ports 311 | self.sections = sections if sections is not None else {} 312 | 313 | def __repr__(self): 314 | return "VhdlComponent('{}')".format(self.name) 315 | 316 | def dump(self): 317 | print('VHDL component: {}'.format(self.name)) 318 | for p in self.ports: 319 | print('\t{} ({}), {} ({})'.format(p.name, type(p.name), p.data_type, type(p.data_type))) 320 | 321 | 322 | def parse_vhdl_file(fname): 323 | '''Parse a named VHDL file 324 | 325 | Args: 326 | fname(str): Name of file to parse 327 | Returns: 328 | Parsed objects. 329 | ''' 330 | with open(fname, 'rt') as fh: 331 | text = fh.read() 332 | return parse_vhdl(text) 333 | 334 | def parse_vhdl(text): 335 | '''Parse a text buffer of VHDL code 336 | 337 | Args: 338 | text(str): Source code to parse 339 | Returns: 340 | Parsed objects. 341 | ''' 342 | lex = VhdlLexer 343 | 344 | name = None 345 | kind = None 346 | saved_type = None 347 | end_param_group = False 348 | cur_package = None 349 | 350 | metacomments = [] 351 | parameters = [] 352 | param_items = [] 353 | 354 | generics = [] 355 | ports = [] 356 | sections = [] 357 | port_param_index = 0 358 | last_item = None 359 | array_range_start_pos = 0 360 | 361 | objects = [] 362 | 363 | for pos, action, groups in lex.run(text): 364 | if action == 'metacomment': 365 | realigned = re.sub(r'^#+', lambda m: ' ' * len(m.group(0)), groups[0]) 366 | if last_item is None: 367 | metacomments.append(realigned) 368 | else: 369 | last_item.desc = realigned 370 | if action == 'section_meta': 371 | sections.append((port_param_index, groups[0])) 372 | 373 | elif action == 'function': 374 | kind = 'function' 375 | name = groups[0] 376 | param_items = [] 377 | parameters = [] 378 | elif action == 'procedure': 379 | kind = 'procedure' 380 | name = groups[0] 381 | param_items = [] 382 | parameters = [] 383 | elif action == 'param': 384 | if end_param_group: 385 | # Complete previous parameters 386 | for i in param_items: 387 | parameters.append(i) 388 | param_items = [] 389 | end_param_group = False 390 | 391 | param_items.append(VhdlParameter(groups[1])) 392 | elif action == 'param_type': 393 | mode, ptype = groups 394 | 395 | if mode is not None: 396 | mode = mode.strip() 397 | 398 | for i in param_items: # Set mode and type for all pending parameters 399 | i.mode = mode 400 | i.data_type = ptype 401 | 402 | end_param_group = True 403 | 404 | elif action == 'param_default': 405 | for i in param_items: 406 | i.default_value = groups[0] 407 | 408 | elif action == 'end_subprogram': 409 | # Complete last parameters 410 | for i in param_items: 411 | parameters.append(i) 412 | 413 | if kind == 'function': 414 | vobj = VhdlFunction(name, cur_package, parameters, groups[0], metacomments) 415 | else: 416 | vobj = VhdlProcedure(name, cur_package, parameters, metacomments) 417 | 418 | objects.append(vobj) 419 | 420 | metacomments = [] 421 | parameters = [] 422 | param_items = [] 423 | kind = None 424 | name = None 425 | 426 | elif action == 'component': 427 | kind = 'component' 428 | name = groups[0] 429 | generics = [] 430 | ports = [] 431 | param_items = [] 432 | sections = [] 433 | port_param_index = 0 434 | 435 | elif action == 'generic_param': 436 | param_items.append(groups[0]) 437 | 438 | elif action == 'generic_param_type': 439 | ptype = groups[0] 440 | 441 | for i in param_items: 442 | generics.append(VhdlParameter(i, 'in', ptype)) 443 | param_items = [] 444 | last_item = generics[-1] 445 | 446 | elif action == 'port_param': 447 | param_items.append(groups[0]) 448 | port_param_index += 1 449 | 450 | elif action == 'port_param_type': 451 | mode, ptype = groups 452 | 453 | for i in param_items: 454 | ports.append(VhdlParameter(i, mode, ptype)) 455 | 456 | param_items = [] 457 | last_item = ports[-1] 458 | 459 | elif action == 'port_array_param_type': 460 | mode, ptype = groups 461 | array_range_start_pos = pos[1] 462 | 463 | elif action == 'array_range_end': 464 | arange = text[array_range_start_pos:pos[0]+1] 465 | 466 | for i in param_items: 467 | ports.append(VhdlParameter(i, mode, ptype + arange)) 468 | 469 | param_items = [] 470 | last_item = ports[-1] 471 | 472 | elif action == 'end_component': 473 | vobj = VhdlComponent(name, cur_package, ports, generics, dict(sections), metacomments) 474 | objects.append(vobj) 475 | last_item = None 476 | metacomments = [] 477 | 478 | elif action == 'package': 479 | objects.append(VhdlPackage(groups[0])) 480 | cur_package = groups[0] 481 | kind = None 482 | name = None 483 | 484 | elif action == 'type': 485 | saved_type = groups[0] 486 | 487 | elif action in ('array_type', 'file_type', 'access_type', 'record_type', 'range_type', 'enum_type', 'incomplete_type'): 488 | vobj = VhdlType(saved_type, cur_package, action, metacomments) 489 | objects.append(vobj) 490 | kind = None 491 | name = None 492 | metacomments = [] 493 | 494 | elif action == 'subtype': 495 | vobj = VhdlSubtype(groups[0], cur_package, groups[1], metacomments) 496 | objects.append(vobj) 497 | kind = None 498 | name = None 499 | metacomments = [] 500 | 501 | elif action == 'constant': 502 | vobj = VhdlConstant(groups[0], cur_package, groups[1], metacomments) 503 | objects.append(vobj) 504 | kind = None 505 | name = None 506 | metacomments = [] 507 | 508 | return objects 509 | 510 | 511 | def subprogram_prototype(vo): 512 | '''Generate a canonical prototype string 513 | 514 | Args: 515 | vo (VhdlFunction, VhdlProcedure): Subprogram object 516 | Returns: 517 | Prototype string. 518 | ''' 519 | 520 | plist = '; '.join(str(p) for p in vo.parameters) 521 | 522 | if isinstance(vo, VhdlFunction): 523 | if len(vo.parameters) > 0: 524 | proto = 'function {}({}) return {};'.format(vo.name, plist, vo.return_type) 525 | else: 526 | proto = 'function {} return {};'.format(vo.name, vo.return_type) 527 | 528 | else: # procedure 529 | proto = 'procedure {}({});'.format(vo.name, plist) 530 | 531 | return proto 532 | 533 | def subprogram_signature(vo, fullname=None): 534 | '''Generate a signature string 535 | 536 | Args: 537 | vo (VhdlFunction, VhdlProcedure): Subprogram object 538 | Returns: 539 | Signature string. 540 | ''' 541 | 542 | if fullname is None: 543 | fullname = vo.name 544 | 545 | if isinstance(vo, VhdlFunction): 546 | plist = ','.join(p.data_type for p in vo.parameters) 547 | sig = '{}[{} return {}]'.format(fullname, plist, vo.return_type) 548 | else: # procedure 549 | plist = ','.join(p.data_type for p in vo.parameters) 550 | sig = '{}[{}]'.format(fullname, plist) 551 | 552 | return sig 553 | 554 | 555 | def is_vhdl(fname): 556 | '''Identify file as VHDL by its extension 557 | 558 | Args: 559 | fname (str): File name to check 560 | Returns: 561 | True when file has a VHDL extension. 562 | ''' 563 | return os.path.splitext(fname)[1].lower() in ('.vhdl', '.vhd') 564 | 565 | 566 | class VhdlExtractor(object): 567 | '''Utility class that caches parsed objects and tracks array type definitions 568 | 569 | Args: 570 | array_types(set): Initial array types 571 | ''' 572 | def __init__(self, array_types=set()): 573 | self.array_types = set(('std_ulogic_vector', 'std_logic_vector', 574 | 'signed', 'unsigned', 'bit_vector')) 575 | 576 | self.array_types |= array_types 577 | self.object_cache = {} 578 | 579 | def extract_objects(self, fname, type_filter=None): 580 | '''Extract objects from a source file 581 | 582 | Args: 583 | fname (str): File to parse 584 | type_filter (class, optional): Object class to filter results 585 | Returns: 586 | List of parsed objects. 587 | ''' 588 | objects = [] 589 | if fname in self.object_cache: 590 | objects = self.object_cache[fname] 591 | else: 592 | with io.open(fname, 'rt', encoding='latin-1') as fh: 593 | text = fh.read() 594 | objects = parse_vhdl(text) 595 | self.object_cache[fname] = objects 596 | self._register_array_types(objects) 597 | 598 | if type_filter: 599 | objects = [o for o in objects if isinstance(o, type_filter)] 600 | 601 | return objects 602 | 603 | def extract_objects_from_source(self, text, type_filter=None): 604 | '''Extract object declarations from a text buffer 605 | 606 | Args: 607 | text (str): Source code to parse 608 | type_filter (class, optional): Object class to filter results 609 | Returns: 610 | List of parsed objects. 611 | ''' 612 | objects = parse_vhdl(text) 613 | self._register_array_types(objects) 614 | 615 | if type_filter: 616 | objects = [o for o in objects if isinstance(o, type_filter)] 617 | 618 | return objects 619 | 620 | 621 | def is_array(self, data_type): 622 | '''Check if a type is a known array type 623 | 624 | Args: 625 | data_type (str): Name of type to check 626 | Returns: 627 | True if ``data_type`` is a known array type. 628 | ''' 629 | 630 | # Split off any brackets 631 | data_type = data_type.split('[')[0].strip() 632 | 633 | return data_type.lower() in self.array_types 634 | 635 | 636 | def _add_array_types(self, type_defs): 637 | '''Add array data types to internal registry 638 | 639 | Args: 640 | type_defs (dict): Dictionary of type definitions 641 | ''' 642 | if 'arrays' in type_defs: 643 | self.array_types |= set(type_defs['arrays']) 644 | 645 | def load_array_types(self, fname): 646 | '''Load file of previously extracted data types 647 | 648 | Args: 649 | fname (str): Name of file to load array database from 650 | ''' 651 | type_defs = '' 652 | with open(fname, 'rt') as fh: 653 | type_defs = fh.read() 654 | 655 | try: 656 | type_defs = ast.literal_eval(type_defs) 657 | except SyntaxError: 658 | type_defs = {} 659 | 660 | self._add_array_types(type_defs) 661 | 662 | def save_array_types(self, fname): 663 | '''Save array type registry to a file 664 | 665 | Args: 666 | fname (str): Name of file to save array database to 667 | ''' 668 | type_defs = {'arrays': sorted(list(self.array_types))} 669 | with open(fname, 'wt') as fh: 670 | pprint(type_defs, stream=fh) 671 | 672 | def _register_array_types(self, objects): 673 | '''Add array type definitions to internal registry 674 | 675 | Args: 676 | objects (list of VhdlType or VhdlSubtype): Array types to track 677 | ''' 678 | # Add all array types directly 679 | types = [o for o in objects if isinstance(o, VhdlType) and o.type_of == 'array_type'] 680 | for t in types: 681 | self.array_types.add(t.name) 682 | 683 | subtypes = {o.name:o.base_type for o in objects if isinstance(o, VhdlSubtype)} 684 | 685 | # Find all subtypes of an array type 686 | for k,v in subtypes.iteritems(): 687 | while v in subtypes: # Follow subtypes of subtypes 688 | v = subtypes[v] 689 | if v in self.array_types: 690 | self.array_types.add(k) 691 | 692 | def register_array_types_from_sources(self, source_files): 693 | '''Add array type definitions from a file list to internal registry 694 | 695 | Args: 696 | source_files (list of str): Files to parse for array definitions 697 | ''' 698 | for fname in source_files: 699 | if is_vhdl(fname): 700 | self._register_array_types(self.extract_objects(fname)) 701 | 702 | 703 | if __name__ == '__main__': 704 | ve = VhdlExtractor() 705 | code = ''' 706 | package foo is 707 | function afunc(q,w,e : std_ulogic; h,j,k : unsigned) return std_ulogic; 708 | 709 | procedure aproc( r,t,y : in std_ulogic; u,i,o : out signed); 710 | 711 | component acomp is 712 | port ( 713 | a,b,c : in std_ulogic; 714 | f,g,h : inout bit 715 | ); 716 | end component; 717 | 718 | end package; 719 | ''' 720 | 721 | objs = ve.extract_objects_from_source(code) 722 | 723 | for o in objs: 724 | print(o.name) 725 | try: 726 | for p in o.parameters: 727 | print(p) 728 | except: 729 | pass 730 | 731 | try: 732 | for p in o.ports: 733 | print(p) 734 | except: 735 | pass 736 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | 2 | from setuptools import setup 3 | 4 | # Use README.rst for the long description 5 | with open('README.rst') as fh: 6 | long_description = fh.read() 7 | 8 | # Scan the script for the version string 9 | version_file = 'hdlparse/minilexer.py' 10 | version = None 11 | with open(version_file) as fh: 12 | try: 13 | version = [line.split('=')[1].strip().strip("'") for line in fh if \ 14 | line.startswith('__version__')][0] 15 | except IndexError: 16 | pass 17 | 18 | if version is None: 19 | raise RuntimeError('Unable to find version string in file: {0}'.format(version_file)) 20 | 21 | 22 | setup(name='hdlparse', 23 | version=version, 24 | author='Kevin Thibedeau', 25 | author_email='kevin.thibedeau@gmail.com', 26 | url='http://kevinpt.github.io/hdlparse', 27 | download_url='http://kevinpt.github.io/hdlparse', 28 | description='HDL parser', 29 | long_description=long_description, 30 | platforms = ['Any'], 31 | install_requires = [], 32 | packages = ['hdlparse'], 33 | py_modules = [], 34 | include_package_data = True, 35 | 36 | use_2to3 = True, 37 | 38 | keywords='HDL parser', 39 | license='MIT', 40 | classifiers=['Development Status :: 5 - Production/Stable', 41 | 'Operating System :: OS Independent', 42 | 'Intended Audience :: Developers', 43 | 'Topic :: Text Processing :: General', 44 | 'Natural Language :: English', 45 | 'Programming Language :: Python :: 2.7', 46 | 'Programming Language :: Python :: 3', 47 | 'License :: OSI Approved :: MIT License' 48 | ] 49 | ) 50 | 51 | --------------------------------------------------------------------------------